/* 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 #include #include #include #include #include #include #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, add also * zd1211B, if we support it. */ strlcpy(req->name, "802.11g zd1211", IFNAMSIZ); 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(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); }