Support for adding and removing interfaces through sysfs. A new class "ieee80211" is created containing physical devices as subdirectories (named phy%d). Only sta (managed) interfaces can be added for now. To add an interface, invoke: echo -n sta0 > /sys/class/phy0/add_iface To remove interface, invoke: echo -n sta0 > /sys/class/phy0/remove_iface The old ioctl interface is still present. Signed-off-by: Jiri Benc Index: netdev/include/net/ieee80211.h =================================================================== --- netdev.orig/include/net/ieee80211.h 2006-01-16 18:49:54.000000000 +0100 +++ netdev/include/net/ieee80211.h 2006-01-16 19:04:40.000000000 +0100 @@ -10,6 +10,7 @@ #ifndef IEEE80211_H #define IEEE80211_H +#include #include "ieee80211_shared.h" /* Note! Only ieee80211_tx_status_irqsafe() and ieee80211_rx_irqsave() can be Index: netdev/net/ieee80211/ieee80211_dev.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ netdev/net/ieee80211/ieee80211_dev.c 2006-01-16 19:04:40.000000000 +0100 @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2006 Jiri Benc + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include "ieee80211_i.h" + +struct ieee80211_dev_list { + struct list_head list; + int dev_index; + struct ieee80211_local *local; +}; + +static LIST_HEAD(dev_list); +static DEFINE_SPINLOCK(dev_list_lock); + + +/* Caller must hold dev_list_lock */ +static struct ieee80211_dev_list *__ieee80211_dev_find(int index) +{ + struct ieee80211_dev_list *dev_item; + + list_for_each_entry(dev_item, &dev_list, list) { + if (dev_item->dev_index == index) + return dev_item; + } + return NULL; +} + +int ieee80211_dev_alloc_index(struct ieee80211_local *local) +{ + struct list_head *i; + struct ieee80211_dev_list *dev_item, *new; + int index = 0; + + new = kmalloc(sizeof(struct ieee80211_dev_list), GFP_KERNEL); + if (!new) + return -ENOMEM; + new->local = local; + spin_lock(&dev_list_lock); + list_for_each(i, &dev_list) { + dev_item = list_entry(i, struct ieee80211_dev_list, list); + if (index < dev_item->dev_index) + break; + index++; + } + new->dev_index = index; + list_add_tail(&new->list, i); + spin_unlock(&dev_list_lock); + local->dev_index = index; + return index; +} + +void ieee80211_dev_free_index(struct ieee80211_local *local) +{ + struct ieee80211_dev_list *dev_item; + + spin_lock(&dev_list_lock); + dev_item = __ieee80211_dev_find(local->dev_index); + if (dev_item) + list_del(&dev_item->list); + spin_unlock(&dev_list_lock); + if (dev_item) + kfree(dev_item); + local->dev_index = -1; +} + +struct ieee80211_local *ieee80211_dev_find(int index) +{ + struct ieee80211_dev_list *dev_item; + + spin_lock(&dev_list_lock); + dev_item = __ieee80211_dev_find(index); + spin_unlock(&dev_list_lock); + return dev_item ? dev_item->local : NULL; +} Index: netdev/net/ieee80211/ieee80211_i.h =================================================================== --- netdev.orig/net/ieee80211/ieee80211_i.h 2006-01-16 19:02:58.000000000 +0100 +++ netdev/net/ieee80211/ieee80211_i.h 2006-01-16 19:04:40.000000000 +0100 @@ -302,6 +302,9 @@ struct ieee80211_local { int open_count; struct ieee80211_conf conf; + int dev_index; + struct class_device class_dev; + /* Tasklet and skb queue to process calls from IRQ mode. All frames * added to skb_queue will be processed, but frames in * skb_queue_unreliable may be dropped if the total length of these @@ -496,16 +499,16 @@ void ieee80211_rx_mgmt(struct net_device void ieee80211_prepare_rates(struct net_device *dev); void ieee80211_tx_set_iswep(struct ieee80211_txrx_data *tx); int ieee80211_if_add_wds(struct net_device *dev, - char *name, + const char *name, struct ieee80211_if_wds *wds, int locked); -int ieee80211_if_add_vlan(struct net_device *dev, char *name, +int ieee80211_if_add_vlan(struct net_device *dev, const char *name, struct ieee80211_if_vlan *vlan, int locked); -int ieee80211_if_add_ap(struct net_device *dev, char *name, u8 *bssid, +int ieee80211_if_add_ap(struct net_device *dev, const char *name, u8 *bssid, int locked); -int ieee80211_if_remove_wds(struct net_device *dev, char *name, int locked); -int ieee80211_if_remove_vlan(struct net_device *dev, char *name, int locked); -int ieee80211_if_remove_ap(struct net_device *dev, char *name, int locked); +int ieee80211_if_remove_wds(struct net_device *dev, const char *name, int locked); +int ieee80211_if_remove_vlan(struct net_device *dev, const char *name, int locked); +int ieee80211_if_remove_ap(struct net_device *dev, const char *name, int locked); int ieee80211_if_flush(struct net_device *dev, int locked); int ieee80211_if_update_wds(struct net_device *dev, char *name, struct ieee80211_if_wds *wds, int locked); @@ -544,8 +547,8 @@ void ieee80211_stop_scan(struct net_devi /* ieee80211.c */ -int ieee80211_if_add_sta(struct net_device *dev, char *name, int locked); -int ieee80211_if_remove_sta(struct net_device *dev, char *name, int locked); +int ieee80211_if_add_sta(struct net_device *dev, const char *name, int locked); +int ieee80211_if_remove_sta(struct net_device *dev, const char *name, int locked); /* ieee80211_ioctl.c */ int ieee80211_set_compression(struct ieee80211_local *local, struct net_device *dev, struct sta_info *sta); @@ -569,5 +572,15 @@ struct sta_info * ieee80211_ibss_add_sta int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason); int ieee80211_sta_disassociate(struct net_device *dev, u16 reason); +/* ieee80211_dev.c */ +int ieee80211_dev_alloc_index(struct ieee80211_local *local); +void ieee80211_dev_free_index(struct ieee80211_local *local); +struct ieee80211_local *ieee80211_dev_find(int index); + +/* ieee80211_sysfs.c */ +int ieee80211_register_sysfs(struct ieee80211_local *local); +void ieee80211_unregister_sysfs(struct ieee80211_local *local); +int ieee80211_sysfs_init(void); +void ieee80211_sysfs_deinit(void); #endif /* IEEE80211_I_H */ Index: netdev/net/ieee80211/ieee80211_sysfs.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ netdev/net/ieee80211/ieee80211_sysfs.c 2006-01-16 19:19:04.000000000 +0100 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2006 Jiri Benc + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "ieee80211_i.h" + +#define to_ieee80211_local(class) container_of(class, struct ieee80211_local, class_dev) + + +static ssize_t store_add_iface(struct class_device *dev, + const char *buf, size_t len) +{ + struct ieee80211_local *local = to_ieee80211_local(dev); + int res; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (len > IFNAMSIZ) + return -EINVAL; + /* Cannot call ieee80211_if_add_sta() with 'locked' parameter equal + * to zero as it would lead to call to register_netdev() and + * interpreting '%d' character in an interface name. */ + rtnl_lock(); + res = ieee80211_if_add_sta(local->mdev, buf, 1); + rtnl_unlock(); + return res < 0 ? res : len; +} + +static ssize_t store_remove_iface(struct class_device *dev, + const char *buf, size_t len) +{ + struct ieee80211_local *local = to_ieee80211_local(dev); + int res; + + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + if (len > IFNAMSIZ) + return -EINVAL; + res = ieee80211_if_remove_sta(local->mdev, buf, 0); + return res < 0 ? res : len; +} + +#ifdef CONFIG_HOTPLUG +static int ieee80211_uevent(struct class_device *cd, char **envp, + int num_envp, char *buf, int size) +{ + struct ieee80211_local *local = to_ieee80211_local(cd); + + if (num_envp < 2) + return -ENOMEM; + envp[0] = buf; + if (snprintf(buf, size, "IEEE80211_DEV=phy%d", + local->dev_index) + 1 >= size) + return -ENOMEM; + envp[1] = NULL; + return 0; +} +#endif + +static struct class_device_attribute ieee80211_class_dev_attrs[] = { + __ATTR(add_iface, S_IWUSR, NULL, store_add_iface), + __ATTR(remove_iface, S_IWUSR, NULL, store_remove_iface), +}; + +static struct class ieee80211_class = { + .name = "ieee80211", + .class_dev_attrs = ieee80211_class_dev_attrs, +#ifdef CONFIG_HOTPLUG + .uevent = ieee80211_uevent, +#endif +}; + +int ieee80211_register_sysfs(struct ieee80211_local *local) +{ + local->class_dev.class = &ieee80211_class; + local->class_dev.class_data = local; + snprintf(local->class_dev.class_id, BUS_ID_SIZE, + "phy%d", local->dev_index); + return class_device_register(&local->class_dev); +} + +void ieee80211_unregister_sysfs(struct ieee80211_local *local) +{ + class_device_del(&local->class_dev); +} + +int ieee80211_sysfs_init(void) +{ + return class_register(&ieee80211_class); +} + +void ieee80211_sysfs_deinit(void) +{ + class_unregister(&ieee80211_class); +} Index: netdev/net/ieee80211/Makefile =================================================================== --- netdev.orig/net/ieee80211/Makefile 2006-01-16 18:49:54.000000000 +0100 +++ netdev/net/ieee80211/Makefile 2006-01-16 19:04:40.000000000 +0100 @@ -9,6 +9,8 @@ obj-$(CONFIG_IEEE80211) += 80211.o rate_ ieee80211_proc.o \ ieee80211_scan.o \ ieee80211_sta.o \ + ieee80211_dev.o \ + ieee80211_sysfs.o \ michael.o \ tkip.o \ aes_ccm.o \ Index: netdev/net/ieee80211/ieee80211.c =================================================================== --- netdev.orig/net/ieee80211/ieee80211.c 2006-01-16 19:04:40.000000000 +0100 +++ netdev/net/ieee80211/ieee80211.c 2006-01-16 19:04:40.000000000 +0100 @@ -3839,7 +3839,7 @@ static void ieee80211_if_sdata_init(stru static struct net_device *ieee80211_if_add(struct net_device *dev, - char *name, int locked) + const char *name, int locked) { struct net_device *wds_dev = NULL, *tmp_dev; struct ieee80211_local *local = dev->priv; @@ -3912,13 +3912,11 @@ static struct net_device *ieee80211_if_a list_add(&sdata->list, &local->sub_if_list); - strcpy(name, wds_dev->name); - return wds_dev; } -int ieee80211_if_add_wds(struct net_device *dev, char *name, +int ieee80211_if_add_wds(struct net_device *dev, const char *name, struct ieee80211_if_wds *wds, int locked) { struct net_device *wds_dev = NULL; @@ -4004,7 +4002,7 @@ static void ieee80211_if_init(struct net int ieee80211_if_add_vlan(struct net_device *dev, - char *name, + const char *name, struct ieee80211_if_vlan *vlan, int locked) { @@ -4041,7 +4039,7 @@ static void ieee80211_if_ap_init(struct } -int ieee80211_if_add_ap(struct net_device *dev, char *name, u8 *bssid, +int ieee80211_if_add_ap(struct net_device *dev, const char *name, u8 *bssid, int locked) { struct ieee80211_local *local = dev->priv; @@ -4088,7 +4086,7 @@ static void ieee80211_addr_inc(u8 *addr) } -int ieee80211_if_add_sta(struct net_device *dev, char *name, int locked) +int ieee80211_if_add_sta(struct net_device *dev, const char *name, int locked) { struct ieee80211_local *local = dev->priv; struct net_device *sta_dev; @@ -4273,8 +4271,8 @@ static void ieee80211_if_del(struct ieee } -static int ieee80211_if_remove(struct net_device *dev, char *name, int id, - int locked) +static int ieee80211_if_remove(struct net_device *dev, const char *name, + int id, int locked) { struct ieee80211_local *local = dev->priv; struct list_head *ptr, *n; @@ -4296,28 +4294,28 @@ static int ieee80211_if_remove(struct ne } -int ieee80211_if_remove_wds(struct net_device *dev, char *name, int locked) +int ieee80211_if_remove_wds(struct net_device *dev, const char *name, int locked) { return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_WDS, locked); } -int ieee80211_if_remove_vlan(struct net_device *dev, char *name, int locked) +int ieee80211_if_remove_vlan(struct net_device *dev, const char *name, int locked) { return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_VLAN, locked); } -int ieee80211_if_remove_ap(struct net_device *dev, char *name, int locked) +int ieee80211_if_remove_ap(struct net_device *dev, const char *name, int locked) { return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_AP, locked); } -int ieee80211_if_remove_sta(struct net_device *dev, char *name, int locked) +int ieee80211_if_remove_sta(struct net_device *dev, const char *name, int locked) { return ieee80211_if_remove(dev, name, IEEE80211_SUB_IF_TYPE_STA, locked); @@ -4432,6 +4430,7 @@ struct net_device *ieee80211_alloc_hw(si dev->tx_queue_len = 0; dev->set_mac_address = ieee80211_set_mac_address; + local->dev_index = -1; local->wdev = dev; local->mdev = mdev; local->rx_handlers = ieee80211_rx_handlers; @@ -4559,6 +4558,15 @@ int ieee80211_register_hw(struct net_dev return -1; } + result = ieee80211_dev_alloc_index(local); + if (result < 0) + return -1; + + local->class_dev.dev = dev->class_dev.dev; + result = ieee80211_register_sysfs(local); + if (result < 0) + goto fail_sysfs; + local->conf.mode = IW_MODE_MASTER; local->conf.beacon_int = 1000; @@ -4568,7 +4576,7 @@ int ieee80211_register_hw(struct net_dev result = register_netdev(local->wdev); if (result < 0) - return -1; + goto fail_1st_dev; result = register_netdev(local->apdev); if (result < 0) @@ -4599,7 +4607,11 @@ fail_3rd_dev: unregister_netdev(local->apdev); fail_2nd_dev: unregister_netdev(local->wdev); +fail_1st_dev: sta_info_stop(local); + ieee80211_unregister_sysfs(local); +fail_sysfs: + ieee80211_dev_free_index(local); return result; } @@ -4668,6 +4680,7 @@ void ieee80211_unregister_hw(struct net_ } sta_info_stop(local); + ieee80211_unregister_sysfs(local); for (i = 0; i < IEEE80211_FRAGMENT_MAX; i++) if (local->fragments[i].skb) @@ -4690,6 +4703,7 @@ void ieee80211_unregister_hw(struct net_ skb_queue_purge(&local->skb_queue_unreliable); rate_control_free(local); + ieee80211_dev_free_index(local); } void ieee80211_free_hw(struct net_device *dev) @@ -4822,6 +4836,8 @@ static int rate_control_initialize(struc static int __init ieee80211_init(void) { struct sk_buff *skb; + int ret; + if (sizeof(struct ieee80211_tx_packet_data) > (sizeof(skb->cb))) { printk("80211: ieee80211_tx_packet_data is bigger " "than the skb->cb (%d > %d)\n", @@ -4829,10 +4845,14 @@ static int __init ieee80211_init(void) (int) sizeof(skb->cb)); return -EINVAL; } + if ((ret = ieee80211_sysfs_init())) { + printk(KERN_WARNING "ieee80211_init: sysfs initialization failed\n"); + return ret; + } ieee80211_proc_init(); { - int ret = ieee80211_wme_register(); + ret = ieee80211_wme_register(); if (ret) { printk(KERN_DEBUG "ieee80211_init: failed to " "initialize WME (err=%d)\n", ret); @@ -4849,6 +4869,7 @@ static void __exit ieee80211_exit(void) { ieee80211_wme_unregister(); ieee80211_proc_deinit(); + ieee80211_sysfs_deinit(); }