diff -r f228415225b7 -r 5bfbb7be5400 examples/tty/serial.c --- a/examples/tty/serial.c Thu Jan 21 11:10:22 2010 +0100 +++ b/examples/tty/serial.c Sun Jan 31 14:50:37 2010 +0100 @@ -29,6 +29,7 @@ #include #include +#include #include "../../include/ecrt.h" // EtherCAT realtime interface #include "../../include/ectty.h" // EtherCAT TTY interface @@ -38,23 +39,32 @@ // Optional features #define PFX "ec_tty_example: " +#define DEBUG 0 + /*****************************************************************************/ #define VendorIdBeckhoff 0x00000002 #define ProductCodeBeckhoffEL6002 0x17723052 -#define Beckhoff_EL6002 VendorIdBeckhoff, ProductCodeBeckhoffEL6002 + +#define VendorIdIds 0x000012ad +#define ProductCodeIdsCSI71A 0x17723052 + +/*****************************************************************************/ typedef enum { SER_REQUEST_INIT, SER_WAIT_FOR_INIT_RESPONSE, - SER_READY -} serial_state_t; + SER_READY, + SER_SET_RTSCTS, + SER_SET_BAUD_RATE, + SER_SET_DATA_FRAME, +} el60xx_port_state; + +#define EL6002_PORT_NAME_SIZE 16 typedef struct { - struct list_head list; - ec_tty_t *tty; - ec_slave_config_t *sc; + char name[EL6002_PORT_NAME_SIZE]; size_t max_tx_data_size; size_t max_rx_data_size; @@ -62,7 +72,7 @@ u8 *tx_data; u8 tx_data_size; - serial_state_t state; + el60xx_port_state state; u8 tx_request_toggle; u8 tx_accepted_toggle; @@ -76,6 +86,29 @@ u32 off_tx; u32 off_status; u32 off_rx; + + ec_sdo_request_t *rtscts_sdo; + u8 requested_rtscts; + u8 current_rtscts; + + ec_sdo_request_t *baud_sdo; + u8 requested_baud_rate; + u8 current_baud_rate; + + ec_sdo_request_t *frame_sdo; + u8 requested_data_frame; + u8 current_data_frame; + + unsigned int config_error; + +} el60xx_port_t; + +#define EL6002_PORTS 2 + +typedef struct { + struct list_head list; + ec_slave_config_t *sc; + el60xx_port_t port[EL6002_PORTS]; } el6002_t; LIST_HEAD(handlers); @@ -198,82 +231,257 @@ {0xff} }; +typedef enum { + PAR_NONE, + PAR_ODD, + PAR_EVEN +} parity_t; + +typedef struct { + u8 value; + unsigned int data_bits; + parity_t parity; + unsigned int stop_bits; +} el600x_data_frame_t; + +/** EL600x supported values for data frame SDO. + */ +el600x_data_frame_t el600x_data_frame[] = { + {0x01, 7, PAR_EVEN, 1}, + {0x09, 7, PAR_EVEN, 2}, + {0x02, 7, PAR_ODD, 1}, + {0x0a, 7, PAR_ODD, 2}, + {0x03, 8, PAR_NONE, 1}, + {0x0b, 8, PAR_NONE, 2}, + {0x04, 8, PAR_EVEN, 1}, + {0x0c, 8, PAR_EVEN, 2}, + {0x05, 8, PAR_ODD, 1}, + {0x0d, 8, PAR_ODD, 2}, +}; + +typedef struct { + u8 value; + unsigned int baud; + tcflag_t cbaud; +} el600x_baud_rate_t; + +/** EL600x supported values for baud rate SDO. + */ +el600x_baud_rate_t el600x_baud_rate[] = { + {1, 300, B300}, + {2, 600, B600}, + {3, 1200, B1200}, + {4, 2400, B2400}, + {5, 4800, B4800}, + {6, 9600, B9600}, + {7, 19200, B19200}, + {8, 38400, B38400}, + {9, 57600, B57600}, + {10, 115200, B115200} +}; + /****************************************************************************/ -int el6002_init(el6002_t *ser, ec_master_t *master, u16 position, - ec_domain_t *domain) +int el60xx_cflag_changed(void *data, tcflag_t cflag) +{ + el60xx_port_t *port = (el60xx_port_t *) data; + unsigned int data_bits, stop_bits; + tcflag_t cbaud, rtscts; + parity_t par; + unsigned int i; + el600x_baud_rate_t *b_to_use = NULL; + el600x_data_frame_t *df_to_use = NULL; + +#if DEBUG + printk(KERN_INFO PFX "%s(%s, cflag=%x).\n", __func__, port->name, cflag); +#endif + + rtscts = cflag & CRTSCTS; + printk(KERN_INFO PFX "%s: Requested RTS/CTS: %s.\n", + port->name, rtscts ? "yes" : "no"); + + cbaud = cflag & CBAUD; + + for (i = 0; i < sizeof(el600x_baud_rate) / sizeof(el600x_baud_rate_t); + i++) { + el600x_baud_rate_t *b = el600x_baud_rate + i; + if (b->cbaud == cbaud) { + b_to_use = b; + break; + } + } + + if (b_to_use) { + printk(KERN_INFO PFX "%s: Requested baud rate: %u.\n", + port->name, b_to_use->baud); + } else { + printk(KERN_ERR PFX "Error: %s does not support" + " baud rate index %x.\n", port->name, cbaud); + return -EINVAL; + } + + switch (cflag & CSIZE) { + case CS5: + data_bits = 5; + break; + case CS6: + data_bits = 6; + break; + case CS7: + data_bits = 7; + break; + case CS8: + data_bits = 8; + break; + default: /* CS5 or CS6 */ + data_bits = 0; + } + + if (cflag & PARENB) { + par = (cflag & PARODD) ? PAR_ODD : PAR_EVEN; + } else { + par = PAR_NONE; + } + + stop_bits = (cflag & CSTOPB) ? 2 : 1; + + printk(KERN_INFO PFX "%s: Requested Data frame: %u%c%u.\n", + port->name, data_bits, + (par == PAR_NONE ? 'N' : (par == PAR_ODD ? 'O' : 'E')), + stop_bits); + + for (i = 0; i < sizeof(el600x_data_frame) / sizeof(el600x_data_frame_t); + i++) { + el600x_data_frame_t *df = el600x_data_frame + i; + if (df->data_bits == data_bits && + df->parity == par && + df->stop_bits == stop_bits) { + df_to_use = df; + break; + } + } + + if (!df_to_use) { + printk(KERN_ERR PFX "Error: %s does not support data frame type.\n", + port->name); + return -EINVAL; + } + + port->requested_rtscts = rtscts != 0; + port->requested_baud_rate = b_to_use->value; + port->requested_data_frame = df_to_use->value; + port->config_error = 0; + return 0; +} + +/****************************************************************************/ + +static ec_tty_operations_t el60xx_tty_ops = { + .cflag_changed = el60xx_cflag_changed, +}; + +/****************************************************************************/ + +int el60xx_port_init(el60xx_port_t *port, ec_slave_config_t *sc, + ec_domain_t *domain, unsigned int slot_offset, const char *name) { int ret = 0; - ser->tty = ectty_create(); - if (IS_ERR(ser->tty)) { - printk(KERN_ERR PFX "Failed to create tty.\n"); - ret = PTR_ERR(ser->tty); + strncpy(port->name, name, EL6002_PORT_NAME_SIZE); + + port->tty = ectty_create(&el60xx_tty_ops, port); + if (IS_ERR(port->tty)) { + printk(KERN_ERR PFX "Failed to create tty for %s.\n", + port->name); + ret = PTR_ERR(port->tty); goto out_return; } - ser->sc = NULL; - ser->max_tx_data_size = 22; - ser->max_rx_data_size = 22; - ser->tx_data = NULL; - ser->tx_data_size = 0; - ser->state = SER_REQUEST_INIT; - ser->tx_request_toggle = 0; - ser->rx_accepted_toggle = 0; - ser->control = 0x0000; - ser->off_ctrl = 0; - ser->off_tx = 0; - ser->off_status = 0; - ser->off_rx = 0; - - if (!(ser->sc = ecrt_master_slave_config( - master, 0, position, Beckhoff_EL6002))) { - printk(KERN_ERR PFX "Failed to create slave configuration.\n"); - ret = -EBUSY; - goto out_free_tty; - } - - if (ecrt_slave_config_pdos(ser->sc, EC_END, el6002_syncs)) { - printk(KERN_ERR PFX "Failed to configure PDOs.\n"); + port->max_tx_data_size = 22; + port->max_rx_data_size = 22; + port->tx_data = NULL; + port->tx_data_size = 0; + port->state = SER_REQUEST_INIT; + port->tx_request_toggle = 0; + port->rx_accepted_toggle = 0; + port->control = 0x0000; + port->off_ctrl = 0; + port->off_tx = 0; + port->off_status = 0; + port->off_rx = 0; + port->requested_rtscts = 0x00; // no hardware handshake + port->current_rtscts = 0xff; + port->requested_baud_rate = 6; // 9600 + port->current_baud_rate = 0; + port->requested_data_frame = 0x03; // 8N1 + port->current_data_frame = 0x00; + port->config_error = 0; + + if (!(port->rtscts_sdo = ecrt_slave_config_create_sdo_request(sc, + 0x8000 + slot_offset, 0x01, 1))) { + printk(KERN_ERR PFX "Failed to create SDO request for %s.\n", + port->name); ret = -ENOMEM; goto out_free_tty; } - + + if (!(port->baud_sdo = ecrt_slave_config_create_sdo_request(sc, + 0x8000 + slot_offset, 0x11, 1))) { + printk(KERN_ERR PFX "Failed to create SDO request for %s.\n", + port->name); + ret = -ENOMEM; + goto out_free_tty; + } + + if (!(port->frame_sdo = ecrt_slave_config_create_sdo_request(sc, + 0x8000 + slot_offset, 0x15, 1))) { + printk(KERN_ERR PFX "Failed to create SDO request for %s\n", + port->name); + ret = -ENOMEM; + goto out_free_tty; + } + ret = ecrt_slave_config_reg_pdo_entry( - ser->sc, 0x7001, 0x01, domain, NULL); + sc, 0x7001 + slot_offset, 0x01, domain, NULL); if (ret < 0) { - printk(KERN_ERR PFX "Failed to register PDO entry.\n"); + printk(KERN_ERR PFX "Failed to register PDO entry of %s\n", + port->name); goto out_free_tty; } - ser->off_ctrl = ret; + port->off_ctrl = ret; ret = ecrt_slave_config_reg_pdo_entry( - ser->sc, 0x7000, 0x11, domain, NULL); + sc, 0x7000 + slot_offset, 0x11, domain, NULL); if (ret < 0) { - printk(KERN_ERR PFX "Failed to register PDO entry.\n"); + printk(KERN_ERR PFX "Failed to register PDO entry of %s.\n", + port->name); goto out_free_tty; } - ser->off_tx = ret; + port->off_tx = ret; ret = ecrt_slave_config_reg_pdo_entry( - ser->sc, 0x6001, 0x01, domain, NULL); + sc, 0x6001 + slot_offset, 0x01, domain, NULL); if (ret < 0) { - printk(KERN_ERR PFX "Failed to register PDO entry.\n"); + printk(KERN_ERR PFX "Failed to register PDO entry of %s.\n", + port->name); goto out_free_tty; } - ser->off_status = ret; + port->off_status = ret; ret = ecrt_slave_config_reg_pdo_entry( - ser->sc, 0x6000, 0x11, domain, NULL); + sc, 0x6000 + slot_offset, 0x11, domain, NULL); if (ret < 0) { - printk(KERN_ERR PFX "Failed to register PDO entry.\n"); + printk(KERN_ERR PFX "Failed to register PDO entry of %s.\n", + port->name); goto out_free_tty; } - ser->off_rx = ret; - - if (ser->max_tx_data_size > 0) { - ser->tx_data = kmalloc(ser->max_tx_data_size, GFP_KERNEL); - if (ser->tx_data == NULL) { + port->off_rx = ret; + + if (port->max_tx_data_size > 0) { + port->tx_data = kmalloc(port->max_tx_data_size, GFP_KERNEL); + if (port->tx_data == NULL) { + printk(KERN_ERR PFX "Failed to allocate %u bytes of TX" + " memory for %s.\n", port->max_tx_data_size, port->name); ret = -ENOMEM; goto out_free_tty; } @@ -282,94 +490,280 @@ return 0; out_free_tty: - ectty_free(ser->tty); + ectty_free(port->tty); out_return: return ret; } /****************************************************************************/ -void el6002_clear(el6002_t *ser) -{ - ectty_free(ser->tty); - if (ser->tx_data) { - kfree(ser->tx_data); +void el60xx_port_clear(el60xx_port_t *port) +{ + ectty_free(port->tty); + if (port->tx_data) { + kfree(port->tx_data); } } /****************************************************************************/ -void el6002_run(el6002_t *ser, u8 *pd) -{ - u16 status = EC_READ_U16(pd + ser->off_status); - u8 *rx_data = pd + ser->off_rx; +void el60xx_port_run(el60xx_port_t *port, u8 *pd) +{ + u16 status = EC_READ_U16(pd + port->off_status); + u8 *rx_data = pd + port->off_rx; uint8_t tx_accepted_toggle, rx_request_toggle; - switch (ser->state) { + switch (port->state) { case SER_READY: + /* Check, if hardware handshaking has to be configured. */ + if (!port->config_error && + port->requested_rtscts != port->current_rtscts) { + EC_WRITE_U8(ecrt_sdo_request_data(port->rtscts_sdo), + port->requested_rtscts); + ecrt_sdo_request_write(port->rtscts_sdo); + port->state = SER_SET_RTSCTS; + break; + } + + /* Check, if the baud rate has to be configured. */ + if (!port->config_error && + port->requested_baud_rate != port->current_baud_rate) { + EC_WRITE_U8(ecrt_sdo_request_data(port->baud_sdo), + port->requested_baud_rate); + ecrt_sdo_request_write(port->baud_sdo); + port->state = SER_SET_BAUD_RATE; + break; + } + + /* Check, if the data frame has to be configured. */ + if (!port->config_error && + port->requested_data_frame != port->current_data_frame) { + EC_WRITE_U8(ecrt_sdo_request_data(port->frame_sdo), + port->requested_data_frame); + ecrt_sdo_request_write(port->frame_sdo); + port->state = SER_SET_DATA_FRAME; + break; + } + /* Send data */ tx_accepted_toggle = status & 0x0001; - if (tx_accepted_toggle != ser->tx_accepted_toggle) { // ready - ser->tx_data_size = - ectty_tx_data(ser->tty, ser->tx_data, ser->max_tx_data_size); - if (ser->tx_data_size) { - printk(KERN_INFO PFX "Sending %u bytes.\n", ser->tx_data_size); - ser->tx_request_toggle = !ser->tx_request_toggle; - ser->tx_accepted_toggle = tx_accepted_toggle; + if (tx_accepted_toggle != port->tx_accepted_toggle) { // ready + port->tx_data_size = + ectty_tx_data(port->tty, port->tx_data, port->max_tx_data_size); + if (port->tx_data_size) { +#if DEBUG + printk(KERN_INFO PFX "%s: Sending %u bytes.\n", + port->name, port->tx_data_size); +#endif + port->tx_request_toggle = !port->tx_request_toggle; + port->tx_accepted_toggle = tx_accepted_toggle; } } /* Receive data */ rx_request_toggle = status & 0x0002; - if (rx_request_toggle != ser->rx_request_toggle) { + if (rx_request_toggle != port->rx_request_toggle) { uint8_t rx_data_size = status >> 8; - ser->rx_request_toggle = rx_request_toggle; - printk(KERN_INFO PFX "Received %u bytes.\n", rx_data_size); - ectty_rx_data(ser->tty, rx_data, rx_data_size); - ser->rx_accepted_toggle = !ser->rx_accepted_toggle; - } - - ser->control = - ser->tx_request_toggle | - ser->rx_accepted_toggle << 1 | - ser->tx_data_size << 8; + port->rx_request_toggle = rx_request_toggle; +#if DEBUG + printk(KERN_INFO PFX "%s: Received %u bytes.\n", + port->name, rx_data_size); +#endif + ectty_rx_data(port->tty, rx_data, rx_data_size); + port->rx_accepted_toggle = !port->rx_accepted_toggle; + } + + port->control = + port->tx_request_toggle | + port->rx_accepted_toggle << 1 | + port->tx_data_size << 8; break; case SER_REQUEST_INIT: if (status & (1 << 2)) { - ser->control = 0x0000; - ser->state = SER_WAIT_FOR_INIT_RESPONSE; + port->control = 0x0000; + port->state = SER_WAIT_FOR_INIT_RESPONSE; } else { - ser->control = 1 << 2; // CW.2, request initialization + port->control = 1 << 2; // CW.2, request initialization } break; case SER_WAIT_FOR_INIT_RESPONSE: if (!(status & (1 << 2))) { - printk(KERN_INFO PFX "Init successful.\n"); - ser->tx_accepted_toggle = 1; - ser->control = 0x0000; - ser->state = SER_READY; - } - break; - } - - EC_WRITE_U16(pd + ser->off_ctrl, ser->control); - memcpy(pd + ser->off_tx, ser->tx_data, ser->tx_data_size); + printk(KERN_INFO PFX "%s: Init successful.\n", port->name); + port->tx_accepted_toggle = 1; + port->control = 0x0000; + port->state = SER_READY; + } + break; + + case SER_SET_RTSCTS: + switch (ecrt_sdo_request_state(port->rtscts_sdo)) { + case EC_REQUEST_SUCCESS: + printk(KERN_INFO PFX "%s: Accepted RTS/CTS.\n", + port->name); + port->current_rtscts = port->requested_rtscts; + port->state = SER_REQUEST_INIT; + break; + case EC_REQUEST_ERROR: + printk(KERN_ERR PFX "Failed to set RTS/CTS on %s!\n", + port->name); + port->state = SER_REQUEST_INIT; + port->config_error = 1; + break; + default: + break; + } + break; + + case SER_SET_BAUD_RATE: + switch (ecrt_sdo_request_state(port->baud_sdo)) { + case EC_REQUEST_SUCCESS: + printk(KERN_INFO PFX "%s: Accepted baud rate.\n", + port->name); + port->current_baud_rate = port->requested_baud_rate; + port->state = SER_REQUEST_INIT; + break; + case EC_REQUEST_ERROR: + printk(KERN_ERR PFX "Failed to set baud rate on %s!\n", + port->name); + port->state = SER_REQUEST_INIT; + port->config_error = 1; + break; + default: + break; + } + break; + + case SER_SET_DATA_FRAME: + switch (ecrt_sdo_request_state(port->frame_sdo)) { + case EC_REQUEST_SUCCESS: + printk(KERN_INFO PFX "%s: Accepted data frame.\n", + port->name); + port->current_data_frame = port->requested_data_frame; + port->state = SER_REQUEST_INIT; + break; + case EC_REQUEST_ERROR: + printk(KERN_ERR PFX "Failed to set data frame on %s!\n", + port->name); + port->state = SER_REQUEST_INIT; + port->config_error = 1; + break; + default: + break; + } + break; + } + + EC_WRITE_U16(pd + port->off_ctrl, port->control); + memcpy(pd + port->off_tx, port->tx_data, port->tx_data_size); +} + +/****************************************************************************/ + +int el6002_init(el6002_t *el6002, ec_master_t *master, u16 position, + ec_domain_t *domain, u32 vendor, u32 product) +{ + int ret = 0, i; + + if (!(el6002->sc = ecrt_master_slave_config( + master, 0, position, vendor, product))) { + printk(KERN_ERR PFX "EL6002(%u): Failed to create" + " slave configuration.\n", position); + ret = -EBUSY; + goto out_return; + } + + if (ecrt_slave_config_pdos(el6002->sc, EC_END, el6002_syncs)) { + printk(KERN_ERR PFX "EL6002(%u): Failed to configure PDOs.\n", + position); + ret = -ENOMEM; + goto out_return; + } + + for (i = 0; i < EL6002_PORTS; i++) { + char name[EL6002_PORT_NAME_SIZE]; + snprintf(name, EL6002_PORT_NAME_SIZE, "EL6002(%u) X%u", + position, i + 1); + if (el60xx_port_init(el6002->port + i, el6002->sc, domain, i * 0x10, + name)) { + printk(KERN_ERR PFX "EL6002(%u): Failed to init port X%u.\n", + position, i); + goto out_ports; + } + } + + return 0; + +out_ports: + for (i--; i >= 0; i--) { + el60xx_port_clear(el6002->port + i); + } +out_return: + return ret; +} + +/****************************************************************************/ + +void el6002_clear(el6002_t *el6002) +{ + int i; + + for (i = 0; i < EL6002_PORTS; i++) { + el60xx_port_clear(el6002->port + i); + } +} + +/****************************************************************************/ + +void el6002_run(el6002_t *el6002, u8 *pd) +{ + int i; + + for (i = 0; i < EL6002_PORTS; i++) { + el60xx_port_run(el6002->port + i, pd); + } } /*****************************************************************************/ void run_serial_devices(u8 *pd) { - el6002_t *ser; - - list_for_each_entry(ser, &handlers, list) { - el6002_run(ser, pd); - } + el6002_t *el6002; + + list_for_each_entry(el6002, &handlers, list) { + el6002_run(el6002, pd); + } +} + +/*****************************************************************************/ + +int create_el6002_handler(ec_master_t *master, ec_domain_t *domain, + u16 position, u32 vendor, u32 product) +{ + el6002_t *el6002; + int ret; + + printk(KERN_INFO PFX "Creating handler for EL6002 at position %u\n", + position); + + el6002 = kmalloc(sizeof(*el6002), GFP_KERNEL); + if (!el6002) { + printk(KERN_ERR PFX "Failed to allocate serial device object.\n"); + return -ENOMEM; + } + + ret = el6002_init(el6002, master, position, domain, vendor, product); + if (ret) { + kfree(el6002); + return ret; + } + + list_add_tail(&el6002->list, &handlers); + return 0; } /*****************************************************************************/ @@ -396,33 +790,24 @@ goto out_free_handlers; } - if (slave_info.vendor_id != VendorIdBeckhoff - || slave_info.product_code != ProductCodeBeckhoffEL6002) { - continue; + if (slave_info.vendor_id == VendorIdBeckhoff + && slave_info.product_code == ProductCodeBeckhoffEL6002) { + if (create_el6002_handler(master, domain, i, + slave_info.vendor_id, slave_info.product_code)) { + goto out_free_handlers; + } } - printk(KERN_INFO PFX "Creating handler for serial device" - " at position %i\n", i); - - ser = kmalloc(sizeof(*ser), GFP_KERNEL); - if (!ser) { - printk(KERN_ERR PFX "Failed to allocate serial device object.\n"); - ret = -ENOMEM; - goto out_free_handlers; + if (slave_info.vendor_id == VendorIdIds + && slave_info.product_code == ProductCodeIdsCSI71A) { + if (create_el6002_handler(master, domain, i, + slave_info.vendor_id, slave_info.product_code)) { + goto out_free_handlers; + } } - - ret = el6002_init(ser, master, i, domain); - if (ret) { - printk(KERN_ERR PFX "Failed to init serial device object.\n"); - kfree(ser); - goto out_free_handlers; - } - - list_add_tail(&ser->list, &handlers); - } - - - printk(KERN_INFO PFX "Finished.\n"); + } + + printk(KERN_INFO PFX "Finished registering serial devices.\n"); return 0; out_free_handlers: