rndis_wlan: convert scan to cfg80211

Convert scan function to cfg80211. Unlike old scan code new code
waits 1 sec before getting scan data from device and passing
forward to cfg80211.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna@mbnet.fi>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
Jussi Kivilinna 2009-03-26 23:40:31 +02:00 committed by John W. Linville
parent 4d2a369ec0
commit 71a7b26d3e
1 changed files with 147 additions and 194 deletions

View File

@ -356,8 +356,11 @@ struct rndis_wext_private {
struct wireless_dev wdev;
struct cfg80211_scan_request *scan_request;
struct workqueue_struct *workqueue;
struct delayed_work stats_work;
struct delayed_work scan_work;
struct work_struct work;
struct mutex command_lock;
spinlock_t stats_lock;
@ -413,8 +416,12 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex,
enum nl80211_iftype type, u32 *flags,
struct vif_params *params);
static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request);
struct cfg80211_ops rndis_config_ops = {
.change_virtual_intf = rndis_change_virtual_intf,
.scan = rndis_scan,
};
void *rndis_wiphy_privid = &rndis_wiphy_privid;
@ -1164,6 +1171,142 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex,
return set_infra_mode(usbdev, mode);
}
#define SCAN_DELAY_JIFFIES (HZ)
static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_scan_request *request)
{
struct usbnet *usbdev = netdev_priv(dev);
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
int ret;
__le32 tmp;
devdbg(usbdev, "cfg80211.scan");
if (!request)
return -EINVAL;
if (priv->scan_request && priv->scan_request != request)
return -EBUSY;
priv->scan_request = request;
tmp = cpu_to_le32(1);
ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
sizeof(tmp));
if (ret == 0) {
/* Wait before retrieving scan results from device */
queue_delayed_work(priv->workqueue, &priv->scan_work,
SCAN_DELAY_JIFFIES);
}
return ret;
}
static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
struct ndis_80211_bssid_ex *bssid)
{
struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
struct ieee80211_channel *channel;
s32 signal;
u64 timestamp;
u16 capability;
u16 beacon_interval;
struct ndis_80211_fixed_ies *fixed;
int ie_len, bssid_len;
u8 *ie;
/* parse bssid structure */
bssid_len = le32_to_cpu(bssid->length);
if (bssid_len < sizeof(struct ndis_80211_bssid_ex) +
sizeof(struct ndis_80211_fixed_ies))
return NULL;
fixed = (struct ndis_80211_fixed_ies *)bssid->ies;
ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies));
ie_len = min(bssid_len - (int)sizeof(*bssid),
(int)le32_to_cpu(bssid->ie_length));
ie_len -= sizeof(struct ndis_80211_fixed_ies);
if (ie_len < 0)
return NULL;
/* extract data for cfg80211_inform_bss */
channel = ieee80211_get_channel(priv->wdev.wiphy,
KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config)));
if (!channel)
return NULL;
signal = level_to_qual(le32_to_cpu(bssid->rssi));
timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp);
capability = le16_to_cpu(fixed->capabilities);
beacon_interval = le16_to_cpu(fixed->beacon_interval);
return cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac,
timestamp, capability, beacon_interval, ie, ie_len, signal,
GFP_KERNEL);
}
static int rndis_check_bssid_list(struct usbnet *usbdev)
{
void *buf = NULL;
struct ndis_80211_bssid_list_ex *bssid_list;
struct ndis_80211_bssid_ex *bssid;
int ret = -EINVAL, len, count, bssid_len;
devdbg(usbdev, "check_bssid_list");
len = CONTROL_BUFFER_SIZE;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out;
}
ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
if (ret != 0)
goto out;
bssid_list = buf;
bssid = bssid_list->bssid;
bssid_len = le32_to_cpu(bssid->length);
count = le32_to_cpu(bssid_list->num_items);
devdbg(usbdev, "check_bssid_list: %d BSSIDs found", count);
while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
rndis_bss_info_update(usbdev, bssid);
bssid = (void *)bssid + bssid_len;
bssid_len = le32_to_cpu(bssid->length);
count--;
}
out:
kfree(buf);
return ret;
}
static void rndis_get_scan_results(struct work_struct *work)
{
struct rndis_wext_private *priv =
container_of(work, struct rndis_wext_private, scan_work.work);
struct usbnet *usbdev = priv->usbdev;
int ret;
devdbg(usbdev, "get_scan_results");
ret = rndis_check_bssid_list(usbdev);
cfg80211_scan_done(priv->scan_request, ret < 0);
priv->scan_request = NULL;
}
/*
* wireless extension handlers
*/
@ -1531,198 +1674,6 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
}
static int rndis_iw_set_scan(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct usbnet *usbdev = netdev_priv(dev);
union iwreq_data evt;
int ret = -EINVAL;
__le32 tmp;
devdbg(usbdev, "SIOCSIWSCAN");
if (wrqu->data.flags == 0) {
tmp = cpu_to_le32(1);
ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
sizeof(tmp));
evt.data.flags = 0;
evt.data.length = 0;
wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
}
return ret;
}
static char *rndis_translate_scan(struct net_device *dev,
struct iw_request_info *info, char *cev,
char *end_buf,
struct ndis_80211_bssid_ex *bssid)
{
struct usbnet *usbdev = netdev_priv(dev);
u8 *ie;
char *current_val;
int bssid_len, ie_len, i;
u32 beacon, atim;
struct iw_event iwe;
unsigned char sbuf[32];
bssid_len = le32_to_cpu(bssid->length);
devdbg(usbdev, "BSSID %pM", bssid->mac);
iwe.cmd = SIOCGIWAP;
iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN);
cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN);
devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length),
bssid->ssid.essid);
iwe.cmd = SIOCGIWESSID;
iwe.u.essid.length = le32_to_cpu(bssid->ssid.length);
iwe.u.essid.flags = 1;
cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid);
devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra));
iwe.cmd = SIOCGIWMODE;
switch (le32_to_cpu(bssid->net_infra)) {
case ndis_80211_infra_adhoc:
iwe.u.mode = IW_MODE_ADHOC;
break;
case ndis_80211_infra_infra:
iwe.u.mode = IW_MODE_INFRA;
break;
/*case ndis_80211_infra_auto_unknown:*/
default:
iwe.u.mode = IW_MODE_AUTO;
break;
}
cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN);
devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config));
iwe.cmd = SIOCGIWFREQ;
dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq);
cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN);
devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi));
iwe.cmd = IWEVQUAL;
iwe.u.qual.qual = level_to_qual(le32_to_cpu(bssid->rssi));
iwe.u.qual.level = level_to_qual(le32_to_cpu(bssid->rssi));
iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
| IW_QUAL_LEVEL_UPDATED
| IW_QUAL_NOISE_INVALID;
cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN);
devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy));
iwe.cmd = SIOCGIWENCODE;
iwe.u.data.length = 0;
if (le32_to_cpu(bssid->privacy) == ndis_80211_priv_accept_all)
iwe.u.data.flags = IW_ENCODE_DISABLED;
else
iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
devdbg(usbdev, "RATES:");
current_val = cev + iwe_stream_lcp_len(info);
iwe.cmd = SIOCGIWRATE;
for (i = 0; i < sizeof(bssid->rates); i++) {
if (bssid->rates[i] & 0x7f) {
iwe.u.bitrate.value =
((bssid->rates[i] & 0x7f) *
500000);
devdbg(usbdev, " %d", iwe.u.bitrate.value);
current_val = iwe_stream_add_value(info, cev,
current_val, end_buf, &iwe,
IW_EV_PARAM_LEN);
}
}
if ((current_val - cev) > iwe_stream_lcp_len(info))
cev = current_val;
beacon = le32_to_cpu(bssid->config.beacon_period);
devdbg(usbdev, "BCN_INT %d", beacon);
iwe.cmd = IWEVCUSTOM;
snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
iwe.u.data.length = strlen(sbuf);
cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
atim = le32_to_cpu(bssid->config.atim_window);
devdbg(usbdev, "ATIM %d", atim);
iwe.cmd = IWEVCUSTOM;
snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
iwe.u.data.length = strlen(sbuf);
cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies));
ie_len = min(bssid_len - (int)sizeof(*bssid),
(int)le32_to_cpu(bssid->ie_length));
ie_len -= sizeof(struct ndis_80211_fixed_ies);
while (ie_len >= 2 && 2 + ie[1] <= ie_len) {
if ((ie[0] == WLAN_EID_GENERIC && ie[1] >= 4 &&
memcmp(ie + 2, "\x00\x50\xf2\x01", 4) == 0) ||
ie[0] == WLAN_EID_RSN) {
devdbg(usbdev, "IE: WPA%d",
(ie[0] == WLAN_EID_RSN) ? 2 : 1);
iwe.cmd = IWEVGENIE;
/* arbitrary cut-off at 64 */
iwe.u.data.length = min(ie[1] + 2, 64);
cev = iwe_stream_add_point(info, cev, end_buf, &iwe, ie);
}
ie_len -= 2 + ie[1];
ie += 2 + ie[1];
}
return cev;
}
static int rndis_iw_get_scan(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
struct usbnet *usbdev = netdev_priv(dev);
void *buf = NULL;
char *cev = extra;
struct ndis_80211_bssid_list_ex *bssid_list;
struct ndis_80211_bssid_ex *bssid;
int ret = -EINVAL, len, count, bssid_len;
devdbg(usbdev, "SIOCGIWSCAN");
len = CONTROL_BUFFER_SIZE;
buf = kmalloc(len, GFP_KERNEL);
if (!buf) {
ret = -ENOMEM;
goto out;
}
ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
if (ret != 0)
goto out;
bssid_list = buf;
bssid = bssid_list->bssid;
bssid_len = le32_to_cpu(bssid->length);
count = le32_to_cpu(bssid_list->num_items);
devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
cev = rndis_translate_scan(dev, info, cev,
extra + IW_SCAN_MAX_DATA, bssid);
bssid = (void *)bssid + bssid_len;
bssid_len = le32_to_cpu(bssid->length);
count--;
}
out:
wrqu->data.length = cev - extra;
wrqu->data.flags = 0;
kfree(buf);
return ret;
}
static int rndis_iw_set_genie(struct net_device *dev,
struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{
@ -2085,8 +2036,8 @@ static const iw_handler rndis_iw_handler[] =
IW_IOCTL(SIOCGIWRANGE) = (iw_handler) cfg80211_wext_giwrange,
IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid,
IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid,
IW_IOCTL(SIOCSIWSCAN) = rndis_iw_set_scan,
IW_IOCTL(SIOCGIWSCAN) = rndis_iw_get_scan,
IW_IOCTL(SIOCSIWSCAN) = (iw_handler) cfg80211_wext_siwscan,
IW_IOCTL(SIOCGIWSCAN) = (iw_handler) cfg80211_wext_giwscan,
IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid,
IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid,
IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick,
@ -2547,6 +2498,7 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
queue_delayed_work(priv->workqueue, &priv->stats_work,
round_jiffies_relative(STATS_UPDATE_JIFFIES));
INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);
INIT_WORK(&priv->work, rndis_wext_worker);
return 0;
@ -2565,6 +2517,7 @@ static void rndis_wext_unbind(struct usbnet *usbdev, struct usb_interface *intf)
disassociate(usbdev, 0);
cancel_delayed_work_sync(&priv->stats_work);
cancel_delayed_work_sync(&priv->scan_work);
cancel_work_sync(&priv->work);
flush_workqueue(priv->workqueue);
destroy_workqueue(priv->workqueue);