diff -r aef7ea866a41 -r cdee4ea90ce9 master/module.c --- a/master/module.c Fri Feb 16 13:30:46 2007 +0000 +++ b/master/module.c Fri Feb 16 17:13:39 2007 +0000 @@ -54,8 +54,12 @@ /*****************************************************************************/ -static int ec_master_count = 1; /**< parameter value, number of masters */ -static struct list_head ec_masters; /**< list of masters */ +static char *main; /**< main devices parameter */ +static char *backup; /**< backup devices parameter */ + +static LIST_HEAD(main_device_ids); /**< list of main device IDs */ +static LIST_HEAD(backup_device_ids); /**< list of main device IDs */ +static LIST_HEAD(masters); /**< list of masters */ static dev_t device_number; /**< XML character device number */ ec_xmldev_t xmldev; /**< XML character device */ @@ -65,18 +69,171 @@ /** \cond */ -module_param(ec_master_count, int, S_IRUGO); - MODULE_AUTHOR("Florian Pose "); MODULE_DESCRIPTION("EtherCAT master driver module"); MODULE_LICENSE("GPL"); MODULE_VERSION(EC_MASTER_VERSION); -MODULE_PARM_DESC(ec_master_count, "number of EtherCAT masters to initialize"); + +module_param(main, charp, S_IRUGO); +MODULE_PARM_DESC(main, "main device IDs"); +module_param(backup, charp, S_IRUGO); +MODULE_PARM_DESC(backup, "backup device IDs"); /** \endcond */ /*****************************************************************************/ +void clear_device_ids(struct list_head *device_ids) +{ + ec_device_id_t *dev_id, *next_dev_id; + + list_for_each_entry_safe(dev_id, next_dev_id, device_ids, list) { + list_del(&dev_id->list); + kfree(dev_id); + } +} + +/*****************************************************************************/ + +static int parse_device_id_mac(ec_device_id_t *dev_id, + const char *src, const char **remainder) +{ + unsigned int i, value; + char *rem; + + for (i = 0; i < ETH_ALEN; i++) { + value = simple_strtoul(src, &rem, 16); + if (rem != src + 2 + || value > 0xFF + || (i < ETH_ALEN - 1 && *rem != ':')) { + return -1; + } + dev_id->octets[i] = value; + if (i < ETH_ALEN - 1) + src = rem + 1; + } + + dev_id->type = ec_device_id_mac; + *remainder = rem; + return 0; +} + +/*****************************************************************************/ + +static int parse_device_ids(struct list_head *device_ids, const char *src) +{ + const char *rem; + ec_device_id_t *dev_id; + unsigned int index = 0; + + while (*src) { + // allocate new device ID + if (!(dev_id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) { + EC_ERR("Out of memory!\n"); + goto out_free; + } + + if (*src == ';') { // empty device ID + dev_id->type = ec_device_id_empty; + } + else if (*src == 'M') { + src++; + if (parse_device_id_mac(dev_id, src, &rem)) { + EC_ERR("Device ID %u: Invalid MAC syntax!\n", index); + kfree(dev_id); + goto out_free; + } + src = rem; + } + else { + EC_ERR("Device ID %u: Unknown format \'%c\'!\n", index, *src); + kfree(dev_id); + goto out_free; + } + + list_add_tail(&dev_id->list, device_ids); + if (*src) { + if (*src != ';') { + EC_ERR("Invalid delimiter '%c' after device ID %i!\n", + *src, index); + goto out_free; + } + src++; // skip delimiter + } + index++; + } + + return 0; + +out_free: + clear_device_ids(device_ids); + return -1; +} + +/*****************************************************************************/ + +static int create_device_ids(void) +{ + ec_device_id_t *id; + unsigned int main_count = 0, backup_count = 0; + + if (parse_device_ids(&main_device_ids, main)) + return -1; + + if (parse_device_ids(&backup_device_ids, main)) + return -1; + + // count main device IDs and check for empty ones + list_for_each_entry(id, &main_device_ids, list) { + if (id->type == ec_device_id_empty) { + EC_ERR("Main device IDs may not be empty!\n"); + return -1; + } + main_count++; + } + + // count backup device IDs + list_for_each_entry(id, &backup_device_ids, list) { + backup_count++; + } + + // fill up backup device IDs + while (backup_count < main_count) { + if (!(id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) { + EC_ERR("Out of memory!\n"); + return -1; + } + + id->type = ec_device_id_empty; + list_add_tail(&id->list, &backup_device_ids); + backup_count++; + } + + return 0; +} + +/*****************************************************************************/ + +static int device_id_check(const ec_device_id_t *dev_id, + const struct net_device *dev, const char *driver_name, + unsigned int device_index) +{ + unsigned int i; + + switch (dev_id->type) { + case ec_device_id_mac: + for (i = 0; i < ETH_ALEN; i++) + if (dev->dev_addr[i] != dev_id->octets[i]) + return 0; + return 1; + default: + return 0; + } +} + + +/*****************************************************************************/ + /** Module initialization. Initializes \a ec_master_count masters. @@ -85,48 +242,68 @@ int __init ec_init_module(void) { - unsigned int i; ec_master_t *master, *next; + ec_device_id_t *main_dev_id, *backup_dev_id; + unsigned int master_index = 0; EC_INFO("Master driver %s\n", EC_MASTER_VERSION); - if (ec_master_count < 1) { - EC_ERR("Invalid ec_master_count: %i\n", ec_master_count); + if (alloc_chrdev_region(&device_number, 0, 1, "EtherCAT")) { + EC_ERR("Failed to obtain device number!\n"); goto out_return; } - if (alloc_chrdev_region(&device_number, 0, ec_master_count, "EtherCAT")) { - EC_ERR("Failed to allocate device number!\n"); - goto out_return; - } - - EC_INFO("Initializing %i EtherCAT master(s)...\n", ec_master_count); - - INIT_LIST_HEAD(&ec_masters); - - for (i = 0; i < ec_master_count; i++) { - if (!(master = - (ec_master_t *) kmalloc(sizeof(ec_master_t), GFP_KERNEL))) { - EC_ERR("Failed to allocate memory for EtherCAT master %i.\n", i); - goto out_free; - } - - if (ec_master_init(master, i, 0)) - goto out_free; - - list_add_tail(&master->list, &ec_masters); - } - - EC_INFO("Master driver initialized.\n"); + if (create_device_ids()) + goto out_free_ids; + + if (!list_empty(&main_device_ids)) { + main_dev_id = + list_entry(main_device_ids.next, ec_device_id_t, list); + backup_dev_id = + list_entry(backup_device_ids.next, ec_device_id_t, list); + + while (1) { + if (!(master = (ec_master_t *) + kmalloc(sizeof(ec_master_t), GFP_KERNEL))) { + EC_ERR("Failed to allocate memory for EtherCAT master %i.\n", + master_index); + goto out_free_masters; + } + + if (ec_master_init(master, master_index, + main_dev_id, backup_dev_id, 0)) + goto out_free_masters; + + list_add_tail(&master->list, &masters); + master_index++; + + // last device IDs? + if (main_dev_id->list.next == &main_device_ids) + break; + + // next device IDs + main_dev_id = + list_entry(main_dev_id->list.next, ec_device_id_t, list); + backup_dev_id = + list_entry(backup_dev_id->list.next, ec_device_id_t, list); + } + } + + EC_INFO("%u master%s waiting for devices.\n", + master_index, (master_index == 1 ? "" : "s")); return 0; - out_free: - list_for_each_entry_safe(master, next, &ec_masters, list) { +out_free_masters: + list_for_each_entry_safe(master, next, &masters, list) { list_del(&master->list); kobject_del(&master->kobj); kobject_put(&master->kobj); } - out_return: +out_free_ids: + clear_device_ids(&main_device_ids); + clear_device_ids(&backup_device_ids); + unregister_chrdev_region(device_number, 1); +out_return: return -1; } @@ -143,12 +320,12 @@ EC_INFO("Cleaning up master driver...\n"); - list_for_each_entry_safe(master, next, &ec_masters, list) { + list_for_each_entry_safe(master, next, &masters, list) { list_del(&master->list); ec_master_destroy(master); } - unregister_chrdev_region(device_number, ec_master_count); + unregister_chrdev_region(device_number, 1); EC_INFO("Master driver cleaned up.\n"); } @@ -164,7 +341,7 @@ { ec_master_t *master; - list_for_each_entry(master, &ec_masters, list) { + list_for_each_entry(master, &masters, list) { if (master->index == master_index) return master; } @@ -270,48 +447,71 @@ *****************************************************************************/ /** - Connects an EtherCAT device to a certain master. - The master will use the device for sending and receiving frames. It is - required that no other instance (for example the kernel IP stack) uses - the device. + Offers an EtherCAT device to a certain master. + The master decides, if it wants to use the device for EtherCAT operation, + or not. It is important, that the offered net_device is not used by + the kernel IP stack. If the master, accepted the offer, the address of + the newly created EtherCAT device is written to the ecdev pointer, else + the pointer is written to zero. \return 0 on success, else < 0 \ingroup DeviceInterface */ -ec_device_t *ecdev_register(unsigned int master_index, /**< master index */ - struct net_device *net_dev, /**< net_device of - the device */ - ec_pollfunc_t poll, /**< device poll function */ - struct module *module /**< pointer to the module */ - ) +int ecdev_offer(struct net_device *net_dev, /**< net_device to offer */ + ec_device_t **ecdev, /**< pointer to store a device on success */ + const char *driver_name, /**< name of the network driver */ + unsigned int device_index, /**< index of the supported device */ + ec_pollfunc_t poll, /**< device poll function */ + struct module *module /**< pointer to the module */ + ) { ec_master_t *master; - - if (!(master = ec_find_master(master_index))) return NULL; - - if (down_interruptible(&master->device_sem)) { - EC_ERR("Interrupted while waiting for device!\n"); - goto out_return; - } - - if (master->device) { - EC_ERR("Master %i already has a device!\n", master_index); - goto out_up; - } - - if (!(master->device = - (ec_device_t *) kmalloc(sizeof(ec_device_t), GFP_KERNEL))) { - EC_ERR("Failed to allocate device!\n"); - goto out_up; - } - - if (ec_device_init(master->device, master, net_dev, poll, module)) { - EC_ERR("Failed to init device!\n"); - goto out_free; - } - - up(&master->device_sem); - return master->device; + unsigned int i; + + list_for_each_entry(master, &masters, list) { + if (down_interruptible(&master->device_sem)) { + EC_ERR("Interrupted while waiting for device semaphore!\n"); + goto out_return; + } + + if (device_id_check(master->main_device_id, net_dev, + driver_name, device_index)) { + + EC_INFO("Accepting device %s:%u (", driver_name, device_index); + for (i = 0; i < ETH_ALEN; i++) { + printk("%02X", net_dev->dev_addr[i]); + if (i < ETH_ALEN - 1) printk(":"); + } + printk(") for master %u.\n", master->index); + + if (master->device) { + EC_ERR("Master already has a device.\n"); + goto out_up; + } + + if (!(master->device = (ec_device_t *) + kmalloc(sizeof(ec_device_t), GFP_KERNEL))) { + EC_ERR("Failed to allocate device!\n"); + goto out_up; + } + + if (ec_device_init(master->device, master, + net_dev, poll, module)) { + EC_ERR("Failed to init device!\n"); + goto out_free; + } + + up(&master->device_sem); + sprintf(net_dev->name, "ec%u", master->index); + *ecdev = master->device; // offer accepted + return 0; // no error + } + + up(&master->device_sem); + } + + *ecdev = NULL; // offer declined + return 0; // no error out_free: kfree(master->device); @@ -319,13 +519,13 @@ out_up: up(&master->device_sem); out_return: - return NULL; -} - -/*****************************************************************************/ - -/** - Disconnect an EtherCAT device from the master. + return 1; +} + +/*****************************************************************************/ + +/** + Withdraws an EtherCAT device from the master. The device is disconnected from the master and all device ressources are freed. \attention Before calling this function, the ecdev_stop() function has @@ -333,26 +533,24 @@ \ingroup DeviceInterface */ -void ecdev_unregister(unsigned int master_index, /**< master index */ - ec_device_t *device /**< EtherCAT device */ - ) -{ - ec_master_t *master; - - if (!(master = ec_find_master(master_index))) return; +void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */) +{ + ec_master_t *master = device->master; + unsigned int i; down(&master->device_sem); - - if (!master->device || master->device != device) { - up(&master->device_sem); - EC_WARN("Unable to unregister device!\n"); - return; - } - + + EC_INFO("Master %u releasing device ", master->index); + for (i = 0; i < ETH_ALEN; i++) { + printk("%02X", device->dev->dev_addr[i]); + if (i < ETH_ALEN - 1) printk(":"); + } + printk(".\n"); + ec_device_clear(master->device); kfree(master->device); master->device = NULL; - + up(&master->device_sem); } @@ -505,8 +703,8 @@ module_init(ec_init_module); module_exit(ec_cleanup_module); -EXPORT_SYMBOL(ecdev_register); -EXPORT_SYMBOL(ecdev_unregister); +EXPORT_SYMBOL(ecdev_offer); +EXPORT_SYMBOL(ecdev_withdraw); EXPORT_SYMBOL(ecdev_open); EXPORT_SYMBOL(ecdev_close); EXPORT_SYMBOL(ecrt_request_master);