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:
Родитель
4d2a369ec0
Коммит
71a7b26d3e
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче