1/* IEEE 802.11 SoftMAC layer 2 * Copyright (c) 2005 Andrea Merello <andrea.merello@gmail.com> 3 * 4 * Mostly extracted from the rtl8180-sa2400 driver for the 5 * in-kernel generic ieee802.11 stack. 6 * 7 * Some pieces of code might be stolen from ipw2100 driver 8 * copyright of who own it's copyright ;-) 9 * 10 * PS wx handler mostly stolen from hostap, copyright who 11 * own it's copyright ;-) 12 * 13 * released under the GPL 14 */ 15 16 17#include <linux/etherdevice.h> 18 19#include "rtllib.h" 20#include "dot11d.h" 21/* FIXME: add A freqs */ 22 23const long rtllib_wlan_frequencies[] = { 24 2412, 2417, 2422, 2427, 25 2432, 2437, 2442, 2447, 26 2452, 2457, 2462, 2467, 27 2472, 2484 28}; 29EXPORT_SYMBOL(rtllib_wlan_frequencies); 30 31 32int rtllib_wx_set_freq(struct rtllib_device *ieee, struct iw_request_info *a, 33 union iwreq_data *wrqu, char *b) 34{ 35 int ret; 36 struct iw_freq *fwrq = &wrqu->freq; 37 38 down(&ieee->wx_sem); 39 40 if (ieee->iw_mode == IW_MODE_INFRA) { 41 ret = 0; 42 goto out; 43 } 44 45 /* if setting by freq convert to channel */ 46 if (fwrq->e == 1) { 47 if ((fwrq->m >= (int) 2.412e8 && 48 fwrq->m <= (int) 2.487e8)) { 49 int f = fwrq->m / 100000; 50 int c = 0; 51 52 while ((c < 14) && (f != rtllib_wlan_frequencies[c])) 53 c++; 54 55 /* hack to fall through */ 56 fwrq->e = 0; 57 fwrq->m = c + 1; 58 } 59 } 60 61 if (fwrq->e > 0 || fwrq->m > 14 || fwrq->m < 1) { 62 ret = -EOPNOTSUPP; 63 goto out; 64 65 } else { /* Set the channel */ 66 67 if (ieee->active_channel_map[fwrq->m] != 1) { 68 ret = -EINVAL; 69 goto out; 70 } 71 ieee->current_network.channel = fwrq->m; 72 ieee->set_chan(ieee->dev, ieee->current_network.channel); 73 74 if (ieee->iw_mode == IW_MODE_ADHOC || 75 ieee->iw_mode == IW_MODE_MASTER) 76 if (ieee->state == RTLLIB_LINKED) { 77 rtllib_stop_send_beacons(ieee); 78 rtllib_start_send_beacons(ieee); 79 } 80 } 81 82 ret = 0; 83out: 84 up(&ieee->wx_sem); 85 return ret; 86} 87EXPORT_SYMBOL(rtllib_wx_set_freq); 88 89 90int rtllib_wx_get_freq(struct rtllib_device *ieee, 91 struct iw_request_info *a, 92 union iwreq_data *wrqu, char *b) 93{ 94 struct iw_freq *fwrq = &wrqu->freq; 95 96 if (ieee->current_network.channel == 0) 97 return -1; 98 fwrq->m = rtllib_wlan_frequencies[ieee->current_network.channel-1] * 99 100000; 100 fwrq->e = 1; 101 return 0; 102} 103EXPORT_SYMBOL(rtllib_wx_get_freq); 104 105int rtllib_wx_get_wap(struct rtllib_device *ieee, 106 struct iw_request_info *info, 107 union iwreq_data *wrqu, char *extra) 108{ 109 unsigned long flags; 110 111 wrqu->ap_addr.sa_family = ARPHRD_ETHER; 112 113 if (ieee->iw_mode == IW_MODE_MONITOR) 114 return -1; 115 116 /* We want avoid to give to the user inconsistent infos*/ 117 spin_lock_irqsave(&ieee->lock, flags); 118 119 if (ieee->state != RTLLIB_LINKED && 120 ieee->state != RTLLIB_LINKED_SCANNING && 121 ieee->wap_set == 0) 122 123 memset(wrqu->ap_addr.sa_data, 0, ETH_ALEN); 124 else 125 memcpy(wrqu->ap_addr.sa_data, 126 ieee->current_network.bssid, ETH_ALEN); 127 128 spin_unlock_irqrestore(&ieee->lock, flags); 129 130 return 0; 131} 132EXPORT_SYMBOL(rtllib_wx_get_wap); 133 134 135int rtllib_wx_set_wap(struct rtllib_device *ieee, 136 struct iw_request_info *info, 137 union iwreq_data *awrq, 138 char *extra) 139{ 140 141 int ret = 0; 142 unsigned long flags; 143 144 short ifup = ieee->proto_started; 145 struct sockaddr *temp = (struct sockaddr *)awrq; 146 147 rtllib_stop_scan_syncro(ieee); 148 149 down(&ieee->wx_sem); 150 /* use ifconfig hw ether */ 151 if (ieee->iw_mode == IW_MODE_MASTER) { 152 ret = -1; 153 goto out; 154 } 155 156 if (temp->sa_family != ARPHRD_ETHER) { 157 ret = -EINVAL; 158 goto out; 159 } 160 161 if (is_zero_ether_addr(temp->sa_data)) { 162 spin_lock_irqsave(&ieee->lock, flags); 163 memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN); 164 ieee->wap_set = 0; 165 spin_unlock_irqrestore(&ieee->lock, flags); 166 ret = -1; 167 goto out; 168 } 169 170 171 if (ifup) 172 rtllib_stop_protocol(ieee, true); 173 174 /* just to avoid to give inconsistent infos in the 175 * get wx method. not really needed otherwise 176 */ 177 spin_lock_irqsave(&ieee->lock, flags); 178 179 ieee->cannot_notify = false; 180 memcpy(ieee->current_network.bssid, temp->sa_data, ETH_ALEN); 181 ieee->wap_set = !is_zero_ether_addr(temp->sa_data); 182 183 spin_unlock_irqrestore(&ieee->lock, flags); 184 185 if (ifup) 186 rtllib_start_protocol(ieee); 187out: 188 up(&ieee->wx_sem); 189 return ret; 190} 191EXPORT_SYMBOL(rtllib_wx_set_wap); 192 193int rtllib_wx_get_essid(struct rtllib_device *ieee, struct iw_request_info *a, 194 union iwreq_data *wrqu, char *b) 195{ 196 int len, ret = 0; 197 unsigned long flags; 198 199 if (ieee->iw_mode == IW_MODE_MONITOR) 200 return -1; 201 202 /* We want avoid to give to the user inconsistent infos*/ 203 spin_lock_irqsave(&ieee->lock, flags); 204 205 if (ieee->current_network.ssid[0] == '\0' || 206 ieee->current_network.ssid_len == 0) { 207 ret = -1; 208 goto out; 209 } 210 211 if (ieee->state != RTLLIB_LINKED && 212 ieee->state != RTLLIB_LINKED_SCANNING && 213 ieee->ssid_set == 0) { 214 ret = -1; 215 goto out; 216 } 217 len = ieee->current_network.ssid_len; 218 wrqu->essid.length = len; 219 strncpy(b, ieee->current_network.ssid, len); 220 wrqu->essid.flags = 1; 221 222out: 223 spin_unlock_irqrestore(&ieee->lock, flags); 224 225 return ret; 226 227} 228EXPORT_SYMBOL(rtllib_wx_get_essid); 229 230int rtllib_wx_set_rate(struct rtllib_device *ieee, 231 struct iw_request_info *info, 232 union iwreq_data *wrqu, char *extra) 233{ 234 235 u32 target_rate = wrqu->bitrate.value; 236 237 ieee->rate = target_rate/100000; 238 return 0; 239} 240EXPORT_SYMBOL(rtllib_wx_set_rate); 241 242int rtllib_wx_get_rate(struct rtllib_device *ieee, 243 struct iw_request_info *info, 244 union iwreq_data *wrqu, char *extra) 245{ 246 u32 tmp_rate = 0; 247 248 tmp_rate = TxCountToDataRate(ieee, 249 ieee->softmac_stats.CurrentShowTxate); 250 wrqu->bitrate.value = tmp_rate * 500000; 251 252 return 0; 253} 254EXPORT_SYMBOL(rtllib_wx_get_rate); 255 256 257int rtllib_wx_set_rts(struct rtllib_device *ieee, 258 struct iw_request_info *info, 259 union iwreq_data *wrqu, char *extra) 260{ 261 if (wrqu->rts.disabled || !wrqu->rts.fixed) 262 ieee->rts = DEFAULT_RTS_THRESHOLD; 263 else { 264 if (wrqu->rts.value < MIN_RTS_THRESHOLD || 265 wrqu->rts.value > MAX_RTS_THRESHOLD) 266 return -EINVAL; 267 ieee->rts = wrqu->rts.value; 268 } 269 return 0; 270} 271EXPORT_SYMBOL(rtllib_wx_set_rts); 272 273int rtllib_wx_get_rts(struct rtllib_device *ieee, 274 struct iw_request_info *info, 275 union iwreq_data *wrqu, char *extra) 276{ 277 wrqu->rts.value = ieee->rts; 278 wrqu->rts.fixed = 0; /* no auto select */ 279 wrqu->rts.disabled = (wrqu->rts.value == DEFAULT_RTS_THRESHOLD); 280 return 0; 281} 282EXPORT_SYMBOL(rtllib_wx_get_rts); 283 284int rtllib_wx_set_mode(struct rtllib_device *ieee, struct iw_request_info *a, 285 union iwreq_data *wrqu, char *b) 286{ 287 int set_mode_status = 0; 288 289 rtllib_stop_scan_syncro(ieee); 290 down(&ieee->wx_sem); 291 switch (wrqu->mode) { 292 case IW_MODE_MONITOR: 293 case IW_MODE_ADHOC: 294 case IW_MODE_INFRA: 295 break; 296 case IW_MODE_AUTO: 297 wrqu->mode = IW_MODE_INFRA; 298 break; 299 default: 300 set_mode_status = -EINVAL; 301 goto out; 302 } 303 304 if (wrqu->mode == ieee->iw_mode) 305 goto out; 306 307 if (wrqu->mode == IW_MODE_MONITOR) { 308 ieee->dev->type = ARPHRD_IEEE80211; 309 rtllib_EnableNetMonitorMode(ieee->dev, false); 310 } else { 311 ieee->dev->type = ARPHRD_ETHER; 312 if (ieee->iw_mode == IW_MODE_MONITOR) 313 rtllib_DisableNetMonitorMode(ieee->dev, false); 314 } 315 316 if (!ieee->proto_started) { 317 ieee->iw_mode = wrqu->mode; 318 } else { 319 rtllib_stop_protocol(ieee, true); 320 ieee->iw_mode = wrqu->mode; 321 rtllib_start_protocol(ieee); 322 } 323 324out: 325 up(&ieee->wx_sem); 326 return set_mode_status; 327} 328EXPORT_SYMBOL(rtllib_wx_set_mode); 329 330void rtllib_wx_sync_scan_wq(void *data) 331{ 332 struct rtllib_device *ieee = container_of_work_rsl(data, 333 struct rtllib_device, wx_sync_scan_wq); 334 short chan; 335 enum ht_extchnl_offset chan_offset = 0; 336 enum ht_channel_width bandwidth = 0; 337 int b40M = 0; 338 339 if (!(ieee->softmac_features & IEEE_SOFTMAC_SCAN)) { 340 rtllib_start_scan_syncro(ieee, 0); 341 goto out; 342 } 343 344 chan = ieee->current_network.channel; 345 346 if (ieee->LeisurePSLeave) 347 ieee->LeisurePSLeave(ieee->dev); 348 /* notify AP to be in PS mode */ 349 rtllib_sta_ps_send_null_frame(ieee, 1); 350 rtllib_sta_ps_send_null_frame(ieee, 1); 351 352 rtllib_stop_all_queues(ieee); 353 354 if (ieee->data_hard_stop) 355 ieee->data_hard_stop(ieee->dev); 356 rtllib_stop_send_beacons(ieee); 357 ieee->state = RTLLIB_LINKED_SCANNING; 358 ieee->link_change(ieee->dev); 359 /* wait for ps packet to be kicked out successfully */ 360 msleep(50); 361 362 if (ieee->ScanOperationBackupHandler) 363 ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_BACKUP); 364 365 if (ieee->pHTInfo->bCurrentHTSupport && ieee->pHTInfo->bEnableHT && 366 ieee->pHTInfo->bCurBW40MHz) { 367 b40M = 1; 368 chan_offset = ieee->pHTInfo->CurSTAExtChnlOffset; 369 bandwidth = (enum ht_channel_width)ieee->pHTInfo->bCurBW40MHz; 370 RT_TRACE(COMP_DBG, "Scan in 40M, force to 20M first:%d, %d\n", 371 chan_offset, bandwidth); 372 ieee->SetBWModeHandler(ieee->dev, HT_CHANNEL_WIDTH_20, 373 HT_EXTCHNL_OFFSET_NO_EXT); 374 } 375 376 rtllib_start_scan_syncro(ieee, 0); 377 378 if (b40M) { 379 RT_TRACE(COMP_DBG, "Scan in 20M, back to 40M\n"); 380 if (chan_offset == HT_EXTCHNL_OFFSET_UPPER) 381 ieee->set_chan(ieee->dev, chan + 2); 382 else if (chan_offset == HT_EXTCHNL_OFFSET_LOWER) 383 ieee->set_chan(ieee->dev, chan - 2); 384 else 385 ieee->set_chan(ieee->dev, chan); 386 ieee->SetBWModeHandler(ieee->dev, bandwidth, chan_offset); 387 } else { 388 ieee->set_chan(ieee->dev, chan); 389 } 390 391 if (ieee->ScanOperationBackupHandler) 392 ieee->ScanOperationBackupHandler(ieee->dev, SCAN_OPT_RESTORE); 393 394 ieee->state = RTLLIB_LINKED; 395 ieee->link_change(ieee->dev); 396 397 /* Notify AP that I wake up again */ 398 rtllib_sta_ps_send_null_frame(ieee, 0); 399 400 if (ieee->LinkDetectInfo.NumRecvBcnInPeriod == 0 || 401 ieee->LinkDetectInfo.NumRecvDataInPeriod == 0) { 402 ieee->LinkDetectInfo.NumRecvBcnInPeriod = 1; 403 ieee->LinkDetectInfo.NumRecvDataInPeriod = 1; 404 } 405 406 if (ieee->data_hard_resume) 407 ieee->data_hard_resume(ieee->dev); 408 409 if (ieee->iw_mode == IW_MODE_ADHOC || ieee->iw_mode == IW_MODE_MASTER) 410 rtllib_start_send_beacons(ieee); 411 412 rtllib_wake_all_queues(ieee); 413 414out: 415 up(&ieee->wx_sem); 416 417} 418 419int rtllib_wx_set_scan(struct rtllib_device *ieee, struct iw_request_info *a, 420 union iwreq_data *wrqu, char *b) 421{ 422 int ret = 0; 423 424 down(&ieee->wx_sem); 425 426 if (ieee->iw_mode == IW_MODE_MONITOR || !(ieee->proto_started)) { 427 ret = -1; 428 goto out; 429 } 430 431 if (ieee->state == RTLLIB_LINKED) { 432 queue_work_rsl(ieee->wq, &ieee->wx_sync_scan_wq); 433 /* intentionally forget to up sem */ 434 return 0; 435 } 436 437out: 438 up(&ieee->wx_sem); 439 return ret; 440} 441EXPORT_SYMBOL(rtllib_wx_set_scan); 442 443int rtllib_wx_set_essid(struct rtllib_device *ieee, 444 struct iw_request_info *a, 445 union iwreq_data *wrqu, char *extra) 446{ 447 448 int ret = 0, len, i; 449 short proto_started; 450 unsigned long flags; 451 452 rtllib_stop_scan_syncro(ieee); 453 down(&ieee->wx_sem); 454 455 proto_started = ieee->proto_started; 456 457 len = (wrqu->essid.length < IW_ESSID_MAX_SIZE) ? wrqu->essid.length : 458 IW_ESSID_MAX_SIZE; 459 460 if (len > IW_ESSID_MAX_SIZE) { 461 ret = -E2BIG; 462 goto out; 463 } 464 465 if (ieee->iw_mode == IW_MODE_MONITOR) { 466 ret = -1; 467 goto out; 468 } 469 470 for (i = 0; i < len; i++) { 471 if (extra[i] < 0) { 472 ret = -1; 473 goto out; 474 } 475 } 476 477 if (proto_started) 478 rtllib_stop_protocol(ieee, true); 479 480 481 /* this is just to be sure that the GET wx callback 482 * has consistent infos. not needed otherwise 483 */ 484 spin_lock_irqsave(&ieee->lock, flags); 485 486 if (wrqu->essid.flags && wrqu->essid.length) { 487 strncpy(ieee->current_network.ssid, extra, len); 488 ieee->current_network.ssid_len = len; 489 ieee->cannot_notify = false; 490 ieee->ssid_set = 1; 491 } else { 492 ieee->ssid_set = 0; 493 ieee->current_network.ssid[0] = '\0'; 494 ieee->current_network.ssid_len = 0; 495 } 496 spin_unlock_irqrestore(&ieee->lock, flags); 497 498 if (proto_started) 499 rtllib_start_protocol(ieee); 500out: 501 up(&ieee->wx_sem); 502 return ret; 503} 504EXPORT_SYMBOL(rtllib_wx_set_essid); 505 506int rtllib_wx_get_mode(struct rtllib_device *ieee, struct iw_request_info *a, 507 union iwreq_data *wrqu, char *b) 508{ 509 wrqu->mode = ieee->iw_mode; 510 return 0; 511} 512EXPORT_SYMBOL(rtllib_wx_get_mode); 513 514int rtllib_wx_set_rawtx(struct rtllib_device *ieee, 515 struct iw_request_info *info, 516 union iwreq_data *wrqu, char *extra) 517{ 518 519 int *parms = (int *)extra; 520 int enable = (parms[0] > 0); 521 short prev = ieee->raw_tx; 522 523 down(&ieee->wx_sem); 524 525 if (enable) 526 ieee->raw_tx = 1; 527 else 528 ieee->raw_tx = 0; 529 530 printk(KERN_INFO"raw TX is %s\n", 531 ieee->raw_tx ? "enabled" : "disabled"); 532 533 if (ieee->iw_mode == IW_MODE_MONITOR) { 534 if (prev == 0 && ieee->raw_tx) { 535 if (ieee->data_hard_resume) 536 ieee->data_hard_resume(ieee->dev); 537 538 netif_carrier_on(ieee->dev); 539 } 540 541 if (prev && ieee->raw_tx == 1) 542 netif_carrier_off(ieee->dev); 543 } 544 545 up(&ieee->wx_sem); 546 547 return 0; 548} 549EXPORT_SYMBOL(rtllib_wx_set_rawtx); 550 551int rtllib_wx_get_name(struct rtllib_device *ieee, 552 struct iw_request_info *info, 553 union iwreq_data *wrqu, char *extra) 554{ 555 strcpy(wrqu->name, "802.11"); 556 557 if (ieee->modulation & RTLLIB_CCK_MODULATION) 558 strcat(wrqu->name, "b"); 559 if (ieee->modulation & RTLLIB_OFDM_MODULATION) 560 strcat(wrqu->name, "g"); 561 if (ieee->mode & (IEEE_N_24G | IEEE_N_5G)) 562 strcat(wrqu->name, "n"); 563 return 0; 564} 565EXPORT_SYMBOL(rtllib_wx_get_name); 566 567 568/* this is mostly stolen from hostap */ 569int rtllib_wx_set_power(struct rtllib_device *ieee, 570 struct iw_request_info *info, 571 union iwreq_data *wrqu, char *extra) 572{ 573 int ret = 0; 574 575 if ((!ieee->sta_wake_up) || 576 (!ieee->enter_sleep_state) || 577 (!ieee->ps_is_queue_empty)) { 578 RTLLIB_DEBUG(RTLLIB_DL_ERR, "%s(): PS mode is tried to be use " 579 "but driver missed a callback\n\n", __func__); 580 return -1; 581 } 582 583 down(&ieee->wx_sem); 584 585 if (wrqu->power.disabled) { 586 RT_TRACE(COMP_DBG, "===>%s(): power disable\n", __func__); 587 ieee->ps = RTLLIB_PS_DISABLED; 588 goto exit; 589 } 590 if (wrqu->power.flags & IW_POWER_TIMEOUT) { 591 ieee->ps_timeout = wrqu->power.value / 1000; 592 RT_TRACE(COMP_DBG, "===>%s():ps_timeout is %d\n", __func__, 593 ieee->ps_timeout); 594 } 595 596 if (wrqu->power.flags & IW_POWER_PERIOD) 597 ieee->ps_period = wrqu->power.value / 1000; 598 599 switch (wrqu->power.flags & IW_POWER_MODE) { 600 case IW_POWER_UNICAST_R: 601 ieee->ps = RTLLIB_PS_UNICAST; 602 break; 603 case IW_POWER_MULTICAST_R: 604 ieee->ps = RTLLIB_PS_MBCAST; 605 break; 606 case IW_POWER_ALL_R: 607 ieee->ps = RTLLIB_PS_UNICAST | RTLLIB_PS_MBCAST; 608 break; 609 610 case IW_POWER_ON: 611 break; 612 613 default: 614 ret = -EINVAL; 615 goto exit; 616 617 } 618exit: 619 up(&ieee->wx_sem); 620 return ret; 621 622} 623EXPORT_SYMBOL(rtllib_wx_set_power); 624 625/* this is stolen from hostap */ 626int rtllib_wx_get_power(struct rtllib_device *ieee, 627 struct iw_request_info *info, 628 union iwreq_data *wrqu, char *extra) 629{ 630 down(&ieee->wx_sem); 631 632 if (ieee->ps == RTLLIB_PS_DISABLED) { 633 wrqu->power.disabled = 1; 634 goto exit; 635 } 636 637 wrqu->power.disabled = 0; 638 639 if ((wrqu->power.flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { 640 wrqu->power.flags = IW_POWER_TIMEOUT; 641 wrqu->power.value = ieee->ps_timeout * 1000; 642 } else { 643 wrqu->power.flags = IW_POWER_PERIOD; 644 wrqu->power.value = ieee->ps_period * 1000; 645 } 646 647 if ((ieee->ps & (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) == 648 (RTLLIB_PS_MBCAST | RTLLIB_PS_UNICAST)) 649 wrqu->power.flags |= IW_POWER_ALL_R; 650 else if (ieee->ps & RTLLIB_PS_MBCAST) 651 wrqu->power.flags |= IW_POWER_MULTICAST_R; 652 else 653 wrqu->power.flags |= IW_POWER_UNICAST_R; 654 655exit: 656 up(&ieee->wx_sem); 657 return 0; 658 659} 660EXPORT_SYMBOL(rtllib_wx_get_power); 661