--- 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 <fp@igh-essen.com>");
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);