# HG changeset patch # User Martin Troxler # Date 1264945837 -3600 # Node ID 5bfbb7be54004943671e458585417abc608268a5 # Parent f228415225b777b86fd87cea974c5f9ddca5c036# Parent 3bb9ca8b58f244115373500292202fe0f5418e1c Merged 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: diff -r f228415225b7 -r 5bfbb7be5400 include/ectty.h --- a/include/ectty.h Thu Jan 21 11:10:22 2010 +0100 +++ b/include/ectty.h Sun Jan 31 14:50:37 2010 +0100 @@ -42,6 +42,8 @@ #ifndef __ECTTY_H__ #define __ECTTY_H__ +#include + /****************************************************************************** * Data types *****************************************************************************/ @@ -49,15 +51,29 @@ struct ec_tty; typedef struct ec_tty ec_tty_t; /**< \see ec_tty */ +/** + * \param cflag_changed This callback function is called when the serial + * settings shall be changed. The \a cflag argument contains the new settings. + */ +typedef struct { + int (*cflag_changed)(void *, tcflag_t); +} ec_tty_operations_t; + /****************************************************************************** * Global functions *****************************************************************************/ /** Create a virtual TTY interface. - * + * + * \param ops Set of callbacks. + * \param cb_data Arbitrary data, that is passed to any callback. + * * \return Pointer to the interface object, otherwise an ERR_PTR value. */ -ec_tty_t *ectty_create(void); +ec_tty_t *ectty_create( + const ec_tty_operations_t *ops, + void *cb_data + ); /****************************************************************************** * TTY interface methods diff -r f228415225b7 -r 5bfbb7be5400 master/fsm_coe.c --- a/master/fsm_coe.c Thu Jan 21 11:10:22 2010 +0100 +++ b/master/fsm_coe.c Sun Jan 31 14:50:37 2010 +0100 @@ -43,12 +43,15 @@ /** Maximum time in ms to wait for responses when reading out the dictionary. */ -#define EC_FSM_COE_DICT_TIMEOUT 3000 +#define EC_FSM_COE_DICT_TIMEOUT 1000 #define EC_COE_DOWN_REQ_HEADER_SIZE 10 #define EC_COE_DOWN_SEG_REQ_HEADER_SIZE 3 #define EC_COE_DOWN_SEG_MIN_DATA_SIZE 7 +#define DEBUG_RETRIES 0 +#define DEBUG_LONG 0 + /*****************************************************************************/ void ec_fsm_coe_dict_start(ec_fsm_coe_t *); @@ -624,8 +627,9 @@ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ; if (diff_ms >= EC_FSM_COE_DICT_TIMEOUT) { fsm->state = ec_fsm_coe_error; - EC_ERR("Timeout while waiting for SDO object description " - "response on slave %u.\n", slave->ring_position); + EC_ERR("Timeout while waiting for SDO 0x%04x object description " + "response on slave %u.\n", fsm->sdo->index, + slave->ring_position); return; } @@ -860,8 +864,9 @@ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ; if (diff_ms >= EC_FSM_COE_DICT_TIMEOUT) { fsm->state = ec_fsm_coe_error; - EC_ERR("Timeout while waiting for SDO entry description response " - "on slave %u.\n", slave->ring_position); + EC_ERR("Timeout while waiting for SDO entry 0x%04x:%x" + " description response on slave %u.\n", + fsm->sdo->index, fsm->subindex, slave->ring_position); return; } @@ -1196,6 +1201,7 @@ { ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; + unsigned long diff_ms; if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) return; // FIXME: check for response first? @@ -1208,27 +1214,39 @@ return; } + diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ; + if (datagram->working_counter != 1) { if (!datagram->working_counter) { - unsigned long diff_ms = - (jiffies - fsm->request->jiffies_sent) * 1000 / HZ; if (diff_ms < fsm->request->response_timeout) { +#if DEBUG_RETRIES if (fsm->slave->master->debug_level) { EC_DBG("Slave %u did not respond to SDO download request. " "Retrying after %u ms...\n", slave->ring_position, (u32) diff_ms); } +#endif // no response; send request datagram again return; } } fsm->state = ec_fsm_coe_error; - EC_ERR("Reception of CoE download request failed on slave %u: ", - slave->ring_position); + EC_ERR("Reception of CoE download request for SDO 0x%04x:%x failed" + " with timeout after %u ms on slave %u: ", + fsm->request->index, fsm->request->subindex, (u32) diff_ms, + fsm->slave->ring_position); ec_datagram_print_wc_error(datagram); return; } +#if DEBUG_LONG + if (diff_ms > 200) { + EC_WARN("SDO 0x%04x:%x download took %u ms on slave %u.\n", + fsm->request->index, fsm->request->subindex, (u32) diff_ms, + fsm->slave->ring_position); + } +#endif + fsm->jiffies_start = datagram->jiffies_sent; ec_slave_mbox_prepare_check(slave, datagram); // can not fail. @@ -1271,8 +1289,10 @@ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ; if (diff_ms >= fsm->request->response_timeout) { fsm->state = ec_fsm_coe_error; - EC_ERR("Timeout while waiting for SDO download response on " - "slave %u.\n", slave->ring_position); + EC_ERR("Timeout after %u ms while waiting for SDO 0x%04x:%x" + " download response on slave %u.\n", (u32) diff_ms, + fsm->request->index, fsm->request->subindex, + slave->ring_position); return; } @@ -1689,6 +1709,7 @@ { ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; + unsigned long diff_ms; if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) return; // FIXME: check for response first? @@ -1701,27 +1722,39 @@ return; } + diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ; + if (datagram->working_counter != 1) { if (!datagram->working_counter) { - unsigned long diff_ms = - (jiffies - fsm->request->jiffies_sent) * 1000 / HZ; if (diff_ms < fsm->request->response_timeout) { +#if DEBUG_RETRIES if (fsm->slave->master->debug_level) { EC_DBG("Slave %u did not respond to SDO upload request. " "Retrying after %u ms...\n", slave->ring_position, (u32) diff_ms); } +#endif // no response; send request datagram again return; } } fsm->state = ec_fsm_coe_error; - EC_ERR("Reception of CoE upload request failed on slave %u: ", - slave->ring_position); + EC_ERR("Reception of CoE upload request for SDO 0x%04x:%x failed" + " with timeout after %u ms on slave %u: ", + fsm->request->index, fsm->request->subindex, (u32) diff_ms, + fsm->slave->ring_position); ec_datagram_print_wc_error(datagram); return; } +#if DEBUG_LONG + if (diff_ms > 200) { + EC_WARN("SDO 0x%04x:%x upload took %u ms on slave %u.\n", + fsm->request->index, fsm->request->subindex, (u32) diff_ms, + fsm->slave->ring_position); + } +#endif + fsm->jiffies_start = datagram->jiffies_sent; ec_slave_mbox_prepare_check(slave, datagram); // can not fail. @@ -1764,8 +1797,10 @@ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ; if (diff_ms >= fsm->request->response_timeout) { fsm->state = ec_fsm_coe_error; - EC_ERR("Timeout while waiting for SDO upload response on " - "slave %u.\n", slave->ring_position); + EC_ERR("Timeout after %u ms while waiting for SDO 0x%04x:%x" + " upload response on slave %u.\n", (u32) diff_ms, + fsm->request->index, fsm->request->subindex, + slave->ring_position); return; } diff -r f228415225b7 -r 5bfbb7be5400 master/sdo_request.c --- a/master/sdo_request.c Thu Jan 21 11:10:22 2010 +0100 +++ b/master/sdo_request.c Sun Jan 31 14:50:37 2010 +0100 @@ -42,7 +42,7 @@ /** Default timeout in ms to wait for SDO transfer responses. */ -#define EC_SDO_REQUEST_RESPONSE_TIMEOUT 3000 +#define EC_SDO_REQUEST_RESPONSE_TIMEOUT 1000 /*****************************************************************************/ diff -r f228415225b7 -r 5bfbb7be5400 script/init.d/ethercat.in --- a/script/init.d/ethercat.in Thu Jan 21 11:10:22 2010 +0100 +++ b/script/init.d/ethercat.in Sun Jan 31 14:50:37 2010 +0100 @@ -49,6 +49,7 @@ RMMOD=/sbin/rmmod MODINFO=/sbin/modinfo ETHERCAT=@prefix@/bin/ethercat +MASTER_ARGS= #------------------------------------------------------------------------------ @@ -162,7 +163,7 @@ done # load master module - if ! ${MODPROBE} ${MODPROBE_FLAGS} ec_master \ + if ! ${MODPROBE} ${MODPROBE_FLAGS} ec_master ${MASTER_ARGS} \ main_devices=${DEVICES} backup_devices=${BACKUPS}; then exit_fail fi diff -r f228415225b7 -r 5bfbb7be5400 tty/module.c --- a/tty/module.c Thu Jan 21 11:10:22 2010 +0100 +++ b/tty/module.c Sun Jan 31 14:50:37 2010 +0100 @@ -41,6 +41,8 @@ #include #include #include +#include +#include #include "../master/globals.h" #include "../include/ectty.h" @@ -49,7 +51,7 @@ #define PFX "ec_tty: " -#define EC_TTY_MAX_DEVICES 10 +#define EC_TTY_MAX_DEVICES 32 #define EC_TTY_TX_BUFFER_SIZE 100 #define EC_TTY_RX_BUFFER_SIZE 100 @@ -64,6 +66,8 @@ ec_tty_t *ttys[EC_TTY_MAX_DEVICES]; struct semaphore tty_sem; +void ec_tty_wakeup(unsigned long); + /*****************************************************************************/ /** \cond */ @@ -78,11 +82,15 @@ /** \endcond */ +/** Standard termios for ec_tty devices. + * + * Simplest possible configuration, as you would expect. + */ static struct ktermios ec_tty_std_termios = { - .c_iflag = ICRNL | IXON, - .c_oflag = OPOST, - .c_cflag = B38400 | CS8 | CREAD | HUPCL, - .c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN, + .c_iflag = 0, + .c_oflag = 0, + .c_cflag = B9600 | CS8 | CREAD, + .c_lflag = 0, .c_cc = INIT_C_CC, }; @@ -101,6 +109,9 @@ struct timer_list timer; struct tty_struct *tty; + + ec_tty_operations_t ops; + void *cb_data; }; static const struct tty_operations ec_tty_ops; // see below @@ -139,7 +150,6 @@ tty_driver->subtype = SERIAL_TYPE_NORMAL; tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; tty_driver->init_termios = ec_tty_std_termios; - tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; tty_set_operations(tty_driver, &ec_tty_ops); ret = tty_register_driver(tty_driver); @@ -169,6 +179,65 @@ printk(KERN_INFO PFX "Module unloading.\n"); } +/****************************************************************************** + * ec_tty_t methods. + *****************************************************************************/ + +int ec_tty_init(ec_tty_t *t, int minor, + const ec_tty_operations_t *ops, void *cb_data) +{ + int ret; + tcflag_t cflag; + struct tty_struct *tty; + + t->minor = minor; + t->tx_read_idx = 0; + t->tx_write_idx = 0; + t->wakeup = 0; + t->rx_read_idx = 0; + t->rx_write_idx = 0; + init_timer(&t->timer); + t->tty = NULL; + t->ops = *ops; + t->cb_data = cb_data; + + t->dev = tty_register_device(tty_driver, t->minor, NULL); + if (IS_ERR(t->dev)) { + printk(KERN_ERR PFX "Failed to register tty device.\n"); + return PTR_ERR(t->dev); + } + + // Tell the device-specific implementation about the initial cflags + tty = tty_driver->ttys[minor]; + + if (tty && tty->termios) { // already opened before + cflag = tty->termios->c_cflag; + } else { + cflag = tty_driver->init_termios.c_cflag; + } + ret = t->ops.cflag_changed(t->cb_data, cflag); + if (ret) { + printk(KERN_ERR PFX "ERROR: Initial cflag 0x%x not accepted.\n", + cflag); + tty_unregister_device(tty_driver, t->minor); + return ret; + } + + t->timer.function = ec_tty_wakeup; + t->timer.data = (unsigned long) t; + t->timer.expires = jiffies + 10; + add_timer(&t->timer); + return 0; +} + +/*****************************************************************************/ + +void ec_tty_clear(ec_tty_t *tty) +{ + del_timer_sync(&tty->timer); + tty_unregister_device(tty_driver, tty->minor); +} + /*****************************************************************************/ unsigned int ec_tty_tx_size(ec_tty_t *tty) @@ -215,6 +284,25 @@ /*****************************************************************************/ +int ec_tty_get_serial_info(ec_tty_t *tty, struct serial_struct *data) +{ + struct serial_struct tmp; + + if (!data) + return -EFAULT; + + memset(&tmp, 0, sizeof(tmp)); + + if (copy_to_user(data, &tmp, sizeof(*data))) { + return -EFAULT; + } + return 0; +} + +/*****************************************************************************/ + +/** Timer function. + */ void ec_tty_wakeup(unsigned long data) { ec_tty_t *tty = (ec_tty_t *) data; @@ -237,10 +325,10 @@ unsigned char *cbuf; int space = tty_prepare_flip_string(tty->tty, &cbuf, to_recv); - if (space < to_recv) { - printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n", + if (space < to_recv) { + printk(KERN_WARNING PFX "Insufficient space to_recv=%d space=%d\n", to_recv, space); - } + } if (space < 0) { to_recv = 0; @@ -257,50 +345,17 @@ for (i = 0; i < to_recv; i++) { cbuf[i] = tty->rx_buffer[tty->rx_read_idx]; - tty->rx_read_idx = (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; + tty->rx_read_idx = + (tty->rx_read_idx + 1) % EC_TTY_RX_BUFFER_SIZE; } tty_flip_buffer_push(tty->tty); } - } + } tty->timer.expires += 1; add_timer(&tty->timer); } -/*****************************************************************************/ - -int ec_tty_init(ec_tty_t *tty, int minor) -{ - tty->minor = minor; - tty->tx_read_idx = 0; - tty->tx_write_idx = 0; - tty->wakeup = 0; - tty->rx_read_idx = 0; - tty->rx_write_idx = 0; - init_timer(&tty->timer); - tty->tty = NULL; - - tty->dev = tty_register_device(tty_driver, tty->minor, NULL); - if (IS_ERR(tty->dev)) { - printk(KERN_ERR PFX "Failed to register tty device.\n"); - return PTR_ERR(tty->dev); - } - - tty->timer.function = ec_tty_wakeup; - tty->timer.data = (unsigned long) tty; - tty->timer.expires = jiffies + 10; - add_timer(&tty->timer); - return 0; -} - -/*****************************************************************************/ - -void ec_tty_clear(ec_tty_t *tty) -{ - del_timer_sync(&tty->timer); - tty_unregister_device(tty_driver, tty->minor); -} - /****************************************************************************** * Device callbacks *****************************************************************************/ @@ -314,8 +369,8 @@ printk(KERN_INFO PFX "Opening line %i.\n", line); #endif - if (line < 0 || line >= EC_TTY_MAX_DEVICES) { - return -ENXIO; + if (line < 0 || line >= EC_TTY_MAX_DEVICES) { + return -ENXIO; } t = ttys[line]; @@ -447,45 +502,75 @@ #if EC_TTY_DEBUG >= 2 printk(KERN_INFO PFX "%s().\n", __func__); #endif + + // FIXME empty ring buffer } /*****************************************************************************/ static int ec_tty_ioctl(struct tty_struct *tty, struct file *file, - unsigned int cmd, unsigned long arg) -{ + unsigned int cmd, unsigned long arg) +{ + ec_tty_t *t = (ec_tty_t *) tty->driver_data; + int ret = -ENOTTY; + +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO PFX "%s(tty=%p, file=%p, cmd=%08x, arg=%08lx).\n", + __func__, tty, file, cmd, arg); + printk(KERN_INFO PFX "decoded: type=%02x nr=%u\n", + _IOC_TYPE(cmd), _IOC_NR(cmd)); +#endif + + switch (cmd) { + case TIOCGSERIAL: + if (access_ok(VERIFY_WRITE, + (void *) arg, sizeof(struct serial_struct))) { + ret = ec_tty_get_serial_info(t, (struct serial_struct *) arg); + } else { + ret = -EFAULT; + } + break; + + case TIOCSSERIAL: // TODO + break; + + default: +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO PFX "no ioctl() -> handled by tty core!\n"); +#endif + ret = -ENOIOCTLCMD; + break; + } + + return ret; +} + +/*****************************************************************************/ + +static void ec_tty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + ec_tty_t *t = (ec_tty_t *) tty->driver_data; + int ret; + #if EC_TTY_DEBUG >= 2 printk(KERN_INFO PFX "%s().\n", __func__); #endif - return -ENOTTY; -} - -/*****************************************************************************/ - -static void ec_tty_throttle(struct tty_struct *tty) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif -} - -/*****************************************************************************/ - -static void ec_tty_unthrottle(struct tty_struct *tty) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif -} - -/*****************************************************************************/ - -static void ec_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif + + if (tty->termios->c_cflag == old_termios->c_cflag) + return; + +#if EC_TTY_DEBUG >= 2 + printk(KERN_INFO "cflag changed from %x to %x.\n", + old_termios->c_cflag, tty->termios->c_cflag); +#endif + + ret = t->ops.cflag_changed(t->cb_data, tty->termios->c_cflag); + if (ret) { + printk(KERN_ERR PFX "ERROR: cflag 0x%x not accepted.\n", + tty->termios->c_cflag); + tty->termios->c_cflag = old_termios->c_cflag; + } } /*****************************************************************************/ @@ -552,54 +637,29 @@ /*****************************************************************************/ -static int ec_tty_tiocmget(struct tty_struct *tty, struct file *file) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s().\n", __func__); -#endif - return -EBUSY; -} - -/*****************************************************************************/ - -static int ec_tty_tiocmset(struct tty_struct *tty, struct file *file, - unsigned int set, unsigned int clear) -{ -#if EC_TTY_DEBUG >= 2 - printk(KERN_INFO PFX "%s(set=%u, clear=%u).\n", __func__, set, clear); -#endif - return -EBUSY; -} - -/*****************************************************************************/ - static const struct tty_operations ec_tty_ops = { .open = ec_tty_open, .close = ec_tty_close, .write = ec_tty_write, - .put_char = ec_tty_put_char, - .write_room = ec_tty_write_room, - .chars_in_buffer = ec_tty_chars_in_buffer, - .flush_buffer = ec_tty_flush_buffer, - .ioctl = ec_tty_ioctl, - .throttle = ec_tty_throttle, - .unthrottle = ec_tty_unthrottle, - .set_termios = ec_tty_set_termios, - .stop = ec_tty_stop, - .start = ec_tty_start, - .hangup = ec_tty_hangup, - .break_ctl = ec_tty_break, - .send_xchar = ec_tty_send_xchar, - .wait_until_sent = ec_tty_wait_until_sent, - .tiocmget = ec_tty_tiocmget, - .tiocmset = ec_tty_tiocmset, + .put_char = ec_tty_put_char, + .write_room = ec_tty_write_room, + .chars_in_buffer = ec_tty_chars_in_buffer, + .flush_buffer = ec_tty_flush_buffer, + .ioctl = ec_tty_ioctl, + .set_termios = ec_tty_set_termios, + .stop = ec_tty_stop, + .start = ec_tty_start, + .hangup = ec_tty_hangup, + .break_ctl = ec_tty_break, + .send_xchar = ec_tty_send_xchar, + .wait_until_sent = ec_tty_wait_until_sent, }; /****************************************************************************** * Public functions and methods *****************************************************************************/ -ec_tty_t *ectty_create(void) +ec_tty_t *ectty_create(const ec_tty_operations_t *ops, void *cb_data) { ec_tty_t *tty; int minor, ret; @@ -619,7 +679,7 @@ return ERR_PTR(-ENOMEM); } - ret = ec_tty_init(tty, minor); + ret = ec_tty_init(tty, minor, ops, cb_data); if (ret) { up(&tty_sem); kfree(tty); @@ -693,7 +753,8 @@ for (i = 0; i < size; i++) { tty->rx_buffer[tty->rx_write_idx] = buffer[i]; - tty->rx_write_idx = (tty->rx_write_idx + 1) % EC_TTY_RX_BUFFER_SIZE; + tty->rx_write_idx = + (tty->rx_write_idx + 1) % EC_TTY_RX_BUFFER_SIZE; } } }