1/* 2 * This file contains our _wx handlers. Make sure you EXPORT_SYMBOL_GPL them 3 * 4 * Copyright (c) 2005, 2006 Johannes Berg <johannes@sipsolutions.net> 5 * Joseph Jezak <josejx@gentoo.org> 6 * Larry Finger <Larry.Finger@lwfinger.net> 7 * Danny van Dyk <kugelfang@gentoo.org> 8 * Michael Buesch <mbuesch@freenet.de> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of version 2 of the GNU General Public License as 12 * published by the Free Software Foundation. 13 * 14 * This program is distributed in the hope that it will be useful, but WITHOUT 15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 17 * more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * The full GNU General Public License is included in this distribution in the 24 * file called COPYING. 25 */ 26 27#include "ieee80211softmac_priv.h" 28 29#include <net/iw_handler.h> 30/* for is_broadcast_ether_addr and is_zero_ether_addr */ 31#include <linux/etherdevice.h> 32 33int 34ieee80211softmac_wx_trigger_scan(struct net_device *net_dev, 35 struct iw_request_info *info, 36 union iwreq_data *data, 37 char *extra) 38{ 39 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); 40 return ieee80211softmac_start_scan(sm); 41} 42EXPORT_SYMBOL_GPL(ieee80211softmac_wx_trigger_scan); 43 44 45/* if we're still scanning, return -EAGAIN so that userspace tools 46 * can get the complete scan results, otherwise return 0. */ 47int 48ieee80211softmac_wx_get_scan_results(struct net_device *net_dev, 49 struct iw_request_info *info, 50 union iwreq_data *data, 51 char *extra) 52{ 53 unsigned long flags; 54 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); 55 56 spin_lock_irqsave(&sm->lock, flags); 57 if (sm->scanning) { 58 spin_unlock_irqrestore(&sm->lock, flags); 59 return -EAGAIN; 60 } 61 spin_unlock_irqrestore(&sm->lock, flags); 62 return ieee80211_wx_get_scan(sm->ieee, info, data, extra); 63} 64EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_scan_results); 65 66int 67ieee80211softmac_wx_set_essid(struct net_device *net_dev, 68 struct iw_request_info *info, 69 union iwreq_data *data, 70 char *extra) 71{ 72 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); 73 struct ieee80211softmac_network *n; 74 struct ieee80211softmac_auth_queue_item *authptr; 75 int length = 0; 76 77 mutex_lock(&sm->associnfo.mutex); 78 79 /* Check if we're already associating to this or another network 80 * If it's another network, cancel and start over with our new network 81 * If it's our network, ignore the change, we're already doing it! 82 */ 83 if((sm->associnfo.associating || sm->associnfo.associated) && 84 (data->essid.flags && data->essid.length)) { 85 /* Get the associating network */ 86 n = ieee80211softmac_get_network_by_bssid(sm, sm->associnfo.bssid); 87 if(n && n->essid.len == data->essid.length && 88 !memcmp(n->essid.data, extra, n->essid.len)) { 89 dprintk(KERN_INFO PFX "Already associating or associated to "MAC_FMT"\n", 90 MAC_ARG(sm->associnfo.bssid)); 91 goto out; 92 } else { 93 dprintk(KERN_INFO PFX "Canceling existing associate request!\n"); 94 /* Cancel assoc work */ 95 cancel_delayed_work(&sm->associnfo.work); 96 /* We don't have to do this, but it's a little cleaner */ 97 list_for_each_entry(authptr, &sm->auth_queue, list) 98 cancel_delayed_work(&authptr->work); 99 sm->associnfo.bssvalid = 0; 100 sm->associnfo.bssfixed = 0; 101 flush_scheduled_work(); 102 sm->associnfo.associating = 0; 103 sm->associnfo.associated = 0; 104 } 105 } 106 107 108 sm->associnfo.static_essid = 0; 109 sm->associnfo.assoc_wait = 0; 110 111 if (data->essid.flags && data->essid.length) { 112 length = min((int)data->essid.length, IW_ESSID_MAX_SIZE); 113 if (length) { 114 memcpy(sm->associnfo.req_essid.data, extra, length); 115 sm->associnfo.static_essid = 1; 116 } 117 } 118 119 /* set our requested ESSID length. 120 * If applicable, we have already copied the data in */ 121 sm->associnfo.req_essid.len = length; 122 123 sm->associnfo.associating = 1; 124 /* queue lower level code to do work (if necessary) */ 125 schedule_delayed_work(&sm->associnfo.work, 0); 126out: 127 mutex_unlock(&sm->associnfo.mutex); 128 129 return 0; 130} 131EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_essid); 132 133int 134ieee80211softmac_wx_get_essid(struct net_device *net_dev, 135 struct iw_request_info *info, 136 union iwreq_data *data, 137 char *extra) 138{ 139 struct ieee80211softmac_device *sm = ieee80211_priv(net_dev); 140 141 mutex_lock(&sm->associnfo.mutex); 142 /* If all fails, return ANY (empty) */ 143 data->essid.length = 0; 144 data->essid.flags = 0; /* active */ 145 146 /* If we have a statically configured ESSID then return it */ 147 if (sm->associnfo.static_essid) { 148 data->essid.length = sm->associnfo.req_essid.len; 149 data->essid.flags = 1; /* active */ 150 memcpy(extra, sm->associnfo.req_essid.data, sm->associnfo.req_essid.len); 151 } 152 153 /* If we're associating/associated, return that */ 154 if (sm->associnfo.associated || sm->associnfo.associating) { 155 data->essid.length = sm->associnfo.associate_essid.len; 156 data->essid.flags = 1; /* active */ 157 memcpy(extra, sm->associnfo.associate_essid.data, sm->associnfo.associate_essid.len); 158 } 159 mutex_unlock(&sm->associnfo.mutex); 160 161 return 0; 162} 163EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_essid); 164 165int 166ieee80211softmac_wx_set_rate(struct net_device *net_dev, 167 struct iw_request_info *info, 168 union iwreq_data *data, 169 char *extra) 170{ 171 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); 172 struct ieee80211_device *ieee = mac->ieee; 173 unsigned long flags; 174 s32 in_rate = data->bitrate.value; 175 u8 rate; 176 int is_ofdm = 0; 177 int err = -EINVAL; 178 179 if (in_rate == -1) { 180 if (ieee->modulation & IEEE80211_OFDM_MODULATION) 181 in_rate = 24000000; 182 else 183 in_rate = 11000000; 184 } 185 186 switch (in_rate) { 187 case 1000000: 188 rate = IEEE80211_CCK_RATE_1MB; 189 break; 190 case 2000000: 191 rate = IEEE80211_CCK_RATE_2MB; 192 break; 193 case 5500000: 194 rate = IEEE80211_CCK_RATE_5MB; 195 break; 196 case 11000000: 197 rate = IEEE80211_CCK_RATE_11MB; 198 break; 199 case 6000000: 200 rate = IEEE80211_OFDM_RATE_6MB; 201 is_ofdm = 1; 202 break; 203 case 9000000: 204 rate = IEEE80211_OFDM_RATE_9MB; 205 is_ofdm = 1; 206 break; 207 case 12000000: 208 rate = IEEE80211_OFDM_RATE_12MB; 209 is_ofdm = 1; 210 break; 211 case 18000000: 212 rate = IEEE80211_OFDM_RATE_18MB; 213 is_ofdm = 1; 214 break; 215 case 24000000: 216 rate = IEEE80211_OFDM_RATE_24MB; 217 is_ofdm = 1; 218 break; 219 case 36000000: 220 rate = IEEE80211_OFDM_RATE_36MB; 221 is_ofdm = 1; 222 break; 223 case 48000000: 224 rate = IEEE80211_OFDM_RATE_48MB; 225 is_ofdm = 1; 226 break; 227 case 54000000: 228 rate = IEEE80211_OFDM_RATE_54MB; 229 is_ofdm = 1; 230 break; 231 default: 232 goto out; 233 } 234 235 spin_lock_irqsave(&mac->lock, flags); 236 237 /* Check if correct modulation for this PHY. */ 238 if (is_ofdm && !(ieee->modulation & IEEE80211_OFDM_MODULATION)) 239 goto out_unlock; 240 241 mac->txrates.user_rate = rate; 242 ieee80211softmac_recalc_txrates(mac); 243 err = 0; 244 245out_unlock: 246 spin_unlock_irqrestore(&mac->lock, flags); 247out: 248 return err; 249} 250EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_rate); 251 252int 253ieee80211softmac_wx_get_rate(struct net_device *net_dev, 254 struct iw_request_info *info, 255 union iwreq_data *data, 256 char *extra) 257{ 258 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); 259 unsigned long flags; 260 int err = -EINVAL; 261 262 spin_lock_irqsave(&mac->lock, flags); 263 264 if (unlikely(!mac->running)) { 265 err = -ENODEV; 266 goto out_unlock; 267 } 268 269 switch (mac->txrates.default_rate) { 270 case IEEE80211_CCK_RATE_1MB: 271 data->bitrate.value = 1000000; 272 break; 273 case IEEE80211_CCK_RATE_2MB: 274 data->bitrate.value = 2000000; 275 break; 276 case IEEE80211_CCK_RATE_5MB: 277 data->bitrate.value = 5500000; 278 break; 279 case IEEE80211_CCK_RATE_11MB: 280 data->bitrate.value = 11000000; 281 break; 282 case IEEE80211_OFDM_RATE_6MB: 283 data->bitrate.value = 6000000; 284 break; 285 case IEEE80211_OFDM_RATE_9MB: 286 data->bitrate.value = 9000000; 287 break; 288 case IEEE80211_OFDM_RATE_12MB: 289 data->bitrate.value = 12000000; 290 break; 291 case IEEE80211_OFDM_RATE_18MB: 292 data->bitrate.value = 18000000; 293 break; 294 case IEEE80211_OFDM_RATE_24MB: 295 data->bitrate.value = 24000000; 296 break; 297 case IEEE80211_OFDM_RATE_36MB: 298 data->bitrate.value = 36000000; 299 break; 300 case IEEE80211_OFDM_RATE_48MB: 301 data->bitrate.value = 48000000; 302 break; 303 case IEEE80211_OFDM_RATE_54MB: 304 data->bitrate.value = 54000000; 305 break; 306 default: 307 assert(0); 308 goto out_unlock; 309 } 310 err = 0; 311out_unlock: 312 spin_unlock_irqrestore(&mac->lock, flags); 313 314 return err; 315} 316EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_rate); 317 318int 319ieee80211softmac_wx_get_wap(struct net_device *net_dev, 320 struct iw_request_info *info, 321 union iwreq_data *data, 322 char *extra) 323{ 324 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); 325 int err = 0; 326 327 mutex_lock(&mac->associnfo.mutex); 328 if (mac->associnfo.bssvalid) 329 memcpy(data->ap_addr.sa_data, mac->associnfo.bssid, ETH_ALEN); 330 else 331 memset(data->ap_addr.sa_data, 0xff, ETH_ALEN); 332 data->ap_addr.sa_family = ARPHRD_ETHER; 333 mutex_unlock(&mac->associnfo.mutex); 334 335 return err; 336} 337EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_wap); 338 339int 340ieee80211softmac_wx_set_wap(struct net_device *net_dev, 341 struct iw_request_info *info, 342 union iwreq_data *data, 343 char *extra) 344{ 345 struct ieee80211softmac_device *mac = ieee80211_priv(net_dev); 346 347 /* sanity check */ 348 if (data->ap_addr.sa_family != ARPHRD_ETHER) { 349 return -EINVAL; 350 } 351 352 mutex_lock(&mac->associnfo.mutex); 353 if (is_broadcast_ether_addr(data->ap_addr.sa_data)) { 354 /* the bssid we have is not to be fixed any longer, 355 * and we should reassociate to the best AP. */ 356 mac->associnfo.bssfixed = 0; 357 /* force reassociation */ 358 mac->associnfo.bssvalid = 0; 359 if (mac->associnfo.associated) 360 schedule_delayed_work(&mac->associnfo.work, 0); 361 } else if (is_zero_ether_addr(data->ap_addr.sa_data)) { 362 /* the bssid we have is no longer fixed */ 363 mac->associnfo.bssfixed = 0; 364 } else { 365 if (!memcmp(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN)) { 366 if (mac->associnfo.associating || mac->associnfo.associated) { 367 /* bssid unchanged and associated or associating - just return */ 368 goto out; 369 } 370 } else { 371 /* copy new value in data->ap_addr.sa_data to bssid */ 372 memcpy(mac->associnfo.bssid, data->ap_addr.sa_data, ETH_ALEN); 373 } 374 /* tell the other code that this bssid should be used no matter what */ 375 mac->associnfo.bssfixed = 1; 376 /* queue associate if new bssid or (old one again and not associated) */ 377 schedule_delayed_work(&mac->associnfo.work, 0); 378 } 379 380 out: 381 mutex_unlock(&mac->associnfo.mutex); 382 383 return 0; 384} 385EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_wap); 386 387int 388ieee80211softmac_wx_set_genie(struct net_device *dev, 389 struct iw_request_info *info, 390 union iwreq_data *wrqu, 391 char *extra) 392{ 393 struct ieee80211softmac_device *mac = ieee80211_priv(dev); 394 unsigned long flags; 395 int err = 0; 396 char *buf; 397 int i; 398 399 mutex_lock(&mac->associnfo.mutex); 400 spin_lock_irqsave(&mac->lock, flags); 401 /* bleh. shouldn't be locked for that kmalloc... */ 402 403 if (wrqu->data.length) { 404 if ((wrqu->data.length < 2) || (extra[1]+2 != wrqu->data.length)) { 405 /* this is an IE, so the length must be 406 * correct. Is it possible though that 407 * more than one IE is passed in? 408 */ 409 err = -EINVAL; 410 goto out; 411 } 412 if (mac->wpa.IEbuflen <= wrqu->data.length) { 413 buf = kmalloc(wrqu->data.length, GFP_ATOMIC); 414 if (!buf) { 415 err = -ENOMEM; 416 goto out; 417 } 418 kfree(mac->wpa.IE); 419 mac->wpa.IE = buf; 420 mac->wpa.IEbuflen = wrqu->data.length; 421 } 422 memcpy(mac->wpa.IE, extra, wrqu->data.length); 423 dprintk(KERN_INFO PFX "generic IE set to "); 424 for (i=0;i<wrqu->data.length;i++) 425 dprintk("%.2x", (u8)mac->wpa.IE[i]); 426 dprintk("\n"); 427 mac->wpa.IElen = wrqu->data.length; 428 } else { 429 kfree(mac->wpa.IE); 430 mac->wpa.IE = NULL; 431 mac->wpa.IElen = 0; 432 mac->wpa.IEbuflen = 0; 433 } 434 435 out: 436 spin_unlock_irqrestore(&mac->lock, flags); 437 mutex_unlock(&mac->associnfo.mutex); 438 439 return err; 440} 441EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_genie); 442 443int 444ieee80211softmac_wx_get_genie(struct net_device *dev, 445 struct iw_request_info *info, 446 union iwreq_data *wrqu, 447 char *extra) 448{ 449 struct ieee80211softmac_device *mac = ieee80211_priv(dev); 450 unsigned long flags; 451 int err = 0; 452 int space = wrqu->data.length; 453 454 mutex_lock(&mac->associnfo.mutex); 455 spin_lock_irqsave(&mac->lock, flags); 456 457 wrqu->data.length = 0; 458 459 if (mac->wpa.IE && mac->wpa.IElen) { 460 wrqu->data.length = mac->wpa.IElen; 461 if (mac->wpa.IElen <= space) 462 memcpy(extra, mac->wpa.IE, mac->wpa.IElen); 463 else 464 err = -E2BIG; 465 } 466 spin_unlock_irqrestore(&mac->lock, flags); 467 mutex_unlock(&mac->associnfo.mutex); 468 469 return err; 470} 471EXPORT_SYMBOL_GPL(ieee80211softmac_wx_get_genie); 472 473int 474ieee80211softmac_wx_set_mlme(struct net_device *dev, 475 struct iw_request_info *info, 476 union iwreq_data *wrqu, 477 char *extra) 478{ 479 struct ieee80211softmac_device *mac = ieee80211_priv(dev); 480 struct iw_mlme *mlme = (struct iw_mlme *)extra; 481 u16 reason = cpu_to_le16(mlme->reason_code); 482 struct ieee80211softmac_network *net; 483 int err = -EINVAL; 484 485 mutex_lock(&mac->associnfo.mutex); 486 487 if (memcmp(mac->associnfo.bssid, mlme->addr.sa_data, ETH_ALEN)) { 488 printk(KERN_DEBUG PFX "wx_set_mlme: requested operation on net we don't use\n"); 489 goto out; 490 } 491 492 switch (mlme->cmd) { 493 case IW_MLME_DEAUTH: 494 net = ieee80211softmac_get_network_by_bssid_locked(mac, mlme->addr.sa_data); 495 if (!net) { 496 printk(KERN_DEBUG PFX "wx_set_mlme: we should know the net here...\n"); 497 goto out; 498 } 499 err = ieee80211softmac_deauth_req(mac, net, reason); 500 goto out; 501 case IW_MLME_DISASSOC: 502 ieee80211softmac_send_disassoc_req(mac, reason); 503 mac->associnfo.associated = 0; 504 mac->associnfo.associating = 0; 505 err = 0; 506 goto out; 507 default: 508 err = -EOPNOTSUPP; 509 } 510 511out: 512 mutex_unlock(&mac->associnfo.mutex); 513 514 return err; 515} 516EXPORT_SYMBOL_GPL(ieee80211softmac_wx_set_mlme); 517