--- eepro100-napi.c Wed Jun 12 17:11:38 2002 +++ eepro100-napi-proc.c Wed Jun 12 17:33:51 2002 @@ -119,6 +119,10 @@ #define CONFIG_EEPRO100_NAPI +#ifdef CONFIG_PROC_FS +#include +#endif + MODULE_AUTHOR("Maintainer: Andrey V. Savochkin "); MODULE_DESCRIPTION("Intel i82557/i82558/i82559 PCI EtherExpressPro driver"); MODULE_LICENSE("GPL"); @@ -516,6 +520,10 @@ unsigned long alloc_fail; unsigned long long poll_cycles; +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *proc_parent; +#endif + #ifdef CONFIG_NET_FASTROUTE unsigned long fastroute_hit; unsigned long fastroute_success; @@ -582,6 +590,11 @@ static void enable_rx_and_rxnobuf_ints(struct net_device *dev); static void disable_rx_and_rxnobuf_ints(struct net_device *dev); +#ifdef CONFIG_PROC_FS +int __devinit speedo_create_proc_subdir(struct net_device *sp); +void speedo_remove_proc_subdir(struct net_device *sp); +#endif + #endif @@ -883,6 +896,14 @@ #ifdef CONFIG_EEPRO100_NAPI dev->poll = speedo_poll; dev->quota = dev->weight = RX_RING_SIZE; + +#ifdef CONFIG_PROC_FS + if (speedo_create_proc_subdir(dev) < 0) { + printk(KERN_ERR "Failed to create proc directory for %s\n", + dev->name); + } +#endif + #endif return 0; } @@ -1885,6 +1906,354 @@ return 1; /* not_done */ } +#ifdef CONFIG_PROC_FS +/* adapted from intel's e100 code */ +static struct proc_dir_entry *adapters_proc_dir = 0; + +static void speedo_proc_cleanup(void); +static unsigned char speedo_init_proc_dir(void); + +#define ADAPTERS_PROC_DIR "eepro100" +#define WRITE_BUF_MAX_LEN 20 +#define READ_BUF_MAX_LEN 256 +#define SPEEDO_PE_LEN 25 + +#define sp_off(off) (unsigned long)(offsetof(struct speedo_private, off)) + +typedef struct _speedo_proc_entry { + char *name; + read_proc_t *read_proc; + write_proc_t *write_proc; + unsigned long offset; /* offset into sp. ~0 means no value, pass NULL. */ +} speedo_proc_entry; + +static int +generic_read(char *page, char **start, off_t off, int count, int *eof, int len) +{ + if (len <= off + count) + *eof = 1; + + *start = page + off; + len -= off; + if (len > count) + len = count; + + if (len < 0) + len = 0; + + return len; +} + +static int +read_ulong(char *page, char **start, off_t off, + int count, int *eof, unsigned long l) +{ + int len; + + len = sprintf(page, "%lu\n", l); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_gen_ulong(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long val = 0; + + if (data) + val = *((unsigned long *) data); + + return read_ulong(page, start, off, count, eof, val); +} + +static int +read_ulonglong(char *page, char **start, off_t off, + int count, int *eof, unsigned long long ll) +{ + int len; + + len = sprintf(page, "%llu\n", ll); + + return generic_read(page, start, off, count, eof, len); +} + +static int +read_gen_ulonglong(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + unsigned long val = 0; + + if (data) + val = *((unsigned long long *) data); + + return read_ulonglong(page, start, off, count, eof, val); +} + +static int +set_debug(struct file *file, const char *buffer, + unsigned long count, void *data) + +{ + if (speedo_debug == 1) + speedo_debug = 6; + else + speedo_debug = 1; + return count; +} + +static int +_speedo_show_state(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + + struct net_device *dev = (struct net_device *)data; + + speedo_show_state(dev); + + return count; +} + +static speedo_proc_entry speedo_proc_list[] = { + {"set_debug", 0, set_debug, ~0}, + {"show_state", 0, _speedo_show_state, ~0}, + {"poll_switch",read_gen_ulong,0,sp_off(poll_switch)}, + {"failed_poll_switch",read_gen_ulong,0,sp_off(failed_poll_switch)}, + {"done_poll",read_gen_ulong,0,sp_off(done_poll)}, + {"notdone_poll",read_gen_ulong,0,sp_off(notdone_poll)}, + {"empty_poll",read_gen_ulong,0,sp_off(empty_poll)}, + {"soft_reset_count",read_gen_ulong,0,sp_off(soft_reset_count)}, + {"rx_resume_count",read_gen_ulong,0,sp_off(rx_resume_count)}, + {"alloc_fail",read_gen_ulong,0,sp_off(alloc_fail)}, + {"poll_cycles",read_gen_ulonglong,0,sp_off(poll_cycles)}, + {"fastroute_hit",read_gen_ulonglong,0,sp_off(fastroute_hit)}, + {"fastroute_success",read_gen_ulonglong,0,sp_off(fastroute_success)}, + {"fastroute_defer",read_gen_ulonglong,0,sp_off(fastroute_defer)}, + {"", 0, 0, 0} +}; + +static int +read_info(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + struct speedo_private *sp = data; + speedo_proc_entry *pe; + int tmp; + void *val; + int len = 0; + + for (pe = speedo_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') { + len += sprintf(page + len, "\n"); + continue; + } + + if (pe->read_proc) { + if ((len + READ_BUF_MAX_LEN + SPEEDO_PE_LEN + 1) >= + PAGE_SIZE) + break; + + if (pe->offset != ~0) + val = ((char *) sp) + pe->offset; + else + val = NULL; + + len += sprintf(page + len, "%-" + __MODULE_STRING(SPEEDO_PE_LEN) + "s ", pe->name); + len += pe->read_proc(page + len, start, 0, + READ_BUF_MAX_LEN + 1, &tmp, val); + } + } + + return generic_read(page, start, off, count, eof, len); +} + +static struct proc_dir_entry * __devinit +create_proc_rw(char *name, void *data, struct proc_dir_entry *parent, + read_proc_t * read_proc, write_proc_t * write_proc) +{ + struct proc_dir_entry *pdep; + mode_t mode = S_IFREG; + + if (write_proc) { + mode |= S_IWUSR; + if (read_proc) { + mode |= S_IRUSR; + } + + } else if (read_proc) { + mode |= S_IRUGO; + } + + if (!(pdep = create_proc_entry(name, mode, parent))) + return NULL; + + pdep->read_proc = read_proc; + pdep->write_proc = write_proc; + pdep->data = data; + return pdep; +} + +void +speedo_remove_proc_subdir(struct net_device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + speedo_proc_entry *pe; + char info[256]; + int len; + + /* If our root /proc dir was not created, there is nothing to remove */ + if (adapters_proc_dir == NULL) { + return; + } + + len = strlen(dev->name); + strncpy(info, dev->name, sizeof (info)); + strncat(info + len, ".info", sizeof (info) - len); + + if (sp->proc_parent) { + for (pe = speedo_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + remove_proc_entry(pe->name, sp->proc_parent); + } + + remove_proc_entry(dev->name, adapters_proc_dir); + sp->proc_parent = NULL; + } + + remove_proc_entry(info, adapters_proc_dir); + + /* try to remove the main /proc dir, if it's empty */ + speedo_proc_cleanup(); +} + +int __devinit +speedo_create_proc_subdir(struct net_device *dev) +{ + struct speedo_private *sp = (struct speedo_private *)dev->priv; + struct proc_dir_entry *dev_dir; + speedo_proc_entry *pe; + char info[256]; + int len; + void *data; + + /* create the main /proc dir if needed */ + if (!adapters_proc_dir) { + if (!speedo_init_proc_dir()) + return -ENOMEM; + } + + strncpy(info, dev->name, sizeof (info)); + len = strlen(info); + strncat(info + len, ".info", sizeof (info) - len); + + /* info */ + if (!(create_proc_rw(info, sp, adapters_proc_dir, read_info, 0))) { + speedo_proc_cleanup(); + return -ENOMEM; + } + + dev_dir = create_proc_entry(dev->name, S_IFDIR, + adapters_proc_dir); + sp->proc_parent = dev_dir; + + if (!dev_dir) { + speedo_remove_proc_subdir(dev); + return -ENOMEM; + } + + for (pe = speedo_proc_list; pe->name[0]; pe++) { + if (pe->name[0] == '\n') + continue; + + if (pe->offset != ~0) + data = ((char *) sp) + pe->offset; + else + data = dev; + + if (!(create_proc_rw(pe->name, data, dev_dir, + pe->read_proc, pe->write_proc))) { + speedo_remove_proc_subdir(dev); + return -ENOMEM; + } + } + + return 0; +} + +/**************************************************************************** + * Name: speedo_init_proc_dir + * + * Description: This routine creates the top-level /proc directory for the + * driver in /proc/net + * + * Arguments: none + * + * Returns: true on success, false on fail + * + ***************************************************************************/ +static unsigned char +speedo_init_proc_dir(void) +{ + int len; + + /* first check if adapters_proc_dir already exists */ + len = strlen(ADAPTERS_PROC_DIR); + for (adapters_proc_dir = proc_net->subdir; + adapters_proc_dir; adapters_proc_dir = adapters_proc_dir->next) { + + if ((adapters_proc_dir->namelen == len) && + (!memcmp(adapters_proc_dir->name, ADAPTERS_PROC_DIR, len))) + break; + } + + if (!adapters_proc_dir) + adapters_proc_dir = + create_proc_entry(ADAPTERS_PROC_DIR, S_IFDIR, proc_net); + + if (!adapters_proc_dir) + return 0; + + return 1; +} + +/**************************************************************************** + * Name: speedo_proc_cleanup + * + * Description: This routine clears the top-level /proc directory, if empty. + * + * Arguments: none + * + * Returns: none + * + ***************************************************************************/ +static void +speedo_proc_cleanup(void) +{ + struct proc_dir_entry *de; + + if (adapters_proc_dir == NULL) { + return; + } + + /* check if subdir list is empty before removing adapters_proc_dir */ + for (de = adapters_proc_dir->subdir; de; de = de->next) { + /* ignore . and .. */ + if (*(de->name) != '.') + break; + } + + if (de) + return; + + remove_proc_entry(ADAPTERS_PROC_DIR, proc_net); + adapters_proc_dir = NULL; +} + +#endif /* CONFIG_PROC_FS */ + #endif /* NAPI */ static int @@ -2474,6 +2843,9 @@ unregister_netdev(dev); +#if defined(CONFIG_EEPRO100_NAPI) && defined(CONFIG_PROC_FS) + speedo_remove_proc_subdir(dev); +#endif release_region(pci_resource_start(pdev, 1), pci_resource_len(pdev, 1)); release_mem_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));