drivers/net/Kconfig | 15 ++ drivers/net/Makefile | 1 drivers/net/maclist.c | 314 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/net/maclist.h | 23 +++ 4 files changed, 353 insertions(+) --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/include/net/maclist.h 2006-02-06 22:35:23.000000000 +0100 @@ -0,0 +1,23 @@ +#ifndef _MACLIST_H +#define _MACLIST_H 1 +/* + * Interfaces to the MAC repository + */ +/* + * Add a single entry, returns 0 on success else an error + * code. Must *not* be called from an interrupt handler. + */ +extern int maclist_add(const u8 id_to_add[6]); + +/* + * Return the current entry count (valid in any context). + */ +extern int maclist_count(void); + +/* + * Return the ID from the n'th entry (valid in any context), + * returns 0 on success, -EINVAL if 'n' is out of range. + */ +extern int maclist_read(u8 (*buffer_for_id)[6], int index_of_id_to_return); + +#endif /*_MACLIST_H*/ --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-nslu2/drivers/net/maclist.c 2006-02-06 22:35:23.000000000 +0100 @@ -0,0 +1,314 @@ +/* + * drivers/net/maclist.c + * + * a simple driver to remember ethernet MAC values + * + * Some Ethernet hardware implementations have no built-in + * storage for allocated MAC values - an example is the Intel + * IXP420 chip which has support for Ethernet but no defined + * way of storing allocated MAC values. With such hardware + * different board level implementations store the allocated + * MAC (or MACs) in different ways. Rather than put board + * level code into a specific Ethernet driver this driver + * provides a generally accessible repository for the MACs + * which can be written by board level code and read by the + * driver. + * + * The implementation also allows user level programs to + * access the MAC information in /proc/net/maclist. This is + * useful as it allows user space code to use the MAC if it + * is not used by a built-in driver. + * + * Copyright (C) 2005 John Bowler + * Author: John Bowler + * Maintainers: http://www.nslu2-linux.org/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * External interfaces: + * Interfaces to linux kernel (and modules) + * maclist_add: add a single MAC + * maclist_count: total number of MACs stored + * maclist_read: read a MAC 0..(maclist_count-1) + */ +#include +#include +#include +#include +#include + +#include + +#define MACLIST_NAME "maclist" + +MODULE_AUTHOR("John Bowler "); +MODULE_DESCRIPTION("MAC list repository"); +MODULE_LICENSE("GPL"); + +typedef struct maclist_entry { + struct maclist_entry *next; /* Linked list, first first */ + u8 id[6]; /* 6 byte Ethernet MAC */ +} maclist_entry_t; + +/* Access to this list is possible at any time - entries in + * the list are never destroyed. Modification of the list is + * safe only from the init code (i.e. modification must be + * single threaded), but read from an interrupt at the same + * time is possible and safe. + */ +static maclist_entry_t *maclist_list = 0; + +/* + * External interfaces. + * + * Add a single entry, returns 0 on success else an error + * code. Must be single threaded. + */ +int maclist_add(const u8 new_id[6]) { + maclist_entry_t *new_entry, **tail; + + if (new_id == 0 || !is_valid_ether_addr(new_id)) { + printk(KERN_ERR MACLIST_NAME ": invalid ethernet address\n"); + return -EINVAL; + } + new_entry = kmalloc(sizeof *new_entry, GFP_KERNEL); + if (new_entry == 0) + return -ENOMEM; + new_entry->next = 0; + memcpy(new_entry->id, new_id, sizeof new_entry->id); + + tail = &maclist_list; + while (*tail != 0) + tail = &(*tail)->next; + *tail = new_entry; + return 0; +} +EXPORT_SYMBOL(maclist_add); + +/* + * Return the current entry count (valid in any context). + */ +int maclist_count(void) { + maclist_entry_t *tail = maclist_list; + int count = 0; + + while (tail != 0) { + tail = tail->next; + ++count; + } + + return count; +} +EXPORT_SYMBOL(maclist_count); + +/* + * Return the ID from the n'th entry (valid in any context), + * returns 0 on success, -EINVAL if 'n' is out of range. + */ +int maclist_read(u8 (*id)[6], int n) { + maclist_entry_t *entry = maclist_list; + + while (n > 0 && entry != 0) { + --n; + entry = entry->next; + } + + if (n == 0 && entry != 0) { + memcpy(id, entry->id, sizeof *id); + return 0; + } + + printk(KERN_ERR MACLIST_NAME ": id does not exist\n"); + return -EINVAL; +} +EXPORT_SYMBOL(maclist_read); + +/* + * Parameter parsing. The option string is a list of MAC + * addresses, comma separated. (The parsing really should + * be somewhere central...) + */ +static int __init maclist_setup(char *param) { + int bytes = 0, seen_a_digit = 0; + u8 id[6]; + + memset(id, 0, sizeof id); + + if (param) do { + int digit = -1; + switch (*param) { + case '0': digit = 0; break; + case '1': digit = 1; break; + case '2': digit = 2; break; + case '3': digit = 3; break; + case '4': digit = 4; break; + case '5': digit = 5; break; + case '6': digit = 6; break; + case '7': digit = 7; break; + case '8': digit = 8; break; + case '9': digit = 9; break; + case 'a': case 'A': digit = 10; break; + case 'b': case 'B': digit = 11; break; + case 'c': case 'C': digit = 12; break; + case 'd': case 'D': digit = 13; break; + case 'e': case 'E': digit = 14; break; + case 'f': case 'F': digit = 15; break; + case ':': + if (seen_a_digit) + bytes = (bytes+1) & ~1; + else + bytes += 2; /* i.e. ff::ff is ff:00:ff */ + seen_a_digit = 0; + break; + case 0: + if (bytes == 0) /* nothing new seen so far */ + return 0; + /*fall through*/ + case ',': case ';': + if (bytes > 0) + bytes = 12; /* i.e. all trailing bytes 0 */ + break; + default: + printk(KERN_ERR MACLIST_NAME ": invalid character <%c[%d]>\n", + *param, *param); + return -EINVAL; + } + + if (digit >= 0) { + id[bytes>>1] = (id[bytes>>1] << 4) + digit; break; + ++bytes; + seen_a_digit = 1; + } + + if (bytes >= 12) { + int rc = maclist_add(id); + if (rc) + return rc; + bytes = 0; + seen_a_digit = 0; + memset(id, 0, sizeof id); + if (*param == 0) + return 0; + } + ++param; + } while (1); + + return 0; +} + +/* + * procfs support, if compiled in. + */ +#ifdef CONFIG_PROC_FS +/* + * Character device read + */ +static int maclist_getchar(off_t n) { + static char xdigit[16] = "0123456789abcdef"; + maclist_entry_t *head = maclist_list; + int b; + + do { + if (head == 0) + return -1; + if (n < 18) + break; + head = head->next; + n -= 18; + } while (1); + + if (n == 17) + return '\n'; + + b = n/3; + switch (n - b*3) { + case 0: return xdigit[head->id[b] >> 4]; + case 1: return xdigit[head->id[b] & 0xf]; + default: return ':'; + } +} + +/* + * The extensively undocumented proc_read_t callback is implemented here. + * Go look in fs/proc/generic.c: + * + * Prototype: + * int f(char *buffer, char **start, off_t offset, + * int count, int *peof, void *dat) + * + * Assume that the buffer is "count" bytes in size. + * + * 2) Set *start = an address within the buffer. + * Put the data of the requested offset at *start. + * Return the number of bytes of data placed there. + * If this number is greater than zero and you + * didn't signal eof and the reader is prepared to + * take more data you will be called again with the + * requested offset advanced by the number of bytes + * absorbed. + */ +static int maclist_proc_read(char *buffer, char **start, off_t offset, + int count, int *peof, void *dat) { + int total; + + *start = buffer; + total = 0; + + while (total < count) { + int ch = maclist_getchar(offset++); + if (ch == -1) { + *peof = 1; + break; + } + *buffer++ = ch; + ++total; + } + + return total; +} +#endif + +/* + * Finally, the init/exit functions. + */ +static void __exit maclist_exit(void) +{ + maclist_entry_t *list; + + remove_proc_entry(MACLIST_NAME, proc_net); + + list = maclist_list; + maclist_list = 0; + + while (list != 0) { + maclist_entry_t *head = list; + list = head->next; + kfree(head); + } +} + +#ifdef MODULE +static char ids[256]; +module_param_string(ids, ids, sizeof ids, 0); +MODULE_PARM_DESC(ids, "comma separated list of MAC ids\n"); +#else +__setup("maclist_ids=", maclist_setup); +#endif + +static int __init maclist_init(void) +{ +# ifdef MODULE + if (ids[0]) + maclist_setup(ids); +# endif + + /* Ignore failure, the module will still work. */ + (void)create_proc_read_entry(MACLIST_NAME, S_IRUGO, proc_net, maclist_proc_read, NULL); + + return 0; +} + +module_init(maclist_init); +module_exit(maclist_exit); --- linux-nslu2.orig/drivers/net/Makefile 2006-02-06 22:35:18.000000000 +0100 +++ linux-nslu2/drivers/net/Makefile 2006-02-06 22:35:23.000000000 +0100 @@ -74,6 +74,7 @@ obj-$(CONFIG_RIONET) += rionet.o # end link order section # +obj-$(CONFIG_MACLIST) += maclist.o obj-$(CONFIG_MII) += mii.o obj-$(CONFIG_PHYLIB) += phy/ --- linux-nslu2.orig/drivers/net/Kconfig 2006-02-06 22:35:18.000000000 +0100 +++ linux-nslu2/drivers/net/Kconfig 2006-02-06 22:35:23.000000000 +0100 @@ -180,6 +180,21 @@ config NET_ETHERNET kernel: saying N will just cause the configurator to skip all the questions about Ethernet network cards. If unsure, say N. +config MACLIST + tristate "Ethernet MAC repository" + depends on NET_ETHERNET + help + Some ethernet controllers have no built-in way of obtaining an + appropriate Ethernet MAC address. Such controllers have to be + initialised in a board-specific way, depending on how the allocated + MAC is stored. The MAC repository provides a set of APIs and a + proc entry (/proc/net/maclist) to store MAC values from the board + so that such drivers can obtain a MAC address without board-specific + code. You do not need to enable this device - it will be selected + automatically by any device which requires it. It is only useful + to enable it manually when building a device driver independently + of the kernel build. + config MII tristate "Generic Media Independent Interface device support" depends on NET_ETHERNET