diff options
Diffstat (limited to 'drivers/net/wireless/zd1211rw/zd_netdev.c')
-rw-r--r-- | drivers/net/wireless/zd1211rw/zd_netdev.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c new file mode 100644 index 00000000000..af3a7b36d07 --- /dev/null +++ b/drivers/net/wireless/zd1211rw/zd_netdev.c @@ -0,0 +1,276 @@ +/* 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); +} |