| /* zd_netdev.c | |
| * | |
| * This program is free software; you can redistribute it and/or modify | |
| * it under the terms of the GNU General Public License as published by | |
| * the Free Software Foundation; either version 2 of the License, or | |
| * (at your option) any later version. | |
| * | |
| * This program is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| * GNU General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU General Public License | |
| * along with this program; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include <linux/netdevice.h> | |
| #include <linux/etherdevice.h> | |
| #include <linux/skbuff.h> | |
| #include <net/ieee80211.h> | |
| #include <net/ieee80211softmac.h> | |
| #include <net/ieee80211softmac_wx.h> | |
| #include <net/iw_handler.h> | |
| #include "zd_def.h" | |
| #include "zd_netdev.h" | |
| #include "zd_mac.h" | |
| #include "zd_ieee80211.h" | |
| /* Region 0 means reset regdomain to default. */ | |
| static int zd_set_regdomain(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| const u8 *regdomain = (u8 *)req; | |
| return zd_mac_set_regdomain(zd_netdev_mac(netdev), *regdomain); | |
| } | |
| static int zd_get_regdomain(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| u8 *regdomain = (u8 *)req; | |
| if (!regdomain) | |
| return -EINVAL; | |
| *regdomain = zd_mac_get_regdomain(zd_netdev_mac(netdev)); | |
| return 0; | |
| } | |
| static const struct iw_priv_args zd_priv_args[] = { | |
| { | |
| .cmd = ZD_PRIV_SET_REGDOMAIN, | |
| .set_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | |
| .name = "set_regdomain", | |
| }, | |
| { | |
| .cmd = ZD_PRIV_GET_REGDOMAIN, | |
| .get_args = IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, | |
| .name = "get_regdomain", | |
| }, | |
| }; | |
| #define PRIV_OFFSET(x) [(x)-SIOCIWFIRSTPRIV] | |
| static const iw_handler zd_priv_handler[] = { | |
| PRIV_OFFSET(ZD_PRIV_SET_REGDOMAIN) = zd_set_regdomain, | |
| PRIV_OFFSET(ZD_PRIV_GET_REGDOMAIN) = zd_get_regdomain, | |
| }; | |
| static int iw_get_name(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| /* FIXME: check whether 802.11a will also supported */ | |
| strlcpy(req->name, "IEEE 802.11b/g", IFNAMSIZ); | |
| return 0; | |
| } | |
| static int iw_get_nick(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| strcpy(extra, "zd1211"); | |
| req->data.length = strlen(extra); | |
| req->data.flags = 1; | |
| return 0; | |
| } | |
| static int iw_set_freq(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| int r; | |
| struct zd_mac *mac = zd_netdev_mac(netdev); | |
| struct iw_freq *freq = &req->freq; | |
| u8 channel; | |
| r = zd_find_channel(&channel, freq); | |
| if (r < 0) | |
| return r; | |
| r = zd_mac_request_channel(mac, channel); | |
| return r; | |
| } | |
| static int iw_get_freq(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| int r; | |
| struct zd_mac *mac = zd_netdev_mac(netdev); | |
| struct iw_freq *freq = &req->freq; | |
| u8 channel; | |
| u8 flags; | |
| r = zd_mac_get_channel(mac, &channel, &flags); | |
| if (r) | |
| return r; | |
| freq->flags = (flags & MAC_FIXED_CHANNEL) ? | |
| IW_FREQ_FIXED : IW_FREQ_AUTO; | |
| dev_dbg_f(zd_mac_dev(mac), "channel %s\n", | |
| (flags & MAC_FIXED_CHANNEL) ? "fixed" : "auto"); | |
| return zd_channel_to_freq(freq, channel); | |
| } | |
| static int iw_set_mode(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| return zd_mac_set_mode(zd_netdev_mac(netdev), req->mode); | |
| } | |
| static int iw_get_mode(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| return zd_mac_get_mode(zd_netdev_mac(netdev), &req->mode); | |
| } | |
| static int iw_get_range(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *req, char *extra) | |
| { | |
| struct iw_range *range = (struct iw_range *)extra; | |
| dev_dbg_f(zd_mac_dev(zd_netdev_mac(netdev)), "\n"); | |
| req->data.length = sizeof(*range); | |
| return zd_mac_get_range(zd_netdev_mac(netdev), range); | |
| } | |
| static int iw_set_encode(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *data, | |
| char *extra) | |
| { | |
| return ieee80211_wx_set_encode(zd_netdev_ieee80211(netdev), info, | |
| data, extra); | |
| } | |
| static int iw_get_encode(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *data, | |
| char *extra) | |
| { | |
| return ieee80211_wx_get_encode(zd_netdev_ieee80211(netdev), info, | |
| data, extra); | |
| } | |
| static int iw_set_encodeext(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *data, | |
| char *extra) | |
| { | |
| return ieee80211_wx_set_encodeext(zd_netdev_ieee80211(netdev), info, | |
| data, extra); | |
| } | |
| static int iw_get_encodeext(struct net_device *netdev, | |
| struct iw_request_info *info, | |
| union iwreq_data *data, | |
| char *extra) | |
| { | |
| return ieee80211_wx_get_encodeext(zd_netdev_ieee80211(netdev), info, | |
| data, extra); | |
| } | |
| #define WX(x) [(x)-SIOCIWFIRST] | |
| static const iw_handler zd_standard_iw_handlers[] = { | |
| WX(SIOCGIWNAME) = iw_get_name, | |
| WX(SIOCGIWNICKN) = iw_get_nick, | |
| WX(SIOCSIWFREQ) = iw_set_freq, | |
| WX(SIOCGIWFREQ) = iw_get_freq, | |
| WX(SIOCSIWMODE) = iw_set_mode, | |
| WX(SIOCGIWMODE) = iw_get_mode, | |
| WX(SIOCGIWRANGE) = iw_get_range, | |
| WX(SIOCSIWENCODE) = iw_set_encode, | |
| WX(SIOCGIWENCODE) = iw_get_encode, | |
| WX(SIOCSIWENCODEEXT) = iw_set_encodeext, | |
| WX(SIOCGIWENCODEEXT) = iw_get_encodeext, | |
| WX(SIOCSIWAUTH) = ieee80211_wx_set_auth, | |
| WX(SIOCGIWAUTH) = ieee80211_wx_get_auth, | |
| WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan, | |
| WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results, | |
| WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid, | |
| WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid, | |
| WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap, | |
| WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap, | |
| WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate, | |
| WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate, | |
| WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie, | |
| WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie, | |
| WX(SIOCSIWMLME) = ieee80211softmac_wx_set_mlme, | |
| }; | |
| static const struct iw_handler_def iw_handler_def = { | |
| .standard = zd_standard_iw_handlers, | |
| .num_standard = ARRAY_SIZE(zd_standard_iw_handlers), | |
| .private = zd_priv_handler, | |
| .num_private = ARRAY_SIZE(zd_priv_handler), | |
| .private_args = zd_priv_args, | |
| .num_private_args = ARRAY_SIZE(zd_priv_args), | |
| .get_wireless_stats = zd_mac_get_wireless_stats, | |
| }; | |
| struct net_device *zd_netdev_alloc(struct usb_interface *intf) | |
| { | |
| int r; | |
| struct net_device *netdev; | |
| struct zd_mac *mac; | |
| netdev = alloc_ieee80211softmac(sizeof(struct zd_mac)); | |
| if (!netdev) { | |
| dev_dbg_f(&intf->dev, "out of memory\n"); | |
| return NULL; | |
| } | |
| mac = zd_netdev_mac(netdev); | |
| r = zd_mac_init(mac, netdev, intf); | |
| if (r) { | |
| usb_set_intfdata(intf, NULL); | |
| free_ieee80211(netdev); | |
| return NULL; | |
| } | |
| SET_MODULE_OWNER(netdev); | |
| SET_NETDEV_DEV(netdev, &intf->dev); | |
| dev_dbg_f(&intf->dev, "netdev->flags %#06hx\n", netdev->flags); | |
| dev_dbg_f(&intf->dev, "netdev->features %#010lx\n", netdev->features); | |
| netdev->open = zd_mac_open; | |
| netdev->stop = zd_mac_stop; | |
| /* netdev->get_stats = */ | |
| /* netdev->set_multicast_list = */ | |
| netdev->set_mac_address = zd_mac_set_mac_address; | |
| netdev->wireless_handlers = &iw_handler_def; | |
| /* netdev->ethtool_ops = */ | |
| return netdev; | |
| } | |
| void zd_netdev_free(struct net_device *netdev) | |
| { | |
| if (!netdev) | |
| return; | |
| zd_mac_clear(zd_netdev_mac(netdev)); | |
| free_ieee80211(netdev); | |
| } | |
| void zd_netdev_disconnect(struct net_device *netdev) | |
| { | |
| unregister_netdev(netdev); | |
| } |