aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/net/wireless/rndis_wlan.c341
1 files changed, 147 insertions, 194 deletions
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 4252903cf89a..239e6a14eae2 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -356,8 +356,11 @@ struct rndis_wext_private {
356 356
357 struct wireless_dev wdev; 357 struct wireless_dev wdev;
358 358
359 struct cfg80211_scan_request *scan_request;
360
359 struct workqueue_struct *workqueue; 361 struct workqueue_struct *workqueue;
360 struct delayed_work stats_work; 362 struct delayed_work stats_work;
363 struct delayed_work scan_work;
361 struct work_struct work; 364 struct work_struct work;
362 struct mutex command_lock; 365 struct mutex command_lock;
363 spinlock_t stats_lock; 366 spinlock_t stats_lock;
@@ -413,8 +416,12 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex,
413 enum nl80211_iftype type, u32 *flags, 416 enum nl80211_iftype type, u32 *flags,
414 struct vif_params *params); 417 struct vif_params *params);
415 418
419static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
420 struct cfg80211_scan_request *request);
421
416struct cfg80211_ops rndis_config_ops = { 422struct cfg80211_ops rndis_config_ops = {
417 .change_virtual_intf = rndis_change_virtual_intf, 423 .change_virtual_intf = rndis_change_virtual_intf,
424 .scan = rndis_scan,
418}; 425};
419 426
420void *rndis_wiphy_privid = &rndis_wiphy_privid; 427void *rndis_wiphy_privid = &rndis_wiphy_privid;
@@ -1164,6 +1171,142 @@ static int rndis_change_virtual_intf(struct wiphy *wiphy, int ifindex,
1164 return set_infra_mode(usbdev, mode); 1171 return set_infra_mode(usbdev, mode);
1165} 1172}
1166 1173
1174
1175#define SCAN_DELAY_JIFFIES (HZ)
1176static int rndis_scan(struct wiphy *wiphy, struct net_device *dev,
1177 struct cfg80211_scan_request *request)
1178{
1179 struct usbnet *usbdev = netdev_priv(dev);
1180 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1181 int ret;
1182 __le32 tmp;
1183
1184 devdbg(usbdev, "cfg80211.scan");
1185
1186 if (!request)
1187 return -EINVAL;
1188
1189 if (priv->scan_request && priv->scan_request != request)
1190 return -EBUSY;
1191
1192 priv->scan_request = request;
1193
1194 tmp = cpu_to_le32(1);
1195 ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
1196 sizeof(tmp));
1197 if (ret == 0) {
1198 /* Wait before retrieving scan results from device */
1199 queue_delayed_work(priv->workqueue, &priv->scan_work,
1200 SCAN_DELAY_JIFFIES);
1201 }
1202
1203 return ret;
1204}
1205
1206
1207static struct cfg80211_bss *rndis_bss_info_update(struct usbnet *usbdev,
1208 struct ndis_80211_bssid_ex *bssid)
1209{
1210 struct rndis_wext_private *priv = get_rndis_wext_priv(usbdev);
1211 struct ieee80211_channel *channel;
1212 s32 signal;
1213 u64 timestamp;
1214 u16 capability;
1215 u16 beacon_interval;
1216 struct ndis_80211_fixed_ies *fixed;
1217 int ie_len, bssid_len;
1218 u8 *ie;
1219
1220 /* parse bssid structure */
1221 bssid_len = le32_to_cpu(bssid->length);
1222
1223 if (bssid_len < sizeof(struct ndis_80211_bssid_ex) +
1224 sizeof(struct ndis_80211_fixed_ies))
1225 return NULL;
1226
1227 fixed = (struct ndis_80211_fixed_ies *)bssid->ies;
1228
1229 ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies));
1230 ie_len = min(bssid_len - (int)sizeof(*bssid),
1231 (int)le32_to_cpu(bssid->ie_length));
1232 ie_len -= sizeof(struct ndis_80211_fixed_ies);
1233 if (ie_len < 0)
1234 return NULL;
1235
1236 /* extract data for cfg80211_inform_bss */
1237 channel = ieee80211_get_channel(priv->wdev.wiphy,
1238 KHZ_TO_MHZ(le32_to_cpu(bssid->config.ds_config)));
1239 if (!channel)
1240 return NULL;
1241
1242 signal = level_to_qual(le32_to_cpu(bssid->rssi));
1243 timestamp = le64_to_cpu(*(__le64 *)fixed->timestamp);
1244 capability = le16_to_cpu(fixed->capabilities);
1245 beacon_interval = le16_to_cpu(fixed->beacon_interval);
1246
1247 return cfg80211_inform_bss(priv->wdev.wiphy, channel, bssid->mac,
1248 timestamp, capability, beacon_interval, ie, ie_len, signal,
1249 GFP_KERNEL);
1250}
1251
1252
1253static int rndis_check_bssid_list(struct usbnet *usbdev)
1254{
1255 void *buf = NULL;
1256 struct ndis_80211_bssid_list_ex *bssid_list;
1257 struct ndis_80211_bssid_ex *bssid;
1258 int ret = -EINVAL, len, count, bssid_len;
1259
1260 devdbg(usbdev, "check_bssid_list");
1261
1262 len = CONTROL_BUFFER_SIZE;
1263 buf = kmalloc(len, GFP_KERNEL);
1264 if (!buf) {
1265 ret = -ENOMEM;
1266 goto out;
1267 }
1268
1269 ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
1270 if (ret != 0)
1271 goto out;
1272
1273 bssid_list = buf;
1274 bssid = bssid_list->bssid;
1275 bssid_len = le32_to_cpu(bssid->length);
1276 count = le32_to_cpu(bssid_list->num_items);
1277 devdbg(usbdev, "check_bssid_list: %d BSSIDs found", count);
1278
1279 while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
1280 rndis_bss_info_update(usbdev, bssid);
1281
1282 bssid = (void *)bssid + bssid_len;
1283 bssid_len = le32_to_cpu(bssid->length);
1284 count--;
1285 }
1286
1287out:
1288 kfree(buf);
1289 return ret;
1290}
1291
1292
1293static void rndis_get_scan_results(struct work_struct *work)
1294{
1295 struct rndis_wext_private *priv =
1296 container_of(work, struct rndis_wext_private, scan_work.work);
1297 struct usbnet *usbdev = priv->usbdev;
1298 int ret;
1299
1300 devdbg(usbdev, "get_scan_results");
1301
1302 ret = rndis_check_bssid_list(usbdev);
1303
1304 cfg80211_scan_done(priv->scan_request, ret < 0);
1305
1306 priv->scan_request = NULL;
1307}
1308
1309
1167/* 1310/*
1168 * wireless extension handlers 1311 * wireless extension handlers
1169 */ 1312 */
@@ -1531,198 +1674,6 @@ static int rndis_iw_set_encode_ext(struct net_device *dev,
1531} 1674}
1532 1675
1533 1676
1534static int rndis_iw_set_scan(struct net_device *dev,
1535 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1536{
1537 struct usbnet *usbdev = netdev_priv(dev);
1538 union iwreq_data evt;
1539 int ret = -EINVAL;
1540 __le32 tmp;
1541
1542 devdbg(usbdev, "SIOCSIWSCAN");
1543
1544 if (wrqu->data.flags == 0) {
1545 tmp = cpu_to_le32(1);
1546 ret = rndis_set_oid(usbdev, OID_802_11_BSSID_LIST_SCAN, &tmp,
1547 sizeof(tmp));
1548 evt.data.flags = 0;
1549 evt.data.length = 0;
1550 wireless_send_event(dev, SIOCGIWSCAN, &evt, NULL);
1551 }
1552 return ret;
1553}
1554
1555
1556static char *rndis_translate_scan(struct net_device *dev,
1557 struct iw_request_info *info, char *cev,
1558 char *end_buf,
1559 struct ndis_80211_bssid_ex *bssid)
1560{
1561 struct usbnet *usbdev = netdev_priv(dev);
1562 u8 *ie;
1563 char *current_val;
1564 int bssid_len, ie_len, i;
1565 u32 beacon, atim;
1566 struct iw_event iwe;
1567 unsigned char sbuf[32];
1568
1569 bssid_len = le32_to_cpu(bssid->length);
1570
1571 devdbg(usbdev, "BSSID %pM", bssid->mac);
1572 iwe.cmd = SIOCGIWAP;
1573 iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
1574 memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN);
1575 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_ADDR_LEN);
1576
1577 devdbg(usbdev, "SSID(%d) %s", le32_to_cpu(bssid->ssid.length),
1578 bssid->ssid.essid);
1579 iwe.cmd = SIOCGIWESSID;
1580 iwe.u.essid.length = le32_to_cpu(bssid->ssid.length);
1581 iwe.u.essid.flags = 1;
1582 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, bssid->ssid.essid);
1583
1584 devdbg(usbdev, "MODE %d", le32_to_cpu(bssid->net_infra));
1585 iwe.cmd = SIOCGIWMODE;
1586 switch (le32_to_cpu(bssid->net_infra)) {
1587 case ndis_80211_infra_adhoc:
1588 iwe.u.mode = IW_MODE_ADHOC;
1589 break;
1590 case ndis_80211_infra_infra:
1591 iwe.u.mode = IW_MODE_INFRA;
1592 break;
1593 /*case ndis_80211_infra_auto_unknown:*/
1594 default:
1595 iwe.u.mode = IW_MODE_AUTO;
1596 break;
1597 }
1598 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_UINT_LEN);
1599
1600 devdbg(usbdev, "FREQ %d kHz", le32_to_cpu(bssid->config.ds_config));
1601 iwe.cmd = SIOCGIWFREQ;
1602 dsconfig_to_freq(le32_to_cpu(bssid->config.ds_config), &iwe.u.freq);
1603 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_FREQ_LEN);
1604
1605 devdbg(usbdev, "QUAL %d", le32_to_cpu(bssid->rssi));
1606 iwe.cmd = IWEVQUAL;
1607 iwe.u.qual.qual = level_to_qual(le32_to_cpu(bssid->rssi));
1608 iwe.u.qual.level = level_to_qual(le32_to_cpu(bssid->rssi));
1609 iwe.u.qual.updated = IW_QUAL_QUAL_UPDATED
1610 | IW_QUAL_LEVEL_UPDATED
1611 | IW_QUAL_NOISE_INVALID;
1612 cev = iwe_stream_add_event(info, cev, end_buf, &iwe, IW_EV_QUAL_LEN);
1613
1614 devdbg(usbdev, "ENCODE %d", le32_to_cpu(bssid->privacy));
1615 iwe.cmd = SIOCGIWENCODE;
1616 iwe.u.data.length = 0;
1617 if (le32_to_cpu(bssid->privacy) == ndis_80211_priv_accept_all)
1618 iwe.u.data.flags = IW_ENCODE_DISABLED;
1619 else
1620 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
1621
1622 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, NULL);
1623
1624 devdbg(usbdev, "RATES:");
1625 current_val = cev + iwe_stream_lcp_len(info);
1626 iwe.cmd = SIOCGIWRATE;
1627 for (i = 0; i < sizeof(bssid->rates); i++) {
1628 if (bssid->rates[i] & 0x7f) {
1629 iwe.u.bitrate.value =
1630 ((bssid->rates[i] & 0x7f) *
1631 500000);
1632 devdbg(usbdev, " %d", iwe.u.bitrate.value);
1633 current_val = iwe_stream_add_value(info, cev,
1634 current_val, end_buf, &iwe,
1635 IW_EV_PARAM_LEN);
1636 }
1637 }
1638
1639 if ((current_val - cev) > iwe_stream_lcp_len(info))
1640 cev = current_val;
1641
1642 beacon = le32_to_cpu(bssid->config.beacon_period);
1643 devdbg(usbdev, "BCN_INT %d", beacon);
1644 iwe.cmd = IWEVCUSTOM;
1645 snprintf(sbuf, sizeof(sbuf), "bcn_int=%d", beacon);
1646 iwe.u.data.length = strlen(sbuf);
1647 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
1648
1649 atim = le32_to_cpu(bssid->config.atim_window);
1650 devdbg(usbdev, "ATIM %d", atim);
1651 iwe.cmd = IWEVCUSTOM;
1652 snprintf(sbuf, sizeof(sbuf), "atim=%u", atim);
1653 iwe.u.data.length = strlen(sbuf);
1654 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, sbuf);
1655
1656 ie = (void *)(bssid->ies + sizeof(struct ndis_80211_fixed_ies));
1657 ie_len = min(bssid_len - (int)sizeof(*bssid),
1658 (int)le32_to_cpu(bssid->ie_length));
1659 ie_len -= sizeof(struct ndis_80211_fixed_ies);
1660 while (ie_len >= 2 && 2 + ie[1] <= ie_len) {
1661 if ((ie[0] == WLAN_EID_GENERIC && ie[1] >= 4 &&
1662 memcmp(ie + 2, "\x00\x50\xf2\x01", 4) == 0) ||
1663 ie[0] == WLAN_EID_RSN) {
1664 devdbg(usbdev, "IE: WPA%d",
1665 (ie[0] == WLAN_EID_RSN) ? 2 : 1);
1666 iwe.cmd = IWEVGENIE;
1667 /* arbitrary cut-off at 64 */
1668 iwe.u.data.length = min(ie[1] + 2, 64);
1669 cev = iwe_stream_add_point(info, cev, end_buf, &iwe, ie);
1670 }
1671
1672 ie_len -= 2 + ie[1];
1673 ie += 2 + ie[1];
1674 }
1675
1676 return cev;
1677}
1678
1679
1680static int rndis_iw_get_scan(struct net_device *dev,
1681 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1682{
1683 struct usbnet *usbdev = netdev_priv(dev);
1684 void *buf = NULL;
1685 char *cev = extra;
1686 struct ndis_80211_bssid_list_ex *bssid_list;
1687 struct ndis_80211_bssid_ex *bssid;
1688 int ret = -EINVAL, len, count, bssid_len;
1689
1690 devdbg(usbdev, "SIOCGIWSCAN");
1691
1692 len = CONTROL_BUFFER_SIZE;
1693 buf = kmalloc(len, GFP_KERNEL);
1694 if (!buf) {
1695 ret = -ENOMEM;
1696 goto out;
1697 }
1698
1699 ret = rndis_query_oid(usbdev, OID_802_11_BSSID_LIST, buf, &len);
1700
1701 if (ret != 0)
1702 goto out;
1703
1704 bssid_list = buf;
1705 bssid = bssid_list->bssid;
1706 bssid_len = le32_to_cpu(bssid->length);
1707 count = le32_to_cpu(bssid_list->num_items);
1708 devdbg(usbdev, "SIOCGIWSCAN: %d BSSIDs found", count);
1709
1710 while (count && ((void *)bssid + bssid_len) <= (buf + len)) {
1711 cev = rndis_translate_scan(dev, info, cev,
1712 extra + IW_SCAN_MAX_DATA, bssid);
1713 bssid = (void *)bssid + bssid_len;
1714 bssid_len = le32_to_cpu(bssid->length);
1715 count--;
1716 }
1717
1718out:
1719 wrqu->data.length = cev - extra;
1720 wrqu->data.flags = 0;
1721 kfree(buf);
1722 return ret;
1723}
1724
1725
1726static int rndis_iw_set_genie(struct net_device *dev, 1677static int rndis_iw_set_genie(struct net_device *dev,
1727 struct iw_request_info *info, union iwreq_data *wrqu, char *extra) 1678 struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
1728{ 1679{
@@ -2085,8 +2036,8 @@ static const iw_handler rndis_iw_handler[] =
2085 IW_IOCTL(SIOCGIWRANGE) = (iw_handler) cfg80211_wext_giwrange, 2036 IW_IOCTL(SIOCGIWRANGE) = (iw_handler) cfg80211_wext_giwrange,
2086 IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid, 2037 IW_IOCTL(SIOCSIWAP) = rndis_iw_set_bssid,
2087 IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid, 2038 IW_IOCTL(SIOCGIWAP) = rndis_iw_get_bssid,
2088 IW_IOCTL(SIOCSIWSCAN) = rndis_iw_set_scan, 2039 IW_IOCTL(SIOCSIWSCAN) = (iw_handler) cfg80211_wext_siwscan,
2089 IW_IOCTL(SIOCGIWSCAN) = rndis_iw_get_scan, 2040 IW_IOCTL(SIOCGIWSCAN) = (iw_handler) cfg80211_wext_giwscan,
2090 IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid, 2041 IW_IOCTL(SIOCSIWESSID) = rndis_iw_set_essid,
2091 IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid, 2042 IW_IOCTL(SIOCGIWESSID) = rndis_iw_get_essid,
2092 IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick, 2043 IW_IOCTL(SIOCSIWNICKN) = rndis_iw_set_nick,
@@ -2547,6 +2498,7 @@ static int rndis_wext_bind(struct usbnet *usbdev, struct usb_interface *intf)
2547 INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats); 2498 INIT_DELAYED_WORK(&priv->stats_work, rndis_update_wireless_stats);
2548 queue_delayed_work(priv->workqueue, &priv->stats_work, 2499 queue_delayed_work(priv->workqueue, &priv->stats_work,
2549 round_jiffies_relative(STATS_UPDATE_JIFFIES)); 2500 round_jiffies_relative(STATS_UPDATE_JIFFIES));
2501 INIT_DELAYED_WORK(&priv->scan_work, rndis_get_scan_results);
2550 INIT_WORK(&priv->work, rndis_wext_worker); 2502 INIT_WORK(&priv->work, rndis_wext_worker);
2551 2503
2552 return 0; 2504 return 0;
@@ -2565,6 +2517,7 @@ static void rndis_wext_unbind(struct usbnet *usbdev, struct usb_interface *intf)
2565 disassociate(usbdev, 0); 2517 disassociate(usbdev, 0);
2566 2518
2567 cancel_delayed_work_sync(&priv->stats_work); 2519 cancel_delayed_work_sync(&priv->stats_work);
2520 cancel_delayed_work_sync(&priv->scan_work);
2568 cancel_work_sync(&priv->work); 2521 cancel_work_sync(&priv->work);
2569 flush_workqueue(priv->workqueue); 2522 flush_workqueue(priv->workqueue);
2570 destroy_workqueue(priv->workqueue); 2523 destroy_workqueue(priv->workqueue);