fp@575: /****************************************************************************** fp@575: * fp@575: * $Id$ fp@575: * fp@575: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@575: * fp@575: * This file is part of the IgH EtherCAT Master. fp@575: * fp@575: * The IgH EtherCAT Master is free software; you can redistribute it fp@575: * and/or modify it under the terms of the GNU General Public License fp@575: * as published by the Free Software Foundation; either version 2 of the fp@575: * License, or (at your option) any later version. fp@575: * fp@575: * The IgH EtherCAT Master is distributed in the hope that it will be fp@575: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@575: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@575: * GNU General Public License for more details. fp@575: * fp@575: * You should have received a copy of the GNU General Public License fp@575: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@575: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@575: * fp@575: * The right to use EtherCAT Technology is granted and comes free of fp@575: * charge under condition of compatibility of product made by fp@575: * Licensee. People intending to distribute/sell products based on the fp@575: * code, have to sign an agreement to guarantee that products using fp@575: * software based on IgH EtherCAT master stay compatible with the actual fp@575: * EtherCAT specification (which are released themselves as an open fp@575: * standard) as the (only) precondition to have the right to use EtherCAT fp@575: * Technology, IP and trade marks. fp@575: * fp@575: *****************************************************************************/ fp@575: fp@575: /** fp@575: \file fp@575: EtherCAT device ID. fp@575: */ fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: #include fp@575: #include fp@575: fp@575: #include "globals.h" fp@575: #include "device_id.h" fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: static int ec_device_id_parse_mac(ec_device_id_t *dev_id, fp@575: const char *src, const char **remainder) fp@575: { fp@575: unsigned int i, value; fp@575: char *rem; fp@575: fp@575: for (i = 0; i < ETH_ALEN; i++) { fp@575: value = simple_strtoul(src, &rem, 16); fp@575: if (rem != src + 2 fp@575: || value > 0xFF fp@575: || (i < ETH_ALEN - 1 && *rem != ':')) { fp@575: return -1; fp@575: } fp@575: dev_id->octets[i] = value; fp@575: if (i < ETH_ALEN - 1) fp@575: src = rem + 1; fp@575: } fp@575: fp@575: dev_id->type = ec_device_id_mac; fp@575: *remainder = rem; fp@575: return 0; fp@575: } fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: void ec_device_id_clear_list(struct list_head *ids) fp@575: { fp@575: ec_device_id_t *dev_id, *next_dev_id; fp@575: fp@575: list_for_each_entry_safe(dev_id, next_dev_id, ids, list) { fp@575: list_del(&dev_id->list); fp@575: kfree(dev_id); fp@575: } fp@575: } fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: static int ec_device_id_create_list(struct list_head *ids, const char *src) fp@575: { fp@575: const char *rem; fp@575: ec_device_id_t *dev_id; fp@575: unsigned int index = 0; fp@575: fp@575: while (*src) { fp@575: // allocate new device ID fp@575: if (!(dev_id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) { fp@575: EC_ERR("Out of memory!\n"); fp@575: goto out_free; fp@575: } fp@575: fp@575: if (*src == ';') { // empty device ID fp@575: dev_id->type = ec_device_id_empty; fp@575: } fp@575: else if (*src == 'M') { fp@575: src++; fp@575: if (ec_device_id_parse_mac(dev_id, src, &rem)) { fp@575: EC_ERR("Device ID %u: Invalid MAC syntax!\n", index); fp@575: kfree(dev_id); fp@575: goto out_free; fp@575: } fp@575: src = rem; fp@575: } fp@575: else { fp@575: EC_ERR("Device ID %u: Unknown format \'%c\'!\n", index, *src); fp@575: kfree(dev_id); fp@575: goto out_free; fp@575: } fp@575: fp@575: list_add_tail(&dev_id->list, ids); fp@575: fp@575: if (*src) { fp@575: if (*src != ';') { fp@575: EC_ERR("Invalid delimiter '%c' after device ID %i!\n", fp@575: *src, index); fp@575: goto out_free; fp@575: } fp@575: src++; // skip delimiter fp@575: } fp@575: index++; fp@575: } fp@575: fp@575: return 0; fp@575: fp@575: out_free: fp@575: ec_device_id_clear_list(ids); fp@575: return -1; fp@575: } fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: int ec_device_id_process_params(const char *main, const char *backup, fp@575: struct list_head *main_ids, struct list_head *backup_ids) fp@575: { fp@575: ec_device_id_t *id; fp@575: unsigned int main_count = 0, backup_count = 0; fp@575: fp@575: if (ec_device_id_create_list(main_ids, main)) fp@575: return -1; fp@575: fp@575: if (ec_device_id_create_list(backup_ids, backup)) fp@575: return -1; fp@575: fp@575: // count main device IDs and check for empty ones fp@575: list_for_each_entry(id, main_ids, list) { fp@575: if (id->type == ec_device_id_empty) { fp@575: EC_ERR("Main device IDs may not be empty!\n"); fp@575: return -1; fp@575: } fp@575: main_count++; fp@575: } fp@575: fp@575: // count backup device IDs fp@575: list_for_each_entry(id, backup_ids, list) { fp@575: backup_count++; fp@575: } fp@575: fp@575: // fill up backup device IDs fp@575: while (backup_count < main_count) { fp@575: if (!(id = kmalloc(sizeof(ec_device_id_t), GFP_KERNEL))) { fp@575: EC_ERR("Out of memory!\n"); fp@575: return -1; fp@575: } fp@575: fp@575: id->type = ec_device_id_empty; fp@575: list_add_tail(&id->list, backup_ids); fp@575: backup_count++; fp@575: } fp@575: fp@575: return 0; fp@575: } fp@575: fp@575: /*****************************************************************************/ fp@575: fp@575: int ec_device_id_check(const ec_device_id_t *dev_id, fp@575: const struct net_device *dev, const char *driver_name, fp@575: unsigned int device_index) fp@575: { fp@575: unsigned int i; fp@575: fp@575: switch (dev_id->type) { fp@575: case ec_device_id_mac: fp@575: for (i = 0; i < ETH_ALEN; i++) fp@575: if (dev->dev_addr[i] != dev_id->octets[i]) fp@575: return 0; fp@575: return 1; fp@575: default: fp@575: return 0; fp@575: } fp@575: } fp@575: fp@575: /*****************************************************************************/ fp@577: fp@577: ssize_t ec_device_id_print(const ec_device_id_t *dev_id, char *buffer) fp@577: { fp@577: off_t off = 0; fp@577: unsigned int i; fp@577: fp@577: switch (dev_id->type) { fp@577: case ec_device_id_empty: fp@577: off += sprintf(buffer + off, "none"); fp@577: break; fp@577: case ec_device_id_mac: fp@577: off += sprintf(buffer + off, "MAC "); fp@577: for (i = 0; i < ETH_ALEN; i++) { fp@577: off += sprintf(buffer + off, "%02X", dev_id->octets[i]); fp@577: if (i < ETH_ALEN - 1) off += sprintf(buffer + off, ":"); fp@577: } fp@577: break; fp@577: } fp@577: fp@577: return off; fp@577: } fp@577: fp@577: /*****************************************************************************/