diff options
Diffstat (limited to 'drivers/net/wireless/ath/wil6210/cfg80211.c')
-rw-r--r-- | drivers/net/wireless/ath/wil6210/cfg80211.c | 241 |
1 files changed, 212 insertions, 29 deletions
diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index 5b340769d5bb..4806a49cb61b 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c | |||
@@ -104,41 +104,125 @@ int wil_iftype_nl2wmi(enum nl80211_iftype type) | |||
104 | return -EOPNOTSUPP; | 104 | return -EOPNOTSUPP; |
105 | } | 105 | } |
106 | 106 | ||
107 | static int wil_cfg80211_get_station(struct wiphy *wiphy, | 107 | static int wil_cid_fill_sinfo(struct wil6210_priv *wil, int cid, |
108 | struct net_device *ndev, | 108 | struct station_info *sinfo) |
109 | u8 *mac, struct station_info *sinfo) | ||
110 | { | 109 | { |
111 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
112 | int rc; | ||
113 | struct wmi_notify_req_cmd cmd = { | 110 | struct wmi_notify_req_cmd cmd = { |
114 | .cid = 0, | 111 | .cid = cid, |
115 | .interval_usec = 0, | 112 | .interval_usec = 0, |
116 | }; | 113 | }; |
114 | struct { | ||
115 | struct wil6210_mbox_hdr_wmi wmi; | ||
116 | struct wmi_notify_req_done_event evt; | ||
117 | } __packed reply; | ||
118 | struct wil_net_stats *stats = &wil->sta[cid].stats; | ||
119 | int rc; | ||
117 | 120 | ||
118 | if (memcmp(mac, wil->dst_addr[0], ETH_ALEN)) | ||
119 | return -ENOENT; | ||
120 | |||
121 | /* WMI_NOTIFY_REQ_DONE_EVENTID handler fills wil->stats.bf_mcs */ | ||
122 | rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), | 121 | rc = wmi_call(wil, WMI_NOTIFY_REQ_CMDID, &cmd, sizeof(cmd), |
123 | WMI_NOTIFY_REQ_DONE_EVENTID, NULL, 0, 20); | 122 | WMI_NOTIFY_REQ_DONE_EVENTID, &reply, sizeof(reply), 20); |
124 | if (rc) | 123 | if (rc) |
125 | return rc; | 124 | return rc; |
126 | 125 | ||
126 | wil_dbg_wmi(wil, "Link status for CID %d: {\n" | ||
127 | " MCS %d TSF 0x%016llx\n" | ||
128 | " BF status 0x%08x SNR 0x%08x SQI %d%%\n" | ||
129 | " Tx Tpt %d goodput %d Rx goodput %d\n" | ||
130 | " Sectors(rx:tx) my %d:%d peer %d:%d\n""}\n", | ||
131 | cid, le16_to_cpu(reply.evt.bf_mcs), | ||
132 | le64_to_cpu(reply.evt.tsf), reply.evt.status, | ||
133 | le32_to_cpu(reply.evt.snr_val), | ||
134 | reply.evt.sqi, | ||
135 | le32_to_cpu(reply.evt.tx_tpt), | ||
136 | le32_to_cpu(reply.evt.tx_goodput), | ||
137 | le32_to_cpu(reply.evt.rx_goodput), | ||
138 | le16_to_cpu(reply.evt.my_rx_sector), | ||
139 | le16_to_cpu(reply.evt.my_tx_sector), | ||
140 | le16_to_cpu(reply.evt.other_rx_sector), | ||
141 | le16_to_cpu(reply.evt.other_tx_sector)); | ||
142 | |||
127 | sinfo->generation = wil->sinfo_gen; | 143 | sinfo->generation = wil->sinfo_gen; |
128 | 144 | ||
129 | sinfo->filled |= STATION_INFO_TX_BITRATE; | 145 | sinfo->filled = STATION_INFO_RX_BYTES | |
146 | STATION_INFO_TX_BYTES | | ||
147 | STATION_INFO_RX_PACKETS | | ||
148 | STATION_INFO_TX_PACKETS | | ||
149 | STATION_INFO_RX_BITRATE | | ||
150 | STATION_INFO_TX_BITRATE | | ||
151 | STATION_INFO_RX_DROP_MISC | | ||
152 | STATION_INFO_TX_FAILED; | ||
153 | |||
130 | sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | 154 | sinfo->txrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; |
131 | sinfo->txrate.mcs = wil->stats.bf_mcs; | 155 | sinfo->txrate.mcs = le16_to_cpu(reply.evt.bf_mcs); |
132 | sinfo->filled |= STATION_INFO_RX_BITRATE; | ||
133 | sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; | 156 | sinfo->rxrate.flags = RATE_INFO_FLAGS_MCS | RATE_INFO_FLAGS_60G; |
134 | sinfo->rxrate.mcs = wil->stats.last_mcs_rx; | 157 | sinfo->rxrate.mcs = stats->last_mcs_rx; |
158 | sinfo->rx_bytes = stats->rx_bytes; | ||
159 | sinfo->rx_packets = stats->rx_packets; | ||
160 | sinfo->rx_dropped_misc = stats->rx_dropped; | ||
161 | sinfo->tx_bytes = stats->tx_bytes; | ||
162 | sinfo->tx_packets = stats->tx_packets; | ||
163 | sinfo->tx_failed = stats->tx_errors; | ||
135 | 164 | ||
136 | if (test_bit(wil_status_fwconnected, &wil->status)) { | 165 | if (test_bit(wil_status_fwconnected, &wil->status)) { |
137 | sinfo->filled |= STATION_INFO_SIGNAL; | 166 | sinfo->filled |= STATION_INFO_SIGNAL; |
138 | sinfo->signal = 12; /* TODO: provide real value */ | 167 | sinfo->signal = reply.evt.sqi; |
139 | } | 168 | } |
140 | 169 | ||
141 | return 0; | 170 | return rc; |
171 | } | ||
172 | |||
173 | static int wil_cfg80211_get_station(struct wiphy *wiphy, | ||
174 | struct net_device *ndev, | ||
175 | u8 *mac, struct station_info *sinfo) | ||
176 | { | ||
177 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
178 | int rc; | ||
179 | |||
180 | int cid = wil_find_cid(wil, mac); | ||
181 | |||
182 | wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); | ||
183 | if (cid < 0) | ||
184 | return cid; | ||
185 | |||
186 | rc = wil_cid_fill_sinfo(wil, cid, sinfo); | ||
187 | |||
188 | return rc; | ||
189 | } | ||
190 | |||
191 | /* | ||
192 | * Find @idx-th active STA for station dump. | ||
193 | */ | ||
194 | static int wil_find_cid_by_idx(struct wil6210_priv *wil, int idx) | ||
195 | { | ||
196 | int i; | ||
197 | |||
198 | for (i = 0; i < ARRAY_SIZE(wil->sta); i++) { | ||
199 | if (wil->sta[i].status == wil_sta_unused) | ||
200 | continue; | ||
201 | if (idx == 0) | ||
202 | return i; | ||
203 | idx--; | ||
204 | } | ||
205 | |||
206 | return -ENOENT; | ||
207 | } | ||
208 | |||
209 | static int wil_cfg80211_dump_station(struct wiphy *wiphy, | ||
210 | struct net_device *dev, int idx, | ||
211 | u8 *mac, struct station_info *sinfo) | ||
212 | { | ||
213 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
214 | int rc; | ||
215 | int cid = wil_find_cid_by_idx(wil, idx); | ||
216 | |||
217 | if (cid < 0) | ||
218 | return -ENOENT; | ||
219 | |||
220 | memcpy(mac, wil->sta[cid].addr, ETH_ALEN); | ||
221 | wil_dbg_misc(wil, "%s(%pM) CID %d\n", __func__, mac, cid); | ||
222 | |||
223 | rc = wil_cid_fill_sinfo(wil, cid, sinfo); | ||
224 | |||
225 | return rc; | ||
142 | } | 226 | } |
143 | 227 | ||
144 | static int wil_cfg80211_change_iface(struct wiphy *wiphy, | 228 | static int wil_cfg80211_change_iface(struct wiphy *wiphy, |
@@ -181,6 +265,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, | |||
181 | u16 chnl[4]; | 265 | u16 chnl[4]; |
182 | } __packed cmd; | 266 | } __packed cmd; |
183 | uint i, n; | 267 | uint i, n; |
268 | int rc; | ||
184 | 269 | ||
185 | if (wil->scan_request) { | 270 | if (wil->scan_request) { |
186 | wil_err(wil, "Already scanning\n"); | 271 | wil_err(wil, "Already scanning\n"); |
@@ -198,7 +283,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, | |||
198 | 283 | ||
199 | /* FW don't support scan after connection attempt */ | 284 | /* FW don't support scan after connection attempt */ |
200 | if (test_bit(wil_status_dontscan, &wil->status)) { | 285 | if (test_bit(wil_status_dontscan, &wil->status)) { |
201 | wil_err(wil, "Scan after connect attempt not supported\n"); | 286 | wil_err(wil, "Can't scan now\n"); |
202 | return -EBUSY; | 287 | return -EBUSY; |
203 | } | 288 | } |
204 | 289 | ||
@@ -221,8 +306,13 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, | |||
221 | request->channels[i]->center_freq); | 306 | request->channels[i]->center_freq); |
222 | } | 307 | } |
223 | 308 | ||
224 | return wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + | 309 | rc = wmi_send(wil, WMI_START_SCAN_CMDID, &cmd, sizeof(cmd.cmd) + |
225 | cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); | 310 | cmd.cmd.num_channels * sizeof(cmd.cmd.channel_list[0])); |
311 | |||
312 | if (rc) | ||
313 | wil->scan_request = NULL; | ||
314 | |||
315 | return rc; | ||
226 | } | 316 | } |
227 | 317 | ||
228 | static int wil_cfg80211_connect(struct wiphy *wiphy, | 318 | static int wil_cfg80211_connect(struct wiphy *wiphy, |
@@ -237,6 +327,10 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, | |||
237 | int ch; | 327 | int ch; |
238 | int rc = 0; | 328 | int rc = 0; |
239 | 329 | ||
330 | if (test_bit(wil_status_fwconnecting, &wil->status) || | ||
331 | test_bit(wil_status_fwconnected, &wil->status)) | ||
332 | return -EALREADY; | ||
333 | |||
240 | bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, | 334 | bss = cfg80211_get_bss(wiphy, sme->channel, sme->bssid, |
241 | sme->ssid, sme->ssid_len, | 335 | sme->ssid, sme->ssid_len, |
242 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | 336 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); |
@@ -318,10 +412,7 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, | |||
318 | 412 | ||
319 | memcpy(conn.bssid, bss->bssid, ETH_ALEN); | 413 | memcpy(conn.bssid, bss->bssid, ETH_ALEN); |
320 | memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); | 414 | memcpy(conn.dst_mac, bss->bssid, ETH_ALEN); |
321 | /* | 415 | |
322 | * FW don't support scan after connection attempt | ||
323 | */ | ||
324 | set_bit(wil_status_dontscan, &wil->status); | ||
325 | set_bit(wil_status_fwconnecting, &wil->status); | 416 | set_bit(wil_status_fwconnecting, &wil->status); |
326 | 417 | ||
327 | rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); | 418 | rc = wmi_send(wil, WMI_CONNECT_CMDID, &conn, sizeof(conn)); |
@@ -330,7 +421,6 @@ static int wil_cfg80211_connect(struct wiphy *wiphy, | |||
330 | mod_timer(&wil->connect_timer, | 421 | mod_timer(&wil->connect_timer, |
331 | jiffies + msecs_to_jiffies(2000)); | 422 | jiffies + msecs_to_jiffies(2000)); |
332 | } else { | 423 | } else { |
333 | clear_bit(wil_status_dontscan, &wil->status); | ||
334 | clear_bit(wil_status_fwconnecting, &wil->status); | 424 | clear_bit(wil_status_fwconnecting, &wil->status); |
335 | } | 425 | } |
336 | 426 | ||
@@ -352,6 +442,40 @@ static int wil_cfg80211_disconnect(struct wiphy *wiphy, | |||
352 | return rc; | 442 | return rc; |
353 | } | 443 | } |
354 | 444 | ||
445 | static int wil_cfg80211_mgmt_tx(struct wiphy *wiphy, | ||
446 | struct wireless_dev *wdev, | ||
447 | struct cfg80211_mgmt_tx_params *params, | ||
448 | u64 *cookie) | ||
449 | { | ||
450 | const u8 *buf = params->buf; | ||
451 | size_t len = params->len; | ||
452 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
453 | int rc; | ||
454 | struct ieee80211_mgmt *mgmt_frame = (void *)buf; | ||
455 | struct wmi_sw_tx_req_cmd *cmd; | ||
456 | struct { | ||
457 | struct wil6210_mbox_hdr_wmi wmi; | ||
458 | struct wmi_sw_tx_complete_event evt; | ||
459 | } __packed evt; | ||
460 | |||
461 | cmd = kmalloc(sizeof(*cmd) + len, GFP_KERNEL); | ||
462 | if (!cmd) | ||
463 | return -ENOMEM; | ||
464 | |||
465 | memcpy(cmd->dst_mac, mgmt_frame->da, WMI_MAC_LEN); | ||
466 | cmd->len = cpu_to_le16(len); | ||
467 | memcpy(cmd->payload, buf, len); | ||
468 | |||
469 | rc = wmi_call(wil, WMI_SW_TX_REQ_CMDID, cmd, sizeof(*cmd) + len, | ||
470 | WMI_SW_TX_COMPLETE_EVENTID, &evt, sizeof(evt), 2000); | ||
471 | if (rc == 0) | ||
472 | rc = evt.evt.status; | ||
473 | |||
474 | kfree(cmd); | ||
475 | |||
476 | return rc; | ||
477 | } | ||
478 | |||
355 | static int wil_cfg80211_set_channel(struct wiphy *wiphy, | 479 | static int wil_cfg80211_set_channel(struct wiphy *wiphy, |
356 | struct cfg80211_chan_def *chandef) | 480 | struct cfg80211_chan_def *chandef) |
357 | { | 481 | { |
@@ -402,6 +526,41 @@ static int wil_cfg80211_set_default_key(struct wiphy *wiphy, | |||
402 | return 0; | 526 | return 0; |
403 | } | 527 | } |
404 | 528 | ||
529 | static int wil_remain_on_channel(struct wiphy *wiphy, | ||
530 | struct wireless_dev *wdev, | ||
531 | struct ieee80211_channel *chan, | ||
532 | unsigned int duration, | ||
533 | u64 *cookie) | ||
534 | { | ||
535 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
536 | int rc; | ||
537 | |||
538 | /* TODO: handle duration */ | ||
539 | wil_info(wil, "%s(%d, %d ms)\n", __func__, chan->center_freq, duration); | ||
540 | |||
541 | rc = wmi_set_channel(wil, chan->hw_value); | ||
542 | if (rc) | ||
543 | return rc; | ||
544 | |||
545 | rc = wmi_rxon(wil, true); | ||
546 | |||
547 | return rc; | ||
548 | } | ||
549 | |||
550 | static int wil_cancel_remain_on_channel(struct wiphy *wiphy, | ||
551 | struct wireless_dev *wdev, | ||
552 | u64 cookie) | ||
553 | { | ||
554 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
555 | int rc; | ||
556 | |||
557 | wil_info(wil, "%s()\n", __func__); | ||
558 | |||
559 | rc = wmi_rxon(wil, false); | ||
560 | |||
561 | return rc; | ||
562 | } | ||
563 | |||
405 | static int wil_fix_bcon(struct wil6210_priv *wil, | 564 | static int wil_fix_bcon(struct wil6210_priv *wil, |
406 | struct cfg80211_beacon_data *bcon) | 565 | struct cfg80211_beacon_data *bcon) |
407 | { | 566 | { |
@@ -450,18 +609,20 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, | |||
450 | if (wil_fix_bcon(wil, bcon)) | 609 | if (wil_fix_bcon(wil, bcon)) |
451 | wil_dbg_misc(wil, "Fixed bcon\n"); | 610 | wil_dbg_misc(wil, "Fixed bcon\n"); |
452 | 611 | ||
612 | mutex_lock(&wil->mutex); | ||
613 | |||
453 | rc = wil_reset(wil); | 614 | rc = wil_reset(wil); |
454 | if (rc) | 615 | if (rc) |
455 | return rc; | 616 | goto out; |
456 | 617 | ||
457 | /* Rx VRING. */ | 618 | /* Rx VRING. */ |
458 | rc = wil_rx_init(wil); | 619 | rc = wil_rx_init(wil); |
459 | if (rc) | 620 | if (rc) |
460 | return rc; | 621 | goto out; |
461 | 622 | ||
462 | rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); | 623 | rc = wmi_set_ssid(wil, info->ssid_len, info->ssid); |
463 | if (rc) | 624 | if (rc) |
464 | return rc; | 625 | goto out; |
465 | 626 | ||
466 | /* MAC address - pre-requisite for other commands */ | 627 | /* MAC address - pre-requisite for other commands */ |
467 | wmi_set_mac_address(wil, ndev->dev_addr); | 628 | wmi_set_mac_address(wil, ndev->dev_addr); |
@@ -485,11 +646,13 @@ static int wil_cfg80211_start_ap(struct wiphy *wiphy, | |||
485 | rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype, | 646 | rc = wmi_pcp_start(wil, info->beacon_interval, wmi_nettype, |
486 | channel->hw_value); | 647 | channel->hw_value); |
487 | if (rc) | 648 | if (rc) |
488 | return rc; | 649 | goto out; |
489 | 650 | ||
490 | 651 | ||
491 | netif_carrier_on(ndev); | 652 | netif_carrier_on(ndev); |
492 | 653 | ||
654 | out: | ||
655 | mutex_unlock(&wil->mutex); | ||
493 | return rc; | 656 | return rc; |
494 | } | 657 | } |
495 | 658 | ||
@@ -499,17 +662,36 @@ static int wil_cfg80211_stop_ap(struct wiphy *wiphy, | |||
499 | int rc = 0; | 662 | int rc = 0; |
500 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | 663 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); |
501 | 664 | ||
665 | mutex_lock(&wil->mutex); | ||
666 | |||
502 | rc = wmi_pcp_stop(wil); | 667 | rc = wmi_pcp_stop(wil); |
503 | 668 | ||
669 | mutex_unlock(&wil->mutex); | ||
504 | return rc; | 670 | return rc; |
505 | } | 671 | } |
506 | 672 | ||
673 | static int wil_cfg80211_del_station(struct wiphy *wiphy, | ||
674 | struct net_device *dev, u8 *mac) | ||
675 | { | ||
676 | struct wil6210_priv *wil = wiphy_to_wil(wiphy); | ||
677 | |||
678 | mutex_lock(&wil->mutex); | ||
679 | wil6210_disconnect(wil, mac); | ||
680 | mutex_unlock(&wil->mutex); | ||
681 | |||
682 | return 0; | ||
683 | } | ||
684 | |||
507 | static struct cfg80211_ops wil_cfg80211_ops = { | 685 | static struct cfg80211_ops wil_cfg80211_ops = { |
508 | .scan = wil_cfg80211_scan, | 686 | .scan = wil_cfg80211_scan, |
509 | .connect = wil_cfg80211_connect, | 687 | .connect = wil_cfg80211_connect, |
510 | .disconnect = wil_cfg80211_disconnect, | 688 | .disconnect = wil_cfg80211_disconnect, |
511 | .change_virtual_intf = wil_cfg80211_change_iface, | 689 | .change_virtual_intf = wil_cfg80211_change_iface, |
512 | .get_station = wil_cfg80211_get_station, | 690 | .get_station = wil_cfg80211_get_station, |
691 | .dump_station = wil_cfg80211_dump_station, | ||
692 | .remain_on_channel = wil_remain_on_channel, | ||
693 | .cancel_remain_on_channel = wil_cancel_remain_on_channel, | ||
694 | .mgmt_tx = wil_cfg80211_mgmt_tx, | ||
513 | .set_monitor_channel = wil_cfg80211_set_channel, | 695 | .set_monitor_channel = wil_cfg80211_set_channel, |
514 | .add_key = wil_cfg80211_add_key, | 696 | .add_key = wil_cfg80211_add_key, |
515 | .del_key = wil_cfg80211_del_key, | 697 | .del_key = wil_cfg80211_del_key, |
@@ -517,6 +699,7 @@ static struct cfg80211_ops wil_cfg80211_ops = { | |||
517 | /* AP mode */ | 699 | /* AP mode */ |
518 | .start_ap = wil_cfg80211_start_ap, | 700 | .start_ap = wil_cfg80211_start_ap, |
519 | .stop_ap = wil_cfg80211_stop_ap, | 701 | .stop_ap = wil_cfg80211_stop_ap, |
702 | .del_station = wil_cfg80211_del_station, | ||
520 | }; | 703 | }; |
521 | 704 | ||
522 | static void wil_wiphy_init(struct wiphy *wiphy) | 705 | static void wil_wiphy_init(struct wiphy *wiphy) |
@@ -542,7 +725,7 @@ static void wil_wiphy_init(struct wiphy *wiphy) | |||
542 | wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; | 725 | wiphy->bands[IEEE80211_BAND_60GHZ] = &wil_band_60ghz; |
543 | 726 | ||
544 | /* TODO: figure this out */ | 727 | /* TODO: figure this out */ |
545 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; | 728 | wiphy->signal_type = CFG80211_SIGNAL_TYPE_UNSPEC; |
546 | 729 | ||
547 | wiphy->cipher_suites = wil_cipher_suites; | 730 | wiphy->cipher_suites = wil_cipher_suites; |
548 | wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); | 731 | wiphy->n_cipher_suites = ARRAY_SIZE(wil_cipher_suites); |