1// SPDX-License-Identifier: GPL-2.0 2/* IEEE 802.11 SoftMAC layer 3 * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com> 4 * 5 * Mostly extracted from the rtl8180-sa2400 driver for the 6 * in-kernel generic ieee802.11 stack. 7 * 8 * Some pieces of code might be stolen from ipw2100 driver 9 * copyright of who own it's copyright ;-) 10 * 11 * PS wx handler mostly stolen from hostap, copyright who 12 * own it's copyright ;-) 13 */ 14#include <linux/etherdevice.h> 15 16#include "rtllib.h" 17 18int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, 19 union iwreq_data *wrqu, char *b) 20{ 21 int ret; 22 struct iw_freq *fwrq = &wrqu->freq; 23 24 mutex_lock(&ieee->wx_mutex); 25 26 if (ieee->iw_mode == IW_MODE_INFRA) { 27 ret = 0; 28 goto out; 29 } 30 31 /* if setting by freq convert to channel */ 32 if (fwrq->e == 1) { 33 if ((fwrq->m >= (int)2.412e8 && 34 fwrq->m <= (int)2.487e8)) { 35 fwrq->m = ieee80211_freq_khz_to_channel(fwrq->m / 100); 36 fwrq->e = 0; 37 } 38 } 39 40 if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) { 41 ret = -EOPNOTSUPP; 42 goto out; 43 44 } else { /* Set the channel */ 45 46 if (ieee->active_channel_map[fwrq->m] != 1) { 47 ret = -EINVAL; 48 goto out; 49 } 50 ieee->current_network.channel = fwrq->m; 51 ieee->set_chan(ieee->dev, ieee->current_network.channel); 52 } 53 54 ret = 0; 55out: 56 mutex_unlock(&ieee->wx_mutex); 57 return ret; 58} 59EXPORT_SYMBOL(rtllib_wx_set_freq); 60 61int rtllib_wx_get_freq(struct rtllib_device *ieee, 62 struct iw_request_info *a, 63 union iwreq_data *wrqu, char *b) 64{ 65 struct iw_freq *fwrq = &wrqu->freq; 66 67 if (ieee->current_network.channel == 0) 68 return -1; 69 fwrq->m = ieee80211_channel_to_freq_khz(ieee->current_network.channel, 70 NL80211_BAND_2GHZ) * 100; 71 fwrq->e = 1; 72 return 0; 73} 74EXPORT_SYMBOL(rtllib_wx_get_freq); 75 76int rtllib_wx_get_wap(struct rtllib_device *ieee, 77 struct iw_request_info *info, 78 union iwreq_data *wrqu, char *extra) 79{ 80 unsigned long flags; 81 82 wrqu->ap_addr.sa_family = ARPHRD_ETHER; 83 84 if (ieee->iw_mode == IW_MODE_MONITOR) 85 return -1; 86 87 /* We want avoid to give to the user inconsistent infos*/ 88 spin_lock_irqsave(&ieee->lock, flags); 89 90 if (ieee->link_state != MAC80211_LINKED && 91 ieee->link_state != MAC80211_LINKED_SCANNING && 92 ieee->wap_set == 0) 93 94 eth_zero_addr(wrqu->ap_addr.sa_data); 95 else 96 memcpy(wrqu->ap_addr.sa_data, 97 ieee->current_network.bssid, ETH_ALEN); 98 99 spin_unlock_irqrestore(&ieee->lock, flags); 100 101 return 0; 102} 103EXPORT_SYMBOL(rtllib_wx_get_wap); 104 105int rtllib_wx_set_wap(struct rtllib_device *ieee, 106 struct iw_request_info *info, 107 union iwreq_data *awrq, 108 char *extra) 109{ 110 int ret = 0; 111 unsigned long flags; 112 113 short ifup = ieee->proto_started; 114 struct sockaddr *temp = (struct sockaddr *)awrq; 115 116 rtllib_stop_scan_syncro(ieee); 117 118 mutex_lock(&ieee->wx_mutex); 119 /* use ifconfig hw ether */ 120 121 if (temp->sa_family != ARPHRD_ETHER) { 122 ret = -EINVAL; 123 goto out; 124 } 125 126 if (is_zero_ether_addr(temp->sa_data)) { 127 spin_lock_irqsave(&ieee->lock, flags); 128 ether_addr_copy(ieee->current_network.bssid, temp->sa_data); 129 ieee->wap_set = 0; 130 spin_unlock_irqrestore(&ieee->lock, flags); 131 ret = -1; 132 goto out; 133 } 134 135 if (ifup) 136 rtllib_stop_protocol(ieee); 137 138 /* just to avoid to give inconsistent infos in the 139 * get wx method. not really needed otherwise 140 */ 141 spin_lock_irqsave(&ieee->lock, flags); 142 143 ieee->cannot_notify = false; 144 ether_addr_copy(ieee->current_network.bssid, temp->sa_data); 145 ieee->wap_set = !is_zero_ether_addr(temp->sa_data); 146 147 spin_unlock_irqrestore(&ieee->lock, flags); 148 149 if (ifup) 150 rtllib_start_protocol(ieee); 151out: 152 mutex_unlock(&ieee->wx_mutex); 153 return ret; 154} 155EXPORT_SYMBOL(rtllib_wx_set_wap); 156 157int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a, 158 union iwreq_data *wrqu, char *b) 159{ 160 int len, ret = 0; 161 unsigned long flags; 162 163 if (ieee->iw_mode == IW_MODE_MONITOR) 164 return -1; 165 166 /* We want avoid to give to the user inconsistent infos*/ 167 spin_lock_irqsave(&ieee->lock, flags); 168 169 if (ieee->current_network.ssid[0] == '\0' || 170 ieee->current_network.ssid_len == 0) { 171 ret = -1; 172 goto out; 173 } 174 175 if (ieee->link_state != MAC80211_LINKED && 176 ieee->link_state != MAC80211_LINKED_SCANNING && 177 ieee->ssid_set == 0) { 178 ret = -1; 179 goto out; 180 } 181 len = ieee->current_network.ssid_len; 182 wrqu->essid.length = len; 183 strncpy(b, ieee->current_network.ssid, len); 184 wrqu->essid.flags = 1; 185 186out: 187 spin_unlock_irqrestore(&ieee->lock, flags); 188 189 return ret; 190} 191EXPORT_SYMBOL(rtllib_wx_get_essid); 192 193int rtllib_wx_set_rate(struct rtllib_device *ieee, 194 struct iw_request_info *info, 195 union iwreq_data *wrqu, char *extra) 196{ 197 u32 target_rate = wrqu->bitrate.value; 198 199 ieee->rate = target_rate / 100000; 200 return 0; 201} 202EXPORT_SYMBOL(rtllib_wx_set_rate); 203 204int rtllib_wx_get_rate(struct rtllib_device *ieee, 205 struct iw_request_info *info, 206 union iwreq_data *wrqu, char *extra) 207{ 208 u32 tmp_rate; 209 210 tmp_rate = tx_count_to_data_rate(ieee, 211 ieee->softmac_stats.CurrentShowTxate); 212 wrqu->bitrate.value = tmp_rate * 500000; 213 214 return 0; 215} 216EXPORT_SYMBOL(rtllib_wx_get_rate); 217 218int rtllib_wx_set_rts(struct rtllib_device *ieee, 219 struct iw_request_info *info, 220 union iwreq_data *wrqu, char *extra) 221{ 222 if (wrqu->rts.disabled || !wrqu->rts.fixed) { 223 ieee->rts = DEFAULT_RTS_THRESHOLD; 224 } else { 225 if (wrqu->rts.value < MIN_RTS_THRESHOLD || 226 wrqu->rts.value > MAX_RTS_THRESHOLD) 227 return -EINVAL; 228 ieee->rts = wrqu->rts.value; 229 } 230 return 0; 231} 232EXPORT_SYMBOL(rtllib_wx_set_rts); 233 234int rtllib_wx_get_rts(struct rtllib_device *ieee, 235 struct iw_request_info *info, 236 union iwreq_data *wrqu, char *extra) 237{ 238 wrqu->rts.value = ieee->rts; 239 wrqu->rts.fixed = 0; /* no auto select */ 240 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); 241 return 0; 242} 243EXPORT_SYMBOL(rtllib_wx_get_rts); 244 245int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, 246 union iwreq_data *wrqu, char *b) 247{ 248 int set_mode_status = 0; 249 250 rtllib_stop_scan_syncro(ieee); 251 mutex_lock(&ieee->wx_mutex); 252 switch (wrqu->mode) { 253 case IW_MODE_MONITOR: 254 case IW_MODE_INFRA: 255 break; 256 case IW_MODE_AUTO: 257 wrqu->mode = IW_MODE_INFRA; 258 break; 259 default: 260 set_mode_status = -EINVAL; 261 goto out; 262 } 263 264 if (wrqu->mode == ieee->iw_mode) 265 goto out; 266 267 if (wrqu->mode == IW_MODE_MONITOR) { 268 ieee->dev->type = ARPHRD_IEEE80211; 269 rtllib_enable_net_monitor_mode(ieee->dev, false); 270 } else { 271 ieee->dev->type = ARPHRD_ETHER; 272 if (ieee->iw_mode == IW_MODE_MONITOR) 273 rtllib_disable_net_monitor_mode(ieee->dev, false); 274 } 275 276 if (!ieee->proto_started) { 277 ieee->iw_mode = wrqu->mode; 278 } else { 279 rtllib_stop_protocol(ieee); 280 ieee->iw_mode = wrqu->mode; 281 rtllib_start_protocol(ieee); 282 } 283 284out: 285 mutex_unlock(&ieee->wx_mutex); 286 return set_mode_status; 287} 288EXPORT_SYMBOL(rtllib_wx_set_mode); 289 290void rtllib_wx_sync_scan_wq(void *data) 291{ 292 struct rtllib_device *ieee = container_of(data, struct rtllib_device, wx_sync_scan_wq); 293 short chan; 294 enum ht_extchnl_offset chan_offset = 0; 295 enum ht_channel_width bandwidth = 0; 296 int b40M = 0; 297 298 mutex_lock(&ieee->wx_mutex); 299 if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) { 300 rtllib_start_scan_syncro(ieee); 301 goto out; 302 } 303 304 chan = ieee->current_network.channel; 305 306 ieee->leisure_ps_leave(ieee->dev); 307 /* notify AP to be in PS mode */ 308 rtllib_sta_ps_send_null_frame(ieee, 1); 309 rtllib_sta_ps_send_null_frame(ieee, 1); 310 311 rtllib_stop_all_queues(ieee); 312 ieee->link_state = MAC80211_LINKED_SCANNING; 313 ieee->link_change(ieee->dev); 314 /* wait for ps packet to be kicked out successfully */ 315 msleep(50); 316 317 ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); 318 319 if (ieee->ht_info->current_ht_support && ieee->ht_info->enable_ht && 320 ieee->ht_info->cur_bw_40mhz) { 321 b40M = 1; 322 chan_offset = ieee->ht_info->CurSTAExtChnlOffset; 323 bandwidth = (enum ht_channel_width)ieee->ht_info->cur_bw_40mhz; 324 ieee->set_bw_mode_handler(ieee->dev, HT_CHANNEL_WIDTH_20, 325 HT_EXTCHNL_OFFSET_NO_EXT); 326 } 327 328 rtllib_start_scan_syncro(ieee); 329 330 if (b40M) { 331 if (chan_offset == HT_EXTCHNL_OFFSET_UPPER) 332 ieee->set_chan(ieee->dev, chan + 2); 333 else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER) 334 ieee->set_chan(ieee->dev, chan - 2); 335 else 336 ieee->set_chan(ieee->dev, chan); 337 ieee->set_bw_mode_handler(ieee->dev, bandwidth, chan_offset); 338 } else { 339 ieee->set_chan(ieee->dev, chan); 340 } 341 342 ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE); 343 344 ieee->link_state = MAC80211_LINKED; 345 ieee->link_change(ieee->dev); 346 347 /* Notify AP that I wake up again */ 348 rtllib_sta_ps_send_null_frame(ieee, 0); 349 350 if (ieee->link_detect_info.num_recv_bcn_in_period == 0 || 351 ieee->link_detect_info.num_recv_data_in_period == 0) { 352 ieee->link_detect_info.num_recv_bcn_in_period = 1; 353 ieee->link_detect_info.num_recv_data_in_period = 1; 354 } 355 rtllib_wake_all_queues(ieee); 356 357out: 358 mutex_unlock(&ieee->wx_mutex); 359} 360 361int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a, 362 union iwreq_data *wrqu, char *b) 363{ 364 int ret = 0; 365 366 if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) { 367 ret = -1; 368 goto out; 369 } 370 371 if (ieee->link_state == MAC80211_LINKED) { 372 schedule_work(&ieee->wx_sync_scan_wq); 373 /* intentionally forget to up sem */ 374 return 0; 375 } 376 377out: 378 return ret; 379} 380EXPORT_SYMBOL(rtllib_wx_set_scan); 381 382int rtllib_wx_set_essid(struct rtllib_device *ieee, 383 struct iw_request_info *a, 384 union iwreq_data *wrqu, char *extra) 385{ 386 int ret = 0, len; 387 short proto_started; 388 unsigned long flags; 389 390 rtllib_stop_scan_syncro(ieee); 391 mutex_lock(&ieee->wx_mutex); 392 393 proto_started = ieee->proto_started; 394 395 len = min_t(__u16, wrqu->essid.length, IW_ESSID_MAX_SIZE); 396 397 if (ieee->iw_mode == IW_MODE_MONITOR) { 398 ret = -1; 399 goto out; 400 } 401 402 if (proto_started) 403 rtllib_stop_protocol(ieee); 404 405 /* this is just to be sure that the GET wx callback 406 * has consistent infos. not needed otherwise 407 */ 408 spin_lock_irqsave(&ieee->lock, flags); 409 410 if (wrqu->essid.flags && wrqu->essid.length) { 411 strncpy(ieee->current_network.ssid, extra, len); 412 ieee->current_network.ssid_len = len; 413 ieee->cannot_notify = false; 414 ieee->ssid_set = 1; 415 } else { 416 ieee->ssid_set = 0; 417 ieee->current_network.ssid[0] = '\0'; 418 ieee->current_network.ssid_len = 0; 419 } 420 spin_unlock_irqrestore(&ieee->lock, flags); 421 422 if (proto_started) 423 rtllib_start_protocol(ieee); 424out: 425 mutex_unlock(&ieee->wx_mutex); 426 return ret; 427} 428EXPORT_SYMBOL(rtllib_wx_set_essid); 429 430int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a, 431 union iwreq_data *wrqu, char *b) 432{ 433 wrqu->mode = ieee->iw_mode; 434 return 0; 435} 436EXPORT_SYMBOL(rtllib_wx_get_mode); 437 438int rtllib_wx_get_name(struct rtllib_device *ieee, struct iw_request_info *info, 439 union iwreq_data *wrqu, char *extra) 440{ 441 const char *n = ieee->mode & (WIRELESS_MODE_N_24G) ? "n" : ""; 442 443 scnprintf(wrqu->name, sizeof(wrqu->name), "802.11bg%s", n); 444 return 0; 445} 446EXPORT_SYMBOL(rtllib_wx_get_name); 447 448/* this is mostly stolen from hostap */ 449int rtllib_wx_set_power(struct rtllib_device *ieee, 450 struct iw_request_info *info, 451 union iwreq_data *wrqu, char *extra) 452{ 453 int ret = 0; 454 455 if ((!ieee->sta_wake_up) || 456 (!ieee->enter_sleep_state) || 457 (!ieee->ps_is_queue_empty)) { 458 netdev_warn(ieee->dev, 459 "%s(): PS mode is tried to be use but driver missed a callback\n", 460 __func__); 461 return -1; 462 } 463 464 mutex_lock(&ieee->wx_mutex); 465 466 if (wrqu->power.disabled) { 467 ieee->ps = RTLLIB_PS_DISABLED; 468 goto exit; 469 } 470 if (wrqu->power.flags & IW_POWER_TIMEOUT) 471 ieee->ps_timeout = wrqu->power.value / 1000; 472 473 if (wrqu->power.flags & IW_POWER_PERIOD) 474 ieee->ps_period = wrqu->power.value / 1000; 475 476 switch (wrqu->power.flags & IW_POWER_MODE) { 477 case IW_POWER_UNICAST_R: 478 ieee->ps = RTLLIB_PS_UNICAST; 479 break; 480 case IW_POWER_MULTICAST_R: 481 ieee->ps = RTLLIB_PS_MBCAST; 482 break; 483 case IW_POWER_ALL_R: 484 ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST; 485 break; 486 487 case IW_POWER_ON: 488 break; 489 490 default: 491 ret = -EINVAL; 492 goto exit; 493 } 494exit: 495 mutex_unlock(&ieee->wx_mutex); 496 return ret; 497} 498EXPORT_SYMBOL(rtllib_wx_set_power); 499 500/* this is stolen from hostap */ 501int rtllib_wx_get_power(struct rtllib_device *ieee, 502 struct iw_request_info *info, 503 union iwreq_data *wrqu, char *extra) 504{ 505 mutex_lock(&ieee->wx_mutex); 506 507 if (ieee->ps == RTLLIB_PS_DISABLED) { 508 wrqu->power.disabled = 1; 509 goto exit; 510 } 511 512 wrqu->power.disabled = 0; 513 514 if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 515 wrqu->power.flags = IW_POWER_TIMEOUT; 516 wrqu->power.value = ieee->ps_timeout * 1000; 517 } else { 518 wrqu->power.flags = IW_POWER_PERIOD; 519 wrqu->power.value = ieee->ps_period * 1000; 520 } 521 522 if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) == 523 (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) 524 wrqu->power.flags |= IW_POWER_ALL_R; 525 else if (ieee->ps & RTLLIB_PS_MBCAST) 526 wrqu->power.flags |= IW_POWER_MULTICAST_R; 527 else 528 wrqu->power.flags |= IW_POWER_UNICAST_R; 529 530exit: 531 mutex_unlock(&ieee->wx_mutex); 532 return 0; 533} 534EXPORT_SYMBOL(rtllib_wx_get_power); 535