266 строки
7.4 KiB
C
266 строки
7.4 KiB
C
/* 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)
|
|
{
|
|
struct zd_mac *mac = zd_netdev_mac(netdev);
|
|
struct iw_freq *freq = &req->freq;
|
|
|
|
return zd_channel_to_freq(freq, zd_mac_get_channel(mac));
|
|
}
|
|
|
|
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 = zd_mac_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);
|
|
}
|