wlcompat.c 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040
  1. /*
  2. * wlcompat.c
  3. *
  4. * Copyright (C) 2005 Mike Baker
  5. * Copyright (C) 2005-2007 Felix Fietkau <[email protected]>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  20. *
  21. */
  22. #include <linux/config.h>
  23. #include <linux/module.h>
  24. #include <linux/moduleparam.h>
  25. #include <linux/init.h>
  26. #include <linux/if_arp.h>
  27. #include <linux/wireless.h>
  28. #include <linux/timer.h>
  29. #include <linux/delay.h>
  30. #include <linux/random.h>
  31. #include <net/iw_handler.h>
  32. #include <asm/uaccess.h>
  33. #include <typedefs.h>
  34. #include <bcmutils.h>
  35. #include <wlioctl.h>
  36. char buf[WLC_IOCTL_MAXLEN];
  37. static struct net_device *dev;
  38. #ifndef DEBUG
  39. static int random = 1;
  40. #endif
  41. #ifndef WL_WEXT
  42. static struct iw_statistics wstats;
  43. static int last_mode = -1;
  44. static int scan_cur = 0;
  45. /* The frequency of each channel in MHz */
  46. const long channel_frequency[] = {
  47. 2412, 2417, 2422, 2427, 2432, 2437, 2442,
  48. 2447, 2452, 2457, 2462, 2467, 2472, 2484
  49. };
  50. #define NUM_CHANNELS ( sizeof(channel_frequency) / sizeof(channel_frequency[0]) )
  51. #endif
  52. #define SCAN_RETRY_MAX 5
  53. #define RNG_POLL_FREQ 1
  54. typedef struct internal_wsec_key {
  55. uint8 index; // 0x00
  56. uint8 unknown_1; // 0x01
  57. uint8 type; // 0x02
  58. uint8 unknown_2[7]; // 0x03
  59. uint8 len; // 0x0a
  60. uint8 pad[3];
  61. char data[32]; // 0x0e
  62. } wkey;
  63. #ifdef DEBUG
  64. void print_buffer(int len, unsigned char *buf);
  65. #endif
  66. static int wl_ioctl(struct net_device *dev, int cmd, void *buf, int len)
  67. {
  68. mm_segment_t old_fs = get_fs();
  69. struct ifreq ifr;
  70. int ret;
  71. wl_ioctl_t ioc;
  72. ioc.cmd = cmd;
  73. ioc.buf = buf;
  74. ioc.len = len;
  75. strncpy(ifr.ifr_name, dev->name, IFNAMSIZ);
  76. ifr.ifr_data = (caddr_t) &ioc;
  77. set_fs(KERNEL_DS);
  78. ret = dev->do_ioctl(dev,&ifr,SIOCDEVPRIVATE);
  79. set_fs (old_fs);
  80. return ret;
  81. }
  82. #if !defined(DEBUG) || !defined(WL_WEXT)
  83. static int
  84. wl_iovar_getbuf(struct net_device *dev, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
  85. {
  86. int err;
  87. uint namelen;
  88. uint iolen;
  89. namelen = strlen(iovar) + 1; /* length of iovar name plus null */
  90. iolen = namelen + paramlen;
  91. /* check for overflow */
  92. if (iolen > buflen)
  93. return (BCME_BUFTOOSHORT);
  94. memcpy(bufptr, iovar, namelen); /* copy iovar name including null */
  95. memcpy((int8*)bufptr + namelen, param, paramlen);
  96. err = wl_ioctl(dev, WLC_GET_VAR, bufptr, buflen);
  97. return (err);
  98. }
  99. static int
  100. wl_iovar_setbuf(struct net_device *dev, char *iovar, void *param, int paramlen, void *bufptr, int buflen)
  101. {
  102. uint namelen;
  103. uint iolen;
  104. namelen = strlen(iovar) + 1; /* length of iovar name plus null */
  105. iolen = namelen + paramlen;
  106. /* check for overflow */
  107. if (iolen > buflen)
  108. return (BCME_BUFTOOSHORT);
  109. memcpy(bufptr, iovar, namelen); /* copy iovar name including null */
  110. memcpy((int8*)bufptr + namelen, param, paramlen);
  111. return wl_ioctl(dev, WLC_SET_VAR, bufptr, iolen);
  112. }
  113. static int
  114. wl_iovar_set(struct net_device *dev, char *iovar, void *param, int paramlen)
  115. {
  116. char smbuf[WLC_IOCTL_SMLEN];
  117. return wl_iovar_setbuf(dev, iovar, param, paramlen, smbuf, sizeof(smbuf));
  118. }
  119. static int
  120. wl_iovar_get(struct net_device *dev, char *iovar, void *bufptr, int buflen)
  121. {
  122. char smbuf[WLC_IOCTL_SMLEN];
  123. int ret;
  124. /* use the return buffer if it is bigger than what we have on the stack */
  125. if (buflen > sizeof(smbuf)) {
  126. ret = wl_iovar_getbuf(dev, iovar, NULL, 0, bufptr, buflen);
  127. } else {
  128. ret = wl_iovar_getbuf(dev, iovar, NULL, 0, smbuf, sizeof(smbuf));
  129. if (ret == 0)
  130. memcpy(bufptr, smbuf, buflen);
  131. }
  132. return ret;
  133. }
  134. #ifdef notyet
  135. /*
  136. * format a bsscfg indexed iovar buffer
  137. */
  138. static int
  139. wl_bssiovar_mkbuf(char *iovar, int bssidx, void *param, int paramlen, void *bufptr, int buflen,
  140. int *plen)
  141. {
  142. char *prefix = "bsscfg:";
  143. int8* p;
  144. uint prefixlen;
  145. uint namelen;
  146. uint iolen;
  147. prefixlen = strlen(prefix); /* length of bsscfg prefix */
  148. namelen = strlen(iovar) + 1; /* length of iovar name + null */
  149. iolen = prefixlen + namelen + sizeof(int) + paramlen;
  150. /* check for overflow */
  151. if (buflen < 0 || iolen > (uint)buflen) {
  152. *plen = 0;
  153. return BCME_BUFTOOSHORT;
  154. }
  155. p = (int8*)bufptr;
  156. /* copy prefix, no null */
  157. memcpy(p, prefix, prefixlen);
  158. p += prefixlen;
  159. /* copy iovar name including null */
  160. memcpy(p, iovar, namelen);
  161. p += namelen;
  162. /* bss config index as first param */
  163. memcpy(p, &bssidx, sizeof(int32));
  164. p += sizeof(int32);
  165. /* parameter buffer follows */
  166. if (paramlen)
  167. memcpy(p, param, paramlen);
  168. *plen = iolen;
  169. return 0;
  170. }
  171. /*
  172. * set named & bss indexed driver variable to buffer value
  173. */
  174. static int
  175. wl_bssiovar_setbuf(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
  176. int buflen)
  177. {
  178. int err;
  179. int iolen;
  180. err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
  181. if (err)
  182. return err;
  183. return wl_ioctl(dev, WLC_SET_VAR, bufptr, iolen);
  184. }
  185. /*
  186. * get named & bss indexed driver variable buffer value
  187. */
  188. static int
  189. wl_bssiovar_getbuf(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen, void *bufptr,
  190. int buflen)
  191. {
  192. int err;
  193. int iolen;
  194. err = wl_bssiovar_mkbuf(iovar, bssidx, param, paramlen, bufptr, buflen, &iolen);
  195. if (err)
  196. return err;
  197. return wl_ioctl(dev, WLC_GET_VAR, bufptr, buflen);
  198. }
  199. /*
  200. * set named & bss indexed driver variable to buffer value
  201. */
  202. static int
  203. wl_bssiovar_set(struct net_device *dev, char *iovar, int bssidx, void *param, int paramlen)
  204. {
  205. char smbuf[WLC_IOCTL_SMLEN];
  206. return wl_bssiovar_setbuf(dev, iovar, bssidx, param, paramlen, smbuf, sizeof(smbuf));
  207. }
  208. /*
  209. * get named & bss indexed driver variable buffer value
  210. */
  211. static int
  212. wl_bssiovar_get(struct net_device *dev, char *iovar, int bssidx, void *outbuf, int len)
  213. {
  214. char smbuf[WLC_IOCTL_SMLEN];
  215. int err;
  216. /* use the return buffer if it is bigger than what we have on the stack */
  217. if (len > (int)sizeof(smbuf)) {
  218. err = wl_bssiovar_getbuf(dev, iovar, bssidx, NULL, 0, outbuf, len);
  219. } else {
  220. memset(smbuf, 0, sizeof(smbuf));
  221. err = wl_bssiovar_getbuf(dev, iovar, bssidx, NULL, 0, smbuf, sizeof(smbuf));
  222. if (err == 0)
  223. memcpy(outbuf, smbuf, len);
  224. }
  225. return err;
  226. }
  227. #endif
  228. #endif
  229. #ifndef WL_WEXT
  230. int get_primary_key(struct net_device *dev)
  231. {
  232. int key, val;
  233. for (key = val = 0; (key < 4) && (val == 0); key++) {
  234. val = key;
  235. if (wl_ioctl(dev, WLC_GET_KEY_PRIMARY, &val, sizeof(val)) < 0)
  236. return -EINVAL;
  237. }
  238. return key;
  239. }
  240. static int wlcompat_ioctl_getiwrange(struct net_device *dev,
  241. char *extra)
  242. {
  243. int i, k;
  244. struct iw_range *range;
  245. range = (struct iw_range *) extra;
  246. memset(extra, 0, sizeof(struct iw_range));
  247. range->we_version_compiled = WIRELESS_EXT;
  248. range->we_version_source = WIRELESS_EXT;
  249. range->min_nwid = range->max_nwid = 0;
  250. range->num_channels = NUM_CHANNELS;
  251. k = 0;
  252. for (i = 0; i < NUM_CHANNELS; i++) {
  253. range->freq[k].i = i + 1;
  254. range->freq[k].m = channel_frequency[i] * 100000;
  255. range->freq[k].e = 1;
  256. k++;
  257. if (k >= IW_MAX_FREQUENCIES)
  258. break;
  259. }
  260. range->num_frequency = k;
  261. range->sensitivity = 3;
  262. /* nbd: don't know what this means, but other drivers set it this way */
  263. range->pmp_flags = IW_POWER_PERIOD;
  264. range->pmt_flags = IW_POWER_TIMEOUT;
  265. range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_UNICAST_R;
  266. range->min_pmp = 0;
  267. range->max_pmp = 65535000;
  268. range->min_pmt = 0;
  269. range->max_pmt = 65535 * 1000;
  270. range->max_qual.qual = 0;
  271. range->max_qual.level = 0;
  272. range->max_qual.noise = 0;
  273. range->min_rts = 0;
  274. if (wl_iovar_get(dev, "rtsthresh", &range->max_rts, sizeof(int)) < 0)
  275. range->max_rts = 2347;
  276. range->min_frag = 256;
  277. if (wl_iovar_get(dev, "fragthresh", &range->max_frag, sizeof(int)) < 0)
  278. range->max_frag = 2346;
  279. range->txpower_capa = IW_TXPOW_DBM;
  280. return 0;
  281. }
  282. static int wlcompat_set_scan(struct net_device *dev,
  283. struct iw_request_info *info,
  284. union iwreq_data *wrqu,
  285. char *extra)
  286. {
  287. int ap = 0;
  288. wl_scan_params_t params;
  289. memset(&params, 0, sizeof(params));
  290. /* use defaults (same parameters as wl scan) */
  291. memset(&params.bssid, 0xff, sizeof(params.bssid));
  292. params.bss_type = DOT11_BSSTYPE_ANY;
  293. params.scan_type = -1;
  294. params.nprobes = -1;
  295. params.active_time = -1;
  296. params.passive_time = -1;
  297. params.home_time = -1;
  298. /* can only scan in STA mode */
  299. wl_ioctl(dev, WLC_GET_AP, &last_mode, sizeof(last_mode));
  300. if (last_mode > 0) {
  301. /* switch to ap mode, scan result query will switch back */
  302. wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
  303. /* wait 250 msec after mode change */
  304. set_current_state(TASK_INTERRUPTIBLE);
  305. schedule_timeout(msecs_to_jiffies(250));
  306. }
  307. scan_cur = SCAN_RETRY_MAX;
  308. while (scan_cur-- && (wl_ioctl(dev, WLC_SCAN, &params, 64) < 0)) {
  309. /* sometimes the driver takes a few tries... */
  310. set_current_state(TASK_INTERRUPTIBLE);
  311. schedule_timeout(msecs_to_jiffies(250));
  312. }
  313. if (!scan_cur)
  314. return -EINVAL;
  315. scan_cur = 0;
  316. /* wait at least 2 seconds for results */
  317. set_current_state(TASK_INTERRUPTIBLE);
  318. schedule_timeout(msecs_to_jiffies(2000));
  319. return 0;
  320. }
  321. struct iw_statistics *wlcompat_get_wireless_stats(struct net_device *dev)
  322. {
  323. struct wl_bss_info *bss_info = (struct wl_bss_info *) buf;
  324. get_pktcnt_t pkt;
  325. unsigned int rssi, noise, ap;
  326. memset(&wstats, 0, sizeof(wstats));
  327. memset(&pkt, 0, sizeof(pkt));
  328. memset(buf, 0, sizeof(buf));
  329. bss_info->version = 0x2000;
  330. wl_ioctl(dev, WLC_GET_BSS_INFO, bss_info, WLC_IOCTL_MAXLEN);
  331. wl_ioctl(dev, WLC_GET_PKTCNTS, &pkt, sizeof(pkt));
  332. rssi = 0;
  333. if ((wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0) || ap) {
  334. if (wl_ioctl(dev, WLC_GET_PHY_NOISE, &noise, sizeof(noise)) < 0)
  335. noise = 0;
  336. } else {
  337. // somehow the structure doesn't fit here
  338. rssi = buf[82];
  339. noise = buf[84];
  340. }
  341. rssi = (rssi == 0 ? 1 : rssi);
  342. wstats.qual.updated = 0x10;
  343. if (rssi <= 1)
  344. wstats.qual.updated |= 0x20;
  345. if (noise <= 1)
  346. wstats.qual.updated |= 0x40;
  347. if ((wstats.qual.updated & 0x60) == 0x60)
  348. return NULL;
  349. wstats.qual.level = rssi;
  350. wstats.qual.noise = noise;
  351. wstats.discard.misc = pkt.rx_bad_pkt;
  352. wstats.discard.retries = pkt.tx_bad_pkt;
  353. return &wstats;
  354. }
  355. static int wlcompat_get_scan(struct net_device *dev,
  356. struct iw_request_info *info,
  357. union iwreq_data *wrqu,
  358. char *extra)
  359. {
  360. wl_scan_results_t *results = (wl_scan_results_t *) buf;
  361. wl_bss_info_t *bss_info;
  362. char *info_ptr;
  363. char *current_ev = extra;
  364. char *current_val;
  365. char *end_buf = extra + IW_SCAN_MAX_DATA;
  366. struct iw_event iwe;
  367. int i, j;
  368. int rssi, noise;
  369. memset(buf, 0, WLC_IOCTL_MAXLEN);
  370. results->buflen = WLC_IOCTL_MAXLEN - sizeof(wl_scan_results_t);
  371. if (wl_ioctl(dev, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0)
  372. return -EAGAIN;
  373. if ((results->count <= 0) && (scan_cur++ < SCAN_RETRY_MAX))
  374. return -EAGAIN;
  375. bss_info = &(results->bss_info[0]);
  376. info_ptr = (char *) bss_info;
  377. for (i = 0; i < results->count; i++) {
  378. /* send the cell address (must be sent first) */
  379. iwe.cmd = SIOCGIWAP;
  380. iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
  381. memcpy(&iwe.u.ap_addr.sa_data, &bss_info->BSSID, sizeof(bss_info->BSSID));
  382. current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
  383. /* send the ESSID */
  384. iwe.cmd = SIOCGIWESSID;
  385. iwe.u.data.length = bss_info->SSID_len;
  386. if (iwe.u.data.length > IW_ESSID_MAX_SIZE)
  387. iwe.u.data.length = IW_ESSID_MAX_SIZE;
  388. iwe.u.data.flags = 1;
  389. current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, bss_info->SSID);
  390. /* send mode */
  391. if (bss_info->capability & (DOT11_CAP_ESS | DOT11_CAP_IBSS)) {
  392. iwe.cmd = SIOCGIWMODE;
  393. if (bss_info->capability & DOT11_CAP_ESS)
  394. iwe.u.mode = IW_MODE_MASTER;
  395. else if (bss_info->capability & DOT11_CAP_IBSS)
  396. iwe.u.mode = IW_MODE_ADHOC;
  397. current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_UINT_LEN);
  398. }
  399. /* send frequency/channel info */
  400. iwe.cmd = SIOCGIWFREQ;
  401. iwe.u.freq.e = 0;
  402. iwe.u.freq.m = bss_info->chanspec & WL_CHANSPEC_CHAN_MASK;
  403. current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
  404. /* add quality statistics */
  405. iwe.cmd = IWEVQUAL;
  406. iwe.u.qual.qual = 0;
  407. iwe.u.qual.level = bss_info->RSSI;
  408. iwe.u.qual.noise = bss_info->phy_noise;
  409. current_ev = iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
  410. /* send encryption capability */
  411. iwe.cmd = SIOCGIWENCODE;
  412. iwe.u.data.pointer = NULL;
  413. iwe.u.data.length = 0;
  414. if (bss_info->capability & DOT11_CAP_PRIVACY)
  415. iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
  416. else
  417. iwe.u.data.flags = IW_ENCODE_DISABLED;
  418. current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
  419. /* send rate information */
  420. iwe.cmd = SIOCGIWRATE;
  421. current_val = current_ev + IW_EV_LCP_LEN;
  422. iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
  423. for(j = 0 ; j < bss_info->rateset.count ; j++) {
  424. iwe.u.bitrate.value = ((bss_info->rateset.rates[j] & 0x7f) * 500000);
  425. current_val = iwe_stream_add_value(current_ev, current_val, end_buf, &iwe, IW_EV_PARAM_LEN);
  426. }
  427. if((current_val - current_ev) > IW_EV_LCP_LEN)
  428. current_ev = current_val;
  429. info_ptr += sizeof(wl_bss_info_t);
  430. if (bss_info->ie_length % 4)
  431. info_ptr += bss_info->ie_length + 4 - (bss_info->ie_length % 4);
  432. else
  433. info_ptr += bss_info->ie_length;
  434. bss_info = (wl_bss_info_t *) info_ptr;
  435. }
  436. wrqu->data.length = (current_ev - extra);
  437. wrqu->data.flags = 0;
  438. if (last_mode > 0)
  439. /* switch back to ap mode */
  440. wl_ioctl(dev, WLC_SET_AP, &last_mode, sizeof(last_mode));
  441. return 0;
  442. }
  443. static int wlcompat_ioctl(struct net_device *dev,
  444. struct iw_request_info *info,
  445. union iwreq_data *wrqu,
  446. char *extra)
  447. {
  448. switch (info->cmd) {
  449. case SIOCGIWNAME:
  450. strcpy(wrqu->name, "IEEE 802.11-DS");
  451. break;
  452. case SIOCGIWFREQ:
  453. {
  454. channel_info_t ci;
  455. if (wl_ioctl(dev,WLC_GET_CHANNEL, &ci, sizeof(ci)) < 0)
  456. return -EINVAL;
  457. wrqu->freq.m = ci.target_channel;
  458. wrqu->freq.e = 0;
  459. break;
  460. }
  461. case SIOCSIWFREQ:
  462. {
  463. if (wrqu->freq.m == -1) {
  464. wrqu->freq.m = 0;
  465. if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
  466. return -EINVAL;
  467. } else {
  468. if (wrqu->freq.e == 1) {
  469. int channel = 0;
  470. int f = wrqu->freq.m / 100000;
  471. while ((channel < NUM_CHANNELS + 1) && (f != channel_frequency[channel]))
  472. channel++;
  473. if (channel == NUM_CHANNELS) // channel not found
  474. return -EINVAL;
  475. wrqu->freq.e = 0;
  476. wrqu->freq.m = channel + 1;
  477. }
  478. if ((wrqu->freq.e == 0) && (wrqu->freq.m < 1000)) {
  479. if (wl_ioctl(dev, WLC_SET_CHANNEL, &wrqu->freq.m, sizeof(int)) < 0)
  480. return -EINVAL;
  481. } else {
  482. return -EINVAL;
  483. }
  484. }
  485. break;
  486. }
  487. case SIOCSIWAP:
  488. {
  489. int ap = 0;
  490. int infra = 0;
  491. rw_reg_t reg;
  492. memset(&reg, 0, sizeof(reg));
  493. if (wrqu->ap_addr.sa_family != ARPHRD_ETHER)
  494. return -EINVAL;
  495. if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
  496. return -EINVAL;
  497. if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
  498. return -EINVAL;
  499. if (!infra)
  500. wl_ioctl(dev, WLC_SET_BSSID, wrqu->ap_addr.sa_data, 6);
  501. if (wl_ioctl(dev, ((ap || !infra) ? WLC_SET_BSSID : WLC_REASSOC), wrqu->ap_addr.sa_data, 6) < 0)
  502. return -EINVAL;
  503. break;
  504. }
  505. case SIOCGIWAP:
  506. {
  507. wrqu->ap_addr.sa_family = ARPHRD_ETHER;
  508. if (wl_ioctl(dev,WLC_GET_BSSID,wrqu->ap_addr.sa_data,6) < 0)
  509. return -EINVAL;
  510. break;
  511. }
  512. case SIOCGIWESSID:
  513. {
  514. wlc_ssid_t ssid;
  515. if (wl_ioctl(dev,WLC_GET_SSID, &ssid, sizeof(wlc_ssid_t)) < 0)
  516. return -EINVAL;
  517. wrqu->essid.flags = wrqu->data.flags = 1;
  518. wrqu->essid.length = wrqu->data.length = ssid.SSID_len + 1;
  519. memcpy(extra,ssid.SSID,ssid.SSID_len + 1);
  520. break;
  521. }
  522. case SIOCSIWESSID:
  523. {
  524. wlc_ssid_t ssid;
  525. memset(&ssid, 0, sizeof(ssid));
  526. ssid.SSID_len = strlen(extra);
  527. if (ssid.SSID_len > 32)
  528. ssid.SSID_len = 32;
  529. memcpy(ssid.SSID, extra, ssid.SSID_len);
  530. if (wl_ioctl(dev, WLC_SET_SSID, &ssid, sizeof(ssid)) < 0)
  531. return -EINVAL;
  532. break;
  533. }
  534. case SIOCGIWRTS:
  535. {
  536. if (wl_iovar_get(dev, "rtsthresh", &(wrqu->rts.value), sizeof(int)) < 0)
  537. return -EINVAL;
  538. break;
  539. }
  540. case SIOCSIWRTS:
  541. {
  542. if (wl_iovar_set(dev, "rtsthresh", &(wrqu->rts.value), sizeof(int)) < 0)
  543. return -EINVAL;
  544. break;
  545. }
  546. case SIOCGIWFRAG:
  547. {
  548. if (wl_iovar_get(dev, "fragthresh", &(wrqu->frag.value), sizeof(int)) < 0)
  549. return -EINVAL;
  550. break;
  551. }
  552. case SIOCSIWFRAG:
  553. {
  554. if (wl_iovar_set(dev, "fragthresh", &(wrqu->frag.value), sizeof(int)) < 0)
  555. return -EINVAL;
  556. break;
  557. }
  558. case SIOCGIWTXPOW:
  559. {
  560. int radio, override;
  561. wl_ioctl(dev, WLC_GET_RADIO, &radio, sizeof(int));
  562. if (wl_iovar_get(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
  563. return -EINVAL;
  564. override = (wrqu->txpower.value & WL_TXPWR_OVERRIDE) == WL_TXPWR_OVERRIDE;
  565. wrqu->txpower.value &= ~WL_TXPWR_OVERRIDE;
  566. if (!override && (wrqu->txpower.value > 76))
  567. wrqu->txpower.value = 76;
  568. wrqu->txpower.value /= 4;
  569. wrqu->txpower.fixed = 0;
  570. wrqu->txpower.disabled = radio;
  571. wrqu->txpower.flags = IW_TXPOW_DBM;
  572. break;
  573. }
  574. case SIOCSIWTXPOW:
  575. {
  576. /* This is weird: WLC_SET_RADIO with 1 as argument disables the radio */
  577. int radio = wrqu->txpower.disabled;
  578. wl_ioctl(dev, WLC_SET_RADIO, &radio, sizeof(int));
  579. if (!wrqu->txpower.disabled && (wrqu->txpower.value > 0)) {
  580. int value;
  581. if (wl_iovar_get(dev, "qtxpower", &value, sizeof(int)) < 0)
  582. return -EINVAL;
  583. value &= WL_TXPWR_OVERRIDE;
  584. wrqu->txpower.value *= 4;
  585. wrqu->txpower.value |= value;
  586. if (wrqu->txpower.flags != IW_TXPOW_DBM)
  587. return -EINVAL;
  588. if (wrqu->txpower.value > 0)
  589. if (wl_iovar_set(dev, "qtxpower", &(wrqu->txpower.value), sizeof(int)) < 0)
  590. return -EINVAL;
  591. }
  592. break;
  593. }
  594. case SIOCSIWENCODE:
  595. {
  596. int val = 0, wep = 1, wrestrict = 1;
  597. int index = (wrqu->data.flags & IW_ENCODE_INDEX) - 1;
  598. if (index < 0)
  599. index = get_primary_key(dev);
  600. if (wrqu->data.flags & IW_ENCODE_DISABLED) {
  601. wep = 0;
  602. if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0)
  603. return -EINVAL;
  604. return 0;
  605. }
  606. if (wl_ioctl(dev, WLC_SET_WSEC, &wep, sizeof(val)) < 0)
  607. return -EINVAL;
  608. if (wrqu->data.flags & IW_ENCODE_OPEN)
  609. wrestrict = 0;
  610. if (wrqu->data.pointer && (wrqu->data.length > 0) && (wrqu->data.length <= 16)) {
  611. wl_wsec_key_t key;
  612. memset(&key, 0, sizeof(key));
  613. key.flags = WL_PRIMARY_KEY;
  614. key.len = wrqu->data.length;
  615. key.index = index;
  616. memcpy(key.data, wrqu->data.pointer, wrqu->data.length);
  617. if (wl_ioctl(dev, WLC_SET_KEY, &key, sizeof(key)) < 0)
  618. return -EINVAL;
  619. }
  620. if (index >= 0)
  621. wl_ioctl(dev, WLC_SET_KEY_PRIMARY, &index, sizeof(index));
  622. if (wrestrict >= 0)
  623. wl_ioctl(dev, WLC_SET_WEP_RESTRICT, &wrestrict, sizeof(wrestrict));
  624. break;
  625. }
  626. case SIOCGIWENCODE:
  627. {
  628. int val;
  629. int key = get_primary_key(dev);
  630. int *info_addr;
  631. wkey *wep_key;
  632. if (wl_ioctl(dev, WLC_GET_WSEC, &val, sizeof(val)) < 0)
  633. return -EINVAL;
  634. if (!(val & WEP_ENABLED)) {
  635. wrqu->data.flags = IW_ENCODE_DISABLED;
  636. break;
  637. }
  638. key = get_primary_key(dev);
  639. wrqu->data.flags = IW_ENCODE_ENABLED;
  640. /* the driver apparently doesn't allow us to read the wep key */
  641. wrqu->data.flags |= IW_ENCODE_NOKEY;
  642. break;
  643. }
  644. case SIOCGIWRANGE:
  645. {
  646. return wlcompat_ioctl_getiwrange(dev, extra);
  647. break;
  648. }
  649. case SIOCSIWMODE:
  650. {
  651. int ap = -1, infra = -1, passive = 0, wet = 0;
  652. wl_ioctl(dev, WLC_GET_WET, &wet, sizeof(wet));
  653. switch (wrqu->mode) {
  654. case IW_MODE_MONITOR:
  655. passive = 1;
  656. break;
  657. case IW_MODE_ADHOC:
  658. infra = 0;
  659. ap = 0;
  660. break;
  661. case IW_MODE_MASTER:
  662. infra = 1;
  663. ap = 1;
  664. break;
  665. case IW_MODE_INFRA:
  666. infra = 1;
  667. ap = 0;
  668. wet = 0;
  669. break;
  670. case IW_MODE_REPEAT:
  671. infra = 1;
  672. ap = 0;
  673. wet = 1;
  674. break;
  675. default:
  676. return -EINVAL;
  677. }
  678. wl_ioctl(dev, WLC_SET_PASSIVE, &passive, sizeof(passive));
  679. wl_ioctl(dev, WLC_SET_MONITOR, &passive, sizeof(passive));
  680. if ((ap == 0) && (infra == 1))
  681. wl_ioctl(dev, WLC_SET_WET, &wet, sizeof(wet));
  682. if (ap >= 0)
  683. wl_ioctl(dev, WLC_SET_AP, &ap, sizeof(ap));
  684. if (infra >= 0)
  685. wl_ioctl(dev, WLC_SET_INFRA, &infra, sizeof(infra));
  686. break;
  687. }
  688. case SIOCGIWMODE:
  689. {
  690. int ap, infra, wet, passive;
  691. if (wl_ioctl(dev, WLC_GET_AP, &ap, sizeof(ap)) < 0)
  692. return -EINVAL;
  693. if (wl_ioctl(dev, WLC_GET_INFRA, &infra, sizeof(infra)) < 0)
  694. return -EINVAL;
  695. if (wl_ioctl(dev, WLC_GET_PASSIVE, &passive, sizeof(passive)) < 0)
  696. return -EINVAL;
  697. if (passive) {
  698. wrqu->mode = IW_MODE_MONITOR;
  699. } else if (!infra) {
  700. wrqu->mode = IW_MODE_ADHOC;
  701. } else {
  702. if (ap) {
  703. wrqu->mode = IW_MODE_MASTER;
  704. } else {
  705. wrqu->mode = IW_MODE_INFRA;
  706. }
  707. }
  708. break;
  709. }
  710. default:
  711. return -EINVAL;
  712. break;
  713. }
  714. return 0;
  715. }
  716. static const iw_handler wlcompat_handler[] = {
  717. NULL, /* SIOCSIWCOMMIT */
  718. wlcompat_ioctl, /* SIOCGIWNAME */
  719. NULL, /* SIOCSIWNWID */
  720. NULL, /* SIOCGIWNWID */
  721. wlcompat_ioctl, /* SIOCSIWFREQ */
  722. wlcompat_ioctl, /* SIOCGIWFREQ */
  723. wlcompat_ioctl, /* SIOCSIWMODE */
  724. wlcompat_ioctl, /* SIOCGIWMODE */
  725. NULL, /* SIOCSIWSENS */
  726. NULL, /* SIOCGIWSENS */
  727. NULL, /* SIOCSIWRANGE, unused */
  728. wlcompat_ioctl, /* SIOCGIWRANGE */
  729. NULL, /* SIOCSIWPRIV */
  730. NULL, /* SIOCGIWPRIV */
  731. NULL, /* SIOCSIWSTATS */
  732. NULL, /* SIOCGIWSTATS */
  733. iw_handler_set_spy, /* SIOCSIWSPY */
  734. iw_handler_get_spy, /* SIOCGIWSPY */
  735. iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
  736. iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
  737. wlcompat_ioctl, /* SIOCSIWAP */
  738. wlcompat_ioctl, /* SIOCGIWAP */
  739. NULL, /* -- hole -- */
  740. NULL, /* SIOCGIWAPLIST */
  741. wlcompat_set_scan, /* SIOCSIWSCAN */
  742. wlcompat_get_scan, /* SIOCGIWSCAN */
  743. wlcompat_ioctl, /* SIOCSIWESSID */
  744. wlcompat_ioctl, /* SIOCGIWESSID */
  745. NULL, /* SIOCSIWNICKN */
  746. NULL, /* SIOCGIWNICKN */
  747. NULL, /* -- hole -- */
  748. NULL, /* -- hole -- */
  749. NULL, /* SIOCSIWRATE */
  750. NULL, /* SIOCGIWRATE */
  751. wlcompat_ioctl, /* SIOCSIWRTS */
  752. wlcompat_ioctl, /* SIOCGIWRTS */
  753. wlcompat_ioctl, /* SIOCSIWFRAG */
  754. wlcompat_ioctl, /* SIOCGIWFRAG */
  755. wlcompat_ioctl, /* SIOCSIWTXPOW */
  756. wlcompat_ioctl, /* SIOCGIWTXPOW */
  757. NULL, /* SIOCSIWRETRY */
  758. NULL, /* SIOCGIWRETRY */
  759. wlcompat_ioctl, /* SIOCSIWENCODE */
  760. wlcompat_ioctl, /* SIOCGIWENCODE */
  761. };
  762. static const struct iw_handler_def wlcompat_handler_def =
  763. {
  764. .standard = (iw_handler *) wlcompat_handler,
  765. .num_standard = sizeof(wlcompat_handler)/sizeof(iw_handler),
  766. };
  767. #endif
  768. #ifdef DEBUG
  769. void print_buffer(int len, unsigned char *buf) {
  770. int x;
  771. if (buf != NULL) {
  772. for (x=0;x<len && x<180 ;x++) {
  773. if ((x % 4) == 0)
  774. printk(" ");
  775. printk("%02X",buf[x]);
  776. }
  777. } else {
  778. printk(" NULL");
  779. }
  780. printk("\n");
  781. }
  782. #endif
  783. static int (*old_ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
  784. static int new_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) {
  785. int ret = 0;
  786. #ifdef DEBUG
  787. printk("dev: %s ioctl: 0x%04x\n",dev->name,cmd);
  788. if (cmd==SIOCDEVPRIVATE) {
  789. wl_ioctl_t *ioc = (wl_ioctl_t *)ifr->ifr_data;
  790. unsigned char *buf = ioc->buf;
  791. printk(" cmd: %d buf: 0x%08x len: %d\n",ioc->cmd,&(ioc->buf),ioc->len);
  792. printk(" send: ->");
  793. print_buffer(ioc->len, buf);
  794. ret = old_ioctl(dev,ifr,cmd);
  795. printk(" recv: ->");
  796. print_buffer(ioc->len, buf);
  797. printk(" ret: %d\n", ret);
  798. } else
  799. #endif
  800. {
  801. ret = old_ioctl(dev,ifr,cmd);
  802. }
  803. return ret;
  804. }
  805. #ifndef DEBUG
  806. static struct timer_list rng_timer;
  807. static spinlock_t rng_lock = SPIN_LOCK_UNLOCKED;
  808. static void rng_timer_tick(unsigned long n)
  809. {
  810. struct net_device *dev = (struct net_device *) n;
  811. unsigned long flags;
  812. u16 data[4];
  813. int i, ret;
  814. ret = 0;
  815. spin_lock_irqsave(&rng_lock, flags);
  816. for (i = 0; i < 3; i++) {
  817. ret |= wl_iovar_get(dev, "rand", &data[i], sizeof(u16));
  818. }
  819. spin_unlock_irqrestore(&rng_lock, flags);
  820. if (!ret)
  821. batch_entropy_store(*((u32 *) &data[0]), *((u32 *) &data[2]), (jiffies % 255));
  822. mod_timer(&rng_timer, jiffies + (HZ/RNG_POLL_FREQ));
  823. }
  824. #endif
  825. static int __init wlcompat_init()
  826. {
  827. int found = 0, i;
  828. char devname[4] = "wl0";
  829. while (!found && (dev = dev_get_by_name(devname))) {
  830. if ((wl_ioctl(dev, WLC_GET_MAGIC, &i, sizeof(i)) == 0) && (i == WLC_IOCTL_MAGIC))
  831. found = 1;
  832. devname[2]++;
  833. }
  834. if (!found) {
  835. printk("No Broadcom devices found.\n");
  836. return -ENODEV;
  837. }
  838. old_ioctl = dev->do_ioctl;
  839. dev->do_ioctl = new_ioctl;
  840. #ifndef WL_WEXT
  841. dev->wireless_handlers = (struct iw_handler_def *)&wlcompat_handler_def;
  842. dev->get_wireless_stats = wlcompat_get_wireless_stats;
  843. #endif
  844. #ifndef DEBUG
  845. if (random) {
  846. init_timer(&rng_timer);
  847. rng_timer.function = rng_timer_tick;
  848. rng_timer.data = (unsigned long) dev;
  849. rng_timer_tick((unsigned long) dev);
  850. }
  851. #endif
  852. #ifdef DEBUG
  853. printk("broadcom driver private data: 0x%08x\n", dev->priv);
  854. #endif
  855. return 0;
  856. }
  857. static void __exit wlcompat_exit()
  858. {
  859. #ifndef DEBUG
  860. if (random)
  861. del_timer(&rng_timer);
  862. #endif
  863. #ifndef WL_WEXT
  864. dev->get_wireless_stats = NULL;
  865. dev->wireless_handlers = NULL;
  866. #endif
  867. dev->do_ioctl = old_ioctl;
  868. return;
  869. }
  870. EXPORT_NO_SYMBOLS;
  871. MODULE_AUTHOR("openwrt.org");
  872. MODULE_LICENSE("GPL");
  873. #ifndef DEBUG
  874. module_param(random, int, 0);
  875. #endif
  876. module_init(wlcompat_init);
  877. module_exit(wlcompat_exit);