patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / drivers / net / wireless / prism54 / isl_ioctl.c
1 /*
2  *  
3  *  Copyright (C) 2002 Intersil Americas Inc.
4  *            (C) 2003,2004 Aurelien Alleaume <slts@free.fr>
5  *            (C) 2003 Herbert Valerio Riedel <hvr@gnu.org>
6  *            (C) 2003 Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License
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
23 #include <linux/version.h>
24 #include <linux/module.h>
25 #include <linux/kernel.h>
26 #include <linux/if_arp.h>
27 #include <linux/pci.h>
28 #include <linux/moduleparam.h>
29
30 #include <asm/uaccess.h>
31
32 #include "isl_ioctl.h"
33 #include "islpci_mgt.h"
34 #include "isl_oid.h"            /* additional types and defs for isl38xx fw */
35 #include "oid_mgt.h"
36
37 #include <net/iw_handler.h>     /* New driver API */
38
39 static int init_mode = CARD_DEFAULT_IW_MODE;
40 static int init_channel = CARD_DEFAULT_CHANNEL;
41 static int init_wep = CARD_DEFAULT_WEP;
42 static int init_filter = CARD_DEFAULT_FILTER;
43 static int init_authen = CARD_DEFAULT_AUTHEN;
44 static int init_dot1x = CARD_DEFAULT_DOT1X;
45 static int init_conformance = CARD_DEFAULT_CONFORMANCE;
46 static int init_mlme = CARD_DEFAULT_MLME_MODE;
47
48 module_param(init_mode, int, 0);
49 MODULE_PARM_DESC(init_mode,
50                  "Set card mode:\n0: Auto\n1: Ad-Hoc\n2: Managed Client (Default)\n3: Master / Access Point\n4: Repeater (Not supported yet)\n5: Secondary (Not supported yet)\n6: Monitor");
51
52 module_param(init_channel, int, 0);
53 MODULE_PARM_DESC(init_channel,
54                  "Check `iwpriv ethx channel` for available channels");
55
56 module_param(init_wep, int, 0);
57 module_param(init_filter, int, 0);
58
59 module_param(init_authen, int, 0);
60 MODULE_PARM_DESC(init_authen,
61                  "Authentication method. Can be of seven types:\n0 0x0000: None\n1 0x0001: DOT11_AUTH_OS (Default)\n2 0x0002: DOT11_AUTH_SK\n3 0x0003: DOT11_AUTH_BOTH");
62
63 module_param(init_dot1x, int, 0);
64 MODULE_PARM_DESC(init_dot1x,
65                  "\n0: None/not set     (Default)\n1: DOT11_DOT1X_AUTHENABLED\n2: DOT11_DOT1X_KEYTXENABLED");
66
67 module_param(init_mlme, int, 0);
68 MODULE_PARM_DESC(init_mlme,
69                  "Sets the MAC layer management entity (MLME) mode of operation,\n0: DOT11_MLME_AUTO (Default)\n1: DOT11_MLME_INTERMEDIATE\n2: DOT11_MLME_EXTENDED");
70
71 /**
72  * prism54_mib_mode_helper - MIB change mode helper function
73  * @mib: the &struct islpci_mib object to modify
74  * @iw_mode: new mode (%IW_MODE_*)
75  * 
76  *  This is a helper function, hence it does not lock. Make sure
77  *  caller deals with locking *if* necessary. This function sets the 
78  *  mode-dependent mib values and does the mapping of the Linux 
79  *  Wireless API modes to Device firmware modes. It also checks for 
80  *  correct valid Linux wireless modes. 
81  */
82 int
83 prism54_mib_mode_helper(islpci_private *priv, u32 iw_mode)
84 {
85         u32 config = INL_CONFIG_MANUALRUN;
86         u32 mode, bsstype;
87
88         /* For now, just catch early the Repeater and Secondary modes here */
89         if (iw_mode == IW_MODE_REPEAT || iw_mode == IW_MODE_SECOND) {
90                 printk(KERN_DEBUG
91                        "%s(): Sorry, Repeater mode and Secondary mode "
92                        "are not yet supported by this driver.\n", __FUNCTION__);
93                 return -EINVAL;
94         }
95
96         priv->iw_mode = iw_mode;
97
98         switch (iw_mode) {
99         case IW_MODE_AUTO:
100                 mode = INL_MODE_CLIENT;
101                 bsstype = DOT11_BSSTYPE_ANY;
102                 break;
103         case IW_MODE_ADHOC:
104                 mode = INL_MODE_CLIENT;
105                 bsstype = DOT11_BSSTYPE_IBSS;
106                 break;
107         case IW_MODE_INFRA:
108                 mode = INL_MODE_CLIENT;
109                 bsstype = DOT11_BSSTYPE_INFRA;
110                 break;
111         case IW_MODE_MASTER:
112                 mode = INL_MODE_AP;
113                 bsstype = DOT11_BSSTYPE_INFRA;
114                 break;
115         case IW_MODE_MONITOR:
116                 mode = INL_MODE_PROMISCUOUS;
117                 bsstype = DOT11_BSSTYPE_ANY;
118                 config |= INL_CONFIG_RXANNEX;
119                 break;
120         default:
121                 return -EINVAL;
122         }
123
124         if (init_wds)
125                 config |= INL_CONFIG_WDS;
126         mgt_set(priv, DOT11_OID_BSSTYPE, &bsstype);
127         mgt_set(priv, OID_INL_CONFIG, &config);
128         mgt_set(priv, OID_INL_MODE, &mode);
129
130         return 0;
131 }
132
133 /**
134  * prism54_mib_init - fill MIB cache with defaults
135  *
136  *  this function initializes the struct given as @mib with defaults,
137  *  of which many are retrieved from the global module parameter
138  *  variables.  
139  */
140
141 void
142 prism54_mib_init(islpci_private *priv)
143 {
144         u32 t;
145         struct obj_buffer psm_buffer = {
146                 .size = PSM_BUFFER_SIZE,
147                 .addr = priv->device_psm_buffer
148         };
149
150         mgt_set(priv, DOT11_OID_CHANNEL, &init_channel);
151         mgt_set(priv, DOT11_OID_AUTHENABLE, &init_authen);
152         mgt_set(priv, DOT11_OID_PRIVACYINVOKED, &init_wep);
153
154         mgt_set(priv, DOT11_OID_PSMBUFFER, &psm_buffer);
155         mgt_set(priv, DOT11_OID_EXUNENCRYPTED, &init_filter);
156         mgt_set(priv, DOT11_OID_DOT1XENABLE, &init_dot1x);
157         mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &init_mlme);
158         mgt_set(priv, OID_INL_DOT11D_CONFORMANCE, &init_conformance);
159
160         t = 127;
161         mgt_set(priv, OID_INL_OUTPUTPOWER, &t);
162
163         /* Important: we are setting a default wireless mode and we are 
164          * forcing a valid one, so prism54_mib_mode_helper should just set
165          * mib values depending on what the wireless mode given is. No need
166          * for it save old values */
167         if (init_mode > IW_MODE_MONITOR || init_mode < IW_MODE_AUTO) {
168                 printk(KERN_DEBUG "%s(): You passed a non-valid init_mode. "
169                                 "Using default mode\n", __FUNCTION__);
170                 init_mode = CARD_DEFAULT_IW_MODE;
171         }
172         /* This sets all of the mode-dependent values */
173         prism54_mib_mode_helper(priv, init_mode);
174 }
175
176 void
177 prism54_mib_init_work(islpci_private *priv)
178 {
179         down_write(&priv->mib_sem);
180         mgt_commit(priv);
181         up_write(&priv->mib_sem);
182 }
183
184 /* this will be executed outside of atomic context thanks to
185  * schedule_work(), thus we can as well use sleeping semaphore
186  * locking */
187 void
188 prism54_update_stats(islpci_private *priv)
189 {
190         char *data;
191         int j;
192         struct obj_bss bss, *bss2;
193         union oid_res_t r;
194
195         if (down_interruptible(&priv->stats_sem))
196                 return;
197
198 /* missing stats are :
199  *  iwstatistics.qual.updated
200  *  iwstatistics.discard.nwid       
201  *  iwstatistics.discard.fragment           
202  *  iwstatistics.discard.misc
203  *  iwstatistics.miss.beacon */
204
205 /* Noise floor.
206  * I'm not sure if the unit is dBm.
207  * Note : If we are not connected, this value seems to be irrevelant. */
208
209         mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
210         priv->local_iwstatistics.qual.noise = r.u;
211
212 /* Get the rssi of the link. To do this we need to retrieve a bss. */
213
214         /* First get the MAC address of the AP we are associated with. */
215         mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
216         data = r.ptr;
217
218         /* copy this MAC to the bss */
219         for (j = 0; j < 6; j++)
220                 bss.address[j] = data[j];
221         kfree(data);
222
223         /* now ask for the corresponding bss */
224         j = mgt_get_request(priv, DOT11_OID_BSSFIND, 0, (void *) &bss, &r);
225         bss2 = r.ptr;
226         /* report the rssi and use it to calculate
227          *  link quality through a signal-noise
228          *  ratio */
229         priv->local_iwstatistics.qual.level = bss2->rssi;
230         priv->local_iwstatistics.qual.qual =
231             bss2->rssi - priv->iwstatistics.qual.noise;
232
233         kfree(bss2);
234
235         /* report that the stats are new */
236         priv->local_iwstatistics.qual.updated = 0x7;
237
238 /* Rx : unable to decrypt the MPDU */
239         mgt_get_request(priv, DOT11_OID_PRIVRXFAILED, 0, NULL, &r);
240         priv->local_iwstatistics.discard.code = r.u;
241
242 /* Tx : Max MAC retries num reached */
243         mgt_get_request(priv, DOT11_OID_MPDUTXFAILED, 0, NULL, &r);
244         priv->local_iwstatistics.discard.retries = r.u;
245
246         up(&priv->stats_sem);
247
248         return;
249 }
250
251 struct iw_statistics *
252 prism54_get_wireless_stats(struct net_device *ndev)
253 {
254         islpci_private *priv = netdev_priv(ndev);
255
256         /* If the stats are being updated return old data */
257         if (down_trylock(&priv->stats_sem) == 0) {
258                 memcpy(&priv->iwstatistics, &priv->local_iwstatistics,
259                        sizeof (struct iw_statistics));
260                 /* They won't be marked updated for the next time */
261                 priv->local_iwstatistics.qual.updated = 0;
262                 up(&priv->stats_sem);
263         } else
264                 priv->iwstatistics.qual.updated = 0;
265
266         /* Update our wireless stats, but do not schedule to often 
267          * (max 1 HZ) */
268         if ((priv->stats_timestamp == 0) ||
269             time_after(jiffies, priv->stats_timestamp + 1 * HZ)) {
270                 schedule_work(&priv->stats_work);
271                 priv->stats_timestamp = jiffies;
272         }
273
274         return &priv->iwstatistics;
275 }
276
277 static int
278 prism54_commit(struct net_device *ndev, struct iw_request_info *info,
279                char *cwrq, char *extra)
280 {
281         islpci_private *priv = netdev_priv(ndev);
282
283         /* simply re-set the last set SSID, this should commit most stuff */
284
285         /* Commit in Monitor mode is not necessary, also setting essid
286          * in Monitor mode does not make sense and isn't allowed for this
287          * device's firmware */
288         if (priv->iw_mode != IW_MODE_MONITOR)
289                 return mgt_set_request(priv, DOT11_OID_SSID, 0, NULL);
290         return 0;
291 }
292
293 static int
294 prism54_get_name(struct net_device *ndev, struct iw_request_info *info,
295                  char *cwrq, char *extra)
296 {
297         islpci_private *priv = netdev_priv(ndev);
298         char *capabilities;
299         union oid_res_t r;
300         int rvalue;
301
302         if (islpci_get_state(priv) < PRV_STATE_INIT) {
303                 strncpy(cwrq, "NOT READY!", IFNAMSIZ);
304                 return 0;
305         }
306         rvalue = mgt_get_request(priv, OID_INL_PHYCAPABILITIES, 0, NULL, &r);
307
308         switch (r.u) {
309         case INL_PHYCAP_5000MHZ:
310                 capabilities = "IEEE 802.11a/b/g";
311                 break;
312         case INL_PHYCAP_FAA:
313                 capabilities = "IEEE 802.11b/g - FAA Support";
314                 break;
315         case INL_PHYCAP_2400MHZ:
316         default:
317                 capabilities = "IEEE 802.11b/g";        /* Default */
318                 break;
319         }
320         strncpy(cwrq, capabilities, IFNAMSIZ);
321         return rvalue;
322 }
323
324 static int
325 prism54_set_freq(struct net_device *ndev, struct iw_request_info *info,
326                  struct iw_freq *fwrq, char *extra)
327 {
328         islpci_private *priv = netdev_priv(ndev);
329         int rvalue;
330         u32 c;
331
332         if (fwrq->m < 1000)
333                 /* we have a channel number */
334                 c = fwrq->m;
335         else
336                 c = (fwrq->e == 1) ? channel_of_freq(fwrq->m / 100000) : 0;
337
338         rvalue = c ? mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c) : -EINVAL;
339
340         /* Call commit handler */
341         return (rvalue ? rvalue : -EINPROGRESS);
342 }
343
344 static int
345 prism54_get_freq(struct net_device *ndev, struct iw_request_info *info,
346                  struct iw_freq *fwrq, char *extra)
347 {
348         islpci_private *priv = netdev_priv(ndev);
349         union oid_res_t r;
350         int rvalue;
351
352         rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r);
353
354         fwrq->m = r.u;
355         fwrq->e = 0;
356
357         return rvalue;
358 }
359
360 static int
361 prism54_set_mode(struct net_device *ndev, struct iw_request_info *info,
362                  __u32 * uwrq, char *extra)
363 {
364         islpci_private *priv = netdev_priv(ndev);
365         u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
366
367         /* Let's see if the user passed a valid Linux Wireless mode */
368         if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) {
369                 printk(KERN_DEBUG
370                        "%s: %s() You passed a non-valid init_mode.\n",
371                        priv->ndev->name, __FUNCTION__);
372                 return -EINVAL;
373         }
374
375         down_write(&priv->mib_sem);
376
377         if (prism54_mib_mode_helper(priv, *uwrq)) {
378                 up_write(&priv->mib_sem);
379                 return -EOPNOTSUPP;
380         }
381
382         /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an
383          * extended one.
384          */
385         if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
386                 mlmeautolevel = DOT11_MLME_INTERMEDIATE;
387         if (priv->wpa)
388                 mlmeautolevel = DOT11_MLME_EXTENDED;
389
390         mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
391
392         mgt_commit(priv);
393         priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR)
394             ? priv->monitor_type : ARPHRD_ETHER;
395         up_write(&priv->mib_sem);
396
397         return 0;
398 }
399
400 /* Use mib cache */
401 static int
402 prism54_get_mode(struct net_device *ndev, struct iw_request_info *info,
403                  __u32 * uwrq, char *extra)
404 {
405         islpci_private *priv = netdev_priv(ndev);
406
407         BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode >
408                                                   IW_MODE_MONITOR));
409         *uwrq = priv->iw_mode;
410
411         return 0;
412 }
413
414 /* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to
415  * emit data if (sensitivity > rssi - noise) (in dBm).
416  * prism54_set_sens does not seem to work.
417  */
418
419 static int
420 prism54_set_sens(struct net_device *ndev, struct iw_request_info *info,
421                  struct iw_param *vwrq, char *extra)
422 {
423         islpci_private *priv = netdev_priv(ndev);
424         u32 sens;
425
426         /* by default  the card sets this to 20. */
427         sens = vwrq->disabled ? 20 : vwrq->value;
428
429         /* set the ed threshold. */
430         return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens);
431 }
432
433 static int
434 prism54_get_sens(struct net_device *ndev, struct iw_request_info *info,
435                  struct iw_param *vwrq, char *extra)
436 {
437         islpci_private *priv = netdev_priv(ndev);
438         union oid_res_t r;
439         int rvalue;
440
441         rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r);
442
443         vwrq->value = r.u;
444         vwrq->disabled = (vwrq->value == 0);
445         vwrq->fixed = 1;
446
447         return rvalue;
448 }
449
450 static int
451 prism54_get_range(struct net_device *ndev, struct iw_request_info *info,
452                   struct iw_point *dwrq, char *extra)
453 {
454         struct iw_range *range = (struct iw_range *) extra;
455         islpci_private *priv = netdev_priv(ndev);
456         char *data;
457         int i, m, rvalue;
458         struct obj_frequencies *freq;
459         union oid_res_t r;
460
461         memset(range, 0, sizeof (struct iw_range));
462         dwrq->length = sizeof (struct iw_range);
463
464         /* set the wireless extension version number */
465         range->we_version_source = SUPPORTED_WIRELESS_EXT;
466         range->we_version_compiled = WIRELESS_EXT;
467
468         /* Now the encoding capabilities */
469         range->num_encoding_sizes = 3;
470         /* 64(40) bits WEP */
471         range->encoding_size[0] = 5;
472         /* 128(104) bits WEP */
473         range->encoding_size[1] = 13;
474         /* 256 bits for WPA-PSK */
475         range->encoding_size[2] = 32;
476         /* 4 keys are allowed */
477         range->max_encoding_tokens = 4;
478
479         /* we don't know the quality range... */
480         range->max_qual.level = 0;
481         range->max_qual.noise = 0;
482         range->max_qual.qual = 0;
483         /* these value describe an average quality. Needs more tweaking... */
484         range->avg_qual.level = -80;    /* -80 dBm */
485         range->avg_qual.noise = 0;      /* don't know what to put here */
486         range->avg_qual.qual = 0;
487
488         range->sensitivity = 200;
489
490         /* retry limit capabilities */
491         range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
492         range->retry_flags = IW_RETRY_LIMIT;
493         range->r_time_flags = IW_RETRY_LIFETIME;
494
495         /* I don't know the range. Put stupid things here */
496         range->min_retry = 1;
497         range->max_retry = 65535;
498         range->min_r_time = 1024;
499         range->max_r_time = 65535 * 1024;
500
501         /* txpower is supported in dBm's */
502         range->txpower_capa = IW_TXPOW_DBM;
503
504         if (islpci_get_state(priv) < PRV_STATE_INIT)
505                 return 0;
506
507         /* Request the device for the supported frequencies
508          * not really revelant since some devices will report the 5 GHz band
509          * frequencies even if they don't support them.
510          */
511         rvalue =
512             mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r);
513         freq = r.ptr;
514
515         range->num_channels = freq->nr;
516         range->num_frequency = freq->nr;
517
518         /* Frequencies are not listed in the right order. The reordering is probably
519          * firmware dependant and thus should work for everyone.
520          */
521         m = min(IW_MAX_FREQUENCIES, (int) freq->nr);
522         for (i = 0; i < m - 12; i++) {
523                 range->freq[i].m = freq->mhz[12 + i];
524                 range->freq[i].e = 6;
525                 range->freq[i].i = i + 1;
526         }
527         for (i = m - 12; i < m; i++) {
528                 range->freq[i].m = freq->mhz[i - m + 12];
529                 range->freq[i].e = 6;
530                 range->freq[i].i = i + 23;
531         }
532
533         kfree(freq);
534
535         rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
536         data = r.ptr;
537
538         /* We got an array of char. It is NULL terminated. */
539         i = 0;
540         while ((i < IW_MAX_BITRATES) && (*data != 0)) {
541                 /*       the result must be in bps. The card gives us 500Kbps */
542                 range->bitrate[i] = (__s32) (*data >> 1);
543                 range->bitrate[i] *= 1000000;
544                 i++;
545                 data++;
546         }
547
548         range->num_bitrates = i;
549
550         kfree(r.ptr);
551
552         return rvalue;
553 }
554
555 /* Set AP address*/
556
557 static int
558 prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
559                 struct sockaddr *awrq, char *extra)
560 {
561         islpci_private *priv = netdev_priv(ndev);
562         char bssid[6];
563         int rvalue;
564
565         if (awrq->sa_family != ARPHRD_ETHER)
566                 return -EINVAL;
567
568         /* prepare the structure for the set object */
569         memcpy(&bssid[0], awrq->sa_data, 6);
570
571         /* set the bssid -- does this make sense when in AP mode? */
572         rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
573
574         return (rvalue ? rvalue : -EINPROGRESS);        /* Call commit handler */
575 }
576
577 /* get AP address*/
578
579 static int
580 prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
581                 struct sockaddr *awrq, char *extra)
582 {
583         islpci_private *priv = netdev_priv(ndev);
584         union oid_res_t r;
585         int rvalue;
586
587         rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
588
589         memcpy(awrq->sa_data, r.ptr, 6);
590         awrq->sa_family = ARPHRD_ETHER;
591         kfree(r.ptr);
592
593         return rvalue;
594 }
595
596 static int
597 prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
598                  struct iw_param *vwrq, char *extra)
599 {
600         /* hehe the device does this automagicaly */
601         return 0;
602 }
603
604 /* a little helper that will translate our data into a card independent
605  * format that the Wireless Tools will understand. This was inspired by
606  * the "Aironet driver for 4500 and 4800 series cards" (GPL)
607  */
608
609 inline char *
610 prism54_translate_bss(struct net_device *ndev, char *current_ev,
611                       char *end_buf, struct obj_bss *bss, char noise)
612 {
613         struct iw_event iwe;    /* Temporary buffer */
614         short cap;
615         islpci_private *priv = netdev_priv(ndev);
616
617         /* The first entry must be the MAC address */
618         memcpy(iwe.u.ap_addr.sa_data, bss->address, 6);
619         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
620         iwe.cmd = SIOCGIWAP;
621         current_ev =
622             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
623
624         /* The following entries will be displayed in the same order we give them */
625
626         /* The ESSID. */
627         iwe.u.data.length = bss->ssid.length;
628         iwe.u.data.flags = 1;
629         iwe.cmd = SIOCGIWESSID;
630         current_ev = iwe_stream_add_point(current_ev, end_buf,
631                                           &iwe, bss->ssid.octets);
632
633         /* Capabilities */
634 #define CAP_ESS 0x01
635 #define CAP_IBSS 0x02
636 #define CAP_CRYPT 0x10
637
638         /* Mode */
639         cap = bss->capinfo;
640         iwe.u.mode = 0;
641         if (cap & CAP_ESS)
642                 iwe.u.mode = IW_MODE_MASTER;
643         else if (cap & CAP_IBSS)
644                 iwe.u.mode = IW_MODE_ADHOC;
645         iwe.cmd = SIOCGIWMODE;
646         if (iwe.u.mode)
647                 current_ev =
648                     iwe_stream_add_event(current_ev, end_buf, &iwe,
649                                          IW_EV_UINT_LEN);
650
651         /* Encryption capability */
652         if (cap & CAP_CRYPT)
653                 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
654         else
655                 iwe.u.data.flags = IW_ENCODE_DISABLED;
656         iwe.u.data.length = 0;
657         iwe.cmd = SIOCGIWENCODE;
658         current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
659
660         /* Add frequency. (short) bss->channel is the frequency in MHz */
661         iwe.u.freq.m = bss->channel;
662         iwe.u.freq.e = 6;
663         iwe.cmd = SIOCGIWFREQ;
664         current_ev =
665             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
666
667         /* Add quality statistics */
668         iwe.u.qual.level = bss->rssi;
669         iwe.u.qual.noise = noise;
670         /* do a simple SNR for quality */
671         iwe.u.qual.qual = bss->rssi - noise;
672         iwe.cmd = IWEVQUAL;
673         current_ev =
674             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
675
676         if (priv->wpa) {
677                 u8 wpa_ie[MAX_WPA_IE_LEN];
678                 char *buf, *p;
679                 size_t wpa_ie_len;
680                 int i;
681
682                 wpa_ie_len = prism54_wpa_ie_get(priv, bss->address, wpa_ie);
683                 if (wpa_ie_len > 0 &&
684                     (buf = kmalloc(wpa_ie_len * 2 + 10, GFP_ATOMIC))) {
685                         p = buf;
686                         p += sprintf(p, "wpa_ie=");
687                         for (i = 0; i < wpa_ie_len; i++) {
688                                 p += sprintf(p, "%02x", wpa_ie[i]);
689                         }
690                         memset(&iwe, 0, sizeof (iwe));
691                         iwe.cmd = IWEVCUSTOM;
692                         iwe.u.data.length = strlen(buf);
693                         current_ev = iwe_stream_add_point(current_ev, end_buf,
694                                                           &iwe, buf);
695                         kfree(buf);
696                 }
697         }
698
699         return current_ev;
700 }
701
702 int
703 prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
704                  struct iw_point *dwrq, char *extra)
705 {
706         islpci_private *priv = netdev_priv(ndev);
707         int i, rvalue;
708         struct obj_bsslist *bsslist;
709         u32 noise = 0;
710         char *current_ev = extra;
711         union oid_res_t r;
712
713         if (islpci_get_state(priv) < PRV_STATE_INIT) {
714                 /* device is not ready, fail gently */
715                 dwrq->length = 0;
716                 return 0;
717         }
718
719         /* first get the noise value. We will use it to report the link quality */
720         rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
721         noise = r.u;
722
723         /* Ask the device for a list of known bss. We can report at most
724          * IW_MAX_AP=64 to the range struct. But the device won't repport anything
725          * if you change the value of MAXBSS=24. Anyway 24 AP It is probably enough.
726          */
727         rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
728         bsslist = r.ptr;
729
730         /* ok now, scan the list and translate its info */
731         for (i = 0; i < min(IW_MAX_AP, (int) bsslist->nr); i++)
732                 current_ev = prism54_translate_bss(ndev, current_ev,
733                                                    extra + IW_SCAN_MAX_DATA,
734                                                    &(bsslist->bsslist[i]),
735                                                    noise);
736         kfree(bsslist);
737         dwrq->length = (current_ev - extra);
738         dwrq->flags = 0;        /* todo */
739
740         return rvalue;
741 }
742
743 static int
744 prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
745                   struct iw_point *dwrq, char *extra)
746 {
747         islpci_private *priv = netdev_priv(ndev);
748         struct obj_ssid essid;
749
750         memset(essid.octets, 0, 33);
751
752         /* Check if we were asked for `any' */
753         if (dwrq->flags && dwrq->length) {
754                 if (dwrq->length > min(33, IW_ESSID_MAX_SIZE + 1))
755                         return -E2BIG;
756                 essid.length = dwrq->length - 1;
757                 memcpy(essid.octets, extra, dwrq->length);
758         } else
759                 essid.length = 0;
760         
761         if (priv->iw_mode != IW_MODE_MONITOR)
762                 return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid);
763
764         /* If in monitor mode, just save to mib */
765         mgt_set(priv, DOT11_OID_SSID, &essid);
766         return 0;
767
768 }
769
770 static int
771 prism54_get_essid(struct net_device *ndev, struct iw_request_info *info,
772                   struct iw_point *dwrq, char *extra)
773 {
774         islpci_private *priv = netdev_priv(ndev);
775         struct obj_ssid *essid;
776         union oid_res_t r;
777         int rvalue;
778
779         rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r);
780         essid = r.ptr;
781
782         if (essid->length) {
783                 dwrq->flags = 1;        /* set ESSID to ON for Wireless Extensions */
784                 /* if it is to big, trunk it */
785                 dwrq->length = min(IW_ESSID_MAX_SIZE, essid->length + 1);
786         } else {
787                 dwrq->flags = 0;
788                 dwrq->length = 0;
789         }
790         essid->octets[essid->length] = '\0';
791         memcpy(extra, essid->octets, dwrq->length);
792         kfree(essid);
793
794         return rvalue;
795 }
796
797 /* Provides no functionality, just completes the ioctl. In essence this is a 
798  * just a cosmetic ioctl.
799  */
800 static int
801 prism54_set_nick(struct net_device *ndev, struct iw_request_info *info,
802                  struct iw_point *dwrq, char *extra)
803 {
804         islpci_private *priv = netdev_priv(ndev);
805
806         if (dwrq->length > IW_ESSID_MAX_SIZE)
807                 return -E2BIG;
808
809         down_write(&priv->mib_sem);
810         memset(priv->nickname, 0, sizeof (priv->nickname));
811         memcpy(priv->nickname, extra, dwrq->length);
812         up_write(&priv->mib_sem);
813
814         return 0;
815 }
816
817 static int
818 prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
819                  struct iw_point *dwrq, char *extra)
820 {
821         islpci_private *priv = netdev_priv(ndev);
822
823         dwrq->length = 0;
824
825         down_read(&priv->mib_sem);
826         dwrq->length = strlen(priv->nickname) + 1;
827         memcpy(extra, priv->nickname, dwrq->length);
828         up_read(&priv->mib_sem);
829
830         return 0;
831 }
832
833 /* Set the allowed Bitrates */
834
835 static int
836 prism54_set_rate(struct net_device *ndev,
837                  struct iw_request_info *info,
838                  struct iw_param *vwrq, char *extra)
839 {
840
841         islpci_private *priv = netdev_priv(ndev);
842         u32 rate, profile;
843         char *data;
844         int ret, i;
845         union oid_res_t r;
846         
847         if (vwrq->value == -1) {
848                 /* auto mode. No limit. */
849                 profile = 1;
850                 return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
851         }
852         
853         if ((ret =
854              mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r)))
855                 return ret;
856                 
857         rate = (u32) (vwrq->value / 500000);
858         data = r.ptr;
859         i = 0;
860         
861         while (data[i]) {
862                 if (rate && (data[i] == rate)) {
863                         break;
864                 }
865                 if (vwrq->value == i) {
866                         break;
867                 }
868                 data[i] |= 0x80;
869                 i++;
870         }
871                 
872         if (!data[i]) {
873                 return -EINVAL;
874         }
875         
876         data[i] |= 0x80;
877         data[i + 1] = 0;
878         
879         /* Now, check if we want a fixed or auto value */
880         if (vwrq->fixed) {
881                 data[0] = data[i];
882                 data[1] = 0;
883         }
884
885 /*
886         i = 0;
887         printk("prism54 rate: ");
888         while(data[i]) {
889                 printk("%u ", data[i]);
890                 i++;
891         }
892         printk("0\n");
893 */      
894         profile = -1;
895         ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
896         ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data);
897         ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data);
898         
899         kfree(r.ptr);
900         
901         return ret;
902 }
903
904 /* Get the current bit rate */
905 static int
906 prism54_get_rate(struct net_device *ndev,
907                  struct iw_request_info *info,
908                  struct iw_param *vwrq, char *extra)
909 {
910         islpci_private *priv = netdev_priv(ndev);
911         int rvalue;
912         char *data;
913         union oid_res_t r;
914
915         /* Get the current bit rate */
916         if ((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r)))
917                 return rvalue;
918         vwrq->value = r.u * 500000;
919
920         /* request the device for the enabled rates */
921         if ((rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r)))
922                 return rvalue;
923         data = r.ptr;
924         vwrq->fixed = (data[0] != 0) && (data[1] == 0);
925         kfree(r.ptr);
926         
927         return 0;
928 }
929
930 static int
931 prism54_set_rts(struct net_device *ndev, struct iw_request_info *info,
932                 struct iw_param *vwrq, char *extra)
933 {
934         islpci_private *priv = netdev_priv(ndev);
935
936         return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value);
937 }
938
939 static int
940 prism54_get_rts(struct net_device *ndev, struct iw_request_info *info,
941                 struct iw_param *vwrq, char *extra)
942 {
943         islpci_private *priv = netdev_priv(ndev);
944         union oid_res_t r;
945         int rvalue;
946
947         /* get the rts threshold */
948         rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r);
949         vwrq->value = r.u;
950
951         return rvalue;
952 }
953
954 static int
955 prism54_set_frag(struct net_device *ndev, struct iw_request_info *info,
956                  struct iw_param *vwrq, char *extra)
957 {
958         islpci_private *priv = netdev_priv(ndev);
959
960         return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value);
961 }
962
963 static int
964 prism54_get_frag(struct net_device *ndev, struct iw_request_info *info,
965                  struct iw_param *vwrq, char *extra)
966 {
967         islpci_private *priv = netdev_priv(ndev);
968         union oid_res_t r;
969         int rvalue;
970
971         rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r);
972         vwrq->value = r.u;
973
974         return rvalue;
975 }
976
977 /* Here we have (min,max) = max retries for (small frames, big frames). Where
978  * big frame <=>  bigger than the rts threshold
979  * small frame <=>  smaller than the rts threshold
980  * This is not really the behavior expected by the wireless tool but it seems
981  * to be a common behavior in other drivers.
982  * 
983  * It seems that playing with this tends to hang the card -> DISABLED
984  */
985
986 static int
987 prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
988                   struct iw_param *vwrq, char *extra)
989 {
990         islpci_private *priv = netdev_priv(ndev);
991         u32 slimit = 0, llimit = 0;     /* short and long limit */
992         u32 lifetime = 0;
993         int rvalue = 0;
994
995         if (vwrq->disabled)
996                 /* we cannot disable this feature */
997                 return -EINVAL;
998
999         if (vwrq->flags & IW_RETRY_LIMIT) {
1000                 if (vwrq->flags & IW_RETRY_MIN)
1001                         slimit = vwrq->value;
1002                 else if (vwrq->flags & IW_RETRY_MAX)
1003                         llimit = vwrq->value;
1004                 else {
1005                         /* we are asked to set both */
1006                         slimit = vwrq->value;
1007                         llimit = vwrq->value;
1008                 }
1009         }
1010         if (vwrq->flags & IW_RETRY_LIFETIME)
1011                 /* Wireless tools use us unit while the device uses 1024 us unit */
1012                 lifetime = vwrq->value / 1024;
1013
1014         /* now set what is requested */
1015
1016         if (slimit != 0)
1017                 rvalue =
1018                     mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit);
1019         if (llimit != 0)
1020                 rvalue |=
1021                     mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit);
1022         if (lifetime != 0)
1023                 rvalue |=
1024                     mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0,
1025                                     &lifetime);
1026
1027         return rvalue;
1028 }
1029
1030 static int
1031 prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
1032                   struct iw_param *vwrq, char *extra)
1033 {
1034         islpci_private *priv = netdev_priv(ndev);
1035         union oid_res_t r;
1036         int rvalue = 0;
1037         vwrq->disabled = 0;     /* It cannot be disabled */
1038
1039         if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
1040                 /* we are asked for the life time */
1041                 rvalue =
1042                     mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
1043                 vwrq->value = r.u * 1024;
1044                 vwrq->flags = IW_RETRY_LIFETIME;
1045         } else if ((vwrq->flags & IW_RETRY_MAX)) {
1046                 /* we are asked for the long retry limit */
1047                 rvalue |=
1048                     mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
1049                 vwrq->value = r.u;
1050                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
1051         } else {
1052                 /* default. get the  short retry limit */
1053                 rvalue |=
1054                     mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
1055                 vwrq->value = r.u;
1056                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
1057         }
1058
1059         return rvalue;
1060 }
1061
1062 static int
1063 prism54_set_encode(struct net_device *ndev, struct iw_request_info *info,
1064                    struct iw_point *dwrq, char *extra)
1065 {
1066         islpci_private *priv = netdev_priv(ndev);
1067         int rvalue = 0, force = 0;
1068         int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
1069         union oid_res_t r;
1070
1071         /* with the new API, it's impossible to get a NULL pointer.
1072          * New version of iwconfig set the IW_ENCODE_NOKEY flag
1073          * when no key is given, but older versions don't. */
1074
1075         if (dwrq->length > 0) {
1076                 /* we have a key to set */
1077                 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1078                 int current_index;
1079                 struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
1080
1081                 /* get the current key index */
1082                 rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
1083                 current_index = r.u;
1084                 /* Verify that the key is not marked as invalid */
1085                 if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
1086                         key.length = dwrq->length > sizeof (key.key) ?
1087                             sizeof (key.key) : dwrq->length;
1088                         memcpy(key.key, extra, key.length);
1089                         if (key.length == 32)
1090                                 /* we want WPA-PSK */
1091                                 key.type = DOT11_PRIV_TKIP;
1092                         if ((index < 0) || (index > 3))
1093                                 /* no index provided use the current one */
1094                                 index = current_index;
1095
1096                         /* now send the key to the card  */
1097                         rvalue |=
1098                             mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
1099                                             &key);
1100                 }
1101                 /*
1102                  * If a valid key is set, encryption should be enabled 
1103                  * (user may turn it off later).
1104                  * This is also how "iwconfig ethX key on" works
1105                  */
1106                 if ((index == current_index) && (key.length > 0))
1107                         force = 1;
1108         } else {
1109                 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1110                 if ((index >= 0) && (index <= 3)) {
1111                         /* we want to set the key index */
1112                         rvalue |=
1113                             mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
1114                                             &index);
1115                 } else {
1116                         if (!dwrq->flags & IW_ENCODE_MODE) {
1117                                 /* we cannot do anything. Complain. */
1118                                 return -EINVAL;
1119                         }
1120                 }
1121         }
1122
1123         /* now read the flags     */
1124         if (dwrq->flags & IW_ENCODE_DISABLED) {
1125                 /* Encoding disabled, 
1126                  * authen = DOT11_AUTH_OS;
1127                  * invoke = 0;
1128                  * exunencrypt = 0; */
1129         }
1130         if (dwrq->flags & IW_ENCODE_OPEN)
1131                 /* Encode but accept non-encoded packets. No auth */
1132                 invoke = 1;
1133         if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) {
1134                 /* Refuse non-encoded packets. Auth */
1135                 authen = DOT11_AUTH_BOTH;
1136                 invoke = 1;
1137                 exunencrypt = 1;
1138         }
1139         /* do the change if requested  */
1140         if ((dwrq->flags & IW_ENCODE_MODE) || force) {
1141                 rvalue |=
1142                     mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
1143                 rvalue |=
1144                     mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
1145                 rvalue |=
1146                     mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
1147                                     &exunencrypt);
1148         }
1149         return rvalue;
1150 }
1151
1152 static int
1153 prism54_get_encode(struct net_device *ndev, struct iw_request_info *info,
1154                    struct iw_point *dwrq, char *extra)
1155 {
1156         islpci_private *priv = netdev_priv(ndev);
1157         struct obj_key *key;
1158         u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1159         u32 authen = 0, invoke = 0, exunencrypt = 0;
1160         int rvalue;
1161         union oid_res_t r;
1162
1163         /* first get the flags */
1164         rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
1165         authen = r.u;
1166         rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
1167         invoke = r.u;
1168         rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
1169         exunencrypt = r.u;
1170
1171         if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt)
1172                 dwrq->flags = IW_ENCODE_RESTRICTED;
1173         else if ((authen == DOT11_AUTH_OS) && !exunencrypt) {
1174                 if (invoke)
1175                         dwrq->flags = IW_ENCODE_OPEN;
1176                 else
1177                         dwrq->flags = IW_ENCODE_DISABLED;
1178         } else
1179                 /* The card should not work in this state */
1180                 dwrq->flags = 0;
1181
1182         /* get the current device key index */
1183         rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
1184         devindex = r.u;
1185         /* Now get the key, return it */
1186         if ((index < 0) || (index > 3))
1187                 /* no index provided, use the current one */
1188                 index = devindex;
1189         rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
1190         key = r.ptr;
1191         dwrq->length = key->length;
1192         memcpy(extra, key->key, dwrq->length);
1193         kfree(key);
1194         /* return the used key index */
1195         dwrq->flags |= devindex + 1;
1196
1197         return rvalue;
1198 }
1199
1200 static int
1201 prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info,
1202                     struct iw_param *vwrq, char *extra)
1203 {
1204         islpci_private *priv = netdev_priv(ndev);
1205         union oid_res_t r;
1206         int rvalue;
1207
1208         rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r);
1209         /* intersil firmware operates in 0.25 dBm (1/4 dBm) */
1210         vwrq->value = (s32) r.u / 4;
1211         vwrq->fixed = 1;
1212         /* radio is not turned of
1213          * btw: how is possible to turn off only the radio 
1214          */
1215         vwrq->disabled = 0;
1216
1217         return rvalue;
1218 }
1219
1220 static int
1221 prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info,
1222                     struct iw_param *vwrq, char *extra)
1223 {
1224         islpci_private *priv = netdev_priv(ndev);
1225         s32 u = vwrq->value;
1226
1227         /* intersil firmware operates in 0.25 dBm (1/4) */
1228         u *= 4;
1229         if (vwrq->disabled) {
1230                 /* don't know how to disable radio */
1231                 printk(KERN_DEBUG
1232                        "%s: %s() disabling radio is not yet supported.\n",
1233                        priv->ndev->name, __FUNCTION__);
1234                 return -ENOTSUPP;
1235         } else if (vwrq->fixed)
1236                 /* currently only fixed value is supported */
1237                 return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u);
1238         else {
1239                 printk(KERN_DEBUG
1240                        "%s: %s() auto power will be implemented later.\n",
1241                        priv->ndev->name, __FUNCTION__);
1242                 return -ENOTSUPP;
1243         }
1244 }
1245
1246 static int
1247 prism54_reset(struct net_device *ndev, struct iw_request_info *info,
1248               __u32 * uwrq, char *extra)
1249 {
1250         islpci_reset(netdev_priv(ndev), 0);
1251
1252         return 0;
1253 }
1254
1255 static int
1256 prism54_get_oid(struct net_device *ndev, struct iw_request_info *info,
1257                 struct iw_point *dwrq, char *extra)
1258 {
1259         union oid_res_t r;
1260         int rvalue;
1261         enum oid_num_t n = dwrq->flags;
1262
1263         rvalue = mgt_get_request((islpci_private *) ndev->priv, n, 0, NULL, &r);
1264         dwrq->length = mgt_response_to_str(n, &r, extra);
1265         if ((isl_oid[n].flags & OID_FLAG_TYPE) != OID_TYPE_U32)
1266                 kfree(r.ptr);
1267         return rvalue;
1268 }
1269
1270 static int
1271 prism54_set_u32(struct net_device *ndev, struct iw_request_info *info,
1272                    __u32 * uwrq, char *extra)
1273 {
1274         /*
1275            u32 *i = (int *) extra;
1276            int param = *i;
1277            int u = *(i + 1);
1278          */
1279         u32 oid = uwrq[0], u = uwrq[1];
1280
1281         return mgt_set_request((islpci_private *) ndev->priv, oid, 0, &u);
1282 }
1283
1284 static int
1285 prism54_set_raw(struct net_device *ndev, struct iw_request_info *info,
1286                 struct iw_point *dwrq, char *extra)
1287 {
1288         u32 oid = dwrq->flags;
1289
1290         return mgt_set_request((islpci_private *) ndev->priv, oid, 0, extra);
1291 }
1292
1293 void
1294 prism54_acl_init(struct islpci_acl *acl)
1295 {
1296         sema_init(&acl->sem, 1);
1297         INIT_LIST_HEAD(&acl->mac_list);
1298         acl->size = 0;
1299         acl->policy = MAC_POLICY_OPEN;
1300 }
1301
1302 static void
1303 prism54_clear_mac(struct islpci_acl *acl)
1304 {
1305         struct list_head *ptr, *next;
1306         struct mac_entry *entry;
1307
1308         if (down_interruptible(&acl->sem))
1309                 return;
1310
1311         if (acl->size == 0) {
1312                 up(&acl->sem);
1313                 return;
1314         }
1315
1316         for (ptr = acl->mac_list.next, next = ptr->next;
1317              ptr != &acl->mac_list; ptr = next, next = ptr->next) {
1318                 entry = list_entry(ptr, struct mac_entry, _list);
1319                 list_del(ptr);
1320                 kfree(entry);
1321         }
1322         acl->size = 0;
1323         up(&acl->sem);
1324 }
1325
1326 void
1327 prism54_acl_clean(struct islpci_acl *acl)
1328 {
1329         prism54_clear_mac(acl);
1330 }
1331
1332 static int
1333 prism54_add_mac(struct net_device *ndev, struct iw_request_info *info,
1334                 struct sockaddr *awrq, char *extra)
1335 {
1336         islpci_private *priv = netdev_priv(ndev);
1337         struct islpci_acl *acl = &priv->acl;
1338         struct mac_entry *entry;
1339         struct sockaddr *addr = (struct sockaddr *) extra;
1340
1341         if (addr->sa_family != ARPHRD_ETHER)
1342                 return -EOPNOTSUPP;
1343
1344         entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL);
1345         if (entry == NULL)
1346                 return -ENOMEM;
1347
1348         memcpy(entry->addr, addr->sa_data, ETH_ALEN);
1349
1350         if (down_interruptible(&acl->sem)) {
1351                 kfree(entry);
1352                 return -ERESTARTSYS;
1353         }
1354         list_add_tail(&entry->_list, &acl->mac_list);
1355         acl->size++;
1356         up(&acl->sem);
1357
1358         return 0;
1359 }
1360
1361 static int
1362 prism54_del_mac(struct net_device *ndev, struct iw_request_info *info,
1363                 struct sockaddr *awrq, char *extra)
1364 {
1365         islpci_private *priv = netdev_priv(ndev);
1366         struct islpci_acl *acl = &priv->acl;
1367         struct mac_entry *entry;
1368         struct list_head *ptr;
1369         struct sockaddr *addr = (struct sockaddr *) extra;
1370
1371         if (addr->sa_family != ARPHRD_ETHER)
1372                 return -EOPNOTSUPP;
1373
1374         if (down_interruptible(&acl->sem))
1375                 return -ERESTARTSYS;
1376         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1377                 entry = list_entry(ptr, struct mac_entry, _list);
1378
1379                 if (memcmp(entry->addr, addr->sa_data, ETH_ALEN) == 0) {
1380                         list_del(ptr);
1381                         acl->size--;
1382                         kfree(entry);
1383                         up(&acl->sem);
1384                         return 0;
1385                 }
1386         }
1387         up(&acl->sem);
1388         return -EINVAL;
1389 }
1390
1391 static int
1392 prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
1393                 struct iw_point *dwrq, char *extra)
1394 {
1395         islpci_private *priv = netdev_priv(ndev);
1396         struct islpci_acl *acl = &priv->acl;
1397         struct mac_entry *entry;
1398         struct list_head *ptr;
1399         struct sockaddr *dst = (struct sockaddr *) extra;
1400
1401         dwrq->length = 0;
1402
1403         if (down_interruptible(&acl->sem))
1404                 return -ERESTARTSYS;
1405
1406         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1407                 entry = list_entry(ptr, struct mac_entry, _list);
1408
1409                 memcpy(dst->sa_data, entry->addr, ETH_ALEN);
1410                 dst->sa_family = ARPHRD_ETHER;
1411                 dwrq->length++;
1412                 dst++;
1413         }
1414         up(&acl->sem);
1415         return 0;
1416 }
1417
1418 /* Setting policy also clears the MAC acl, even if we don't change the defaut
1419  * policy
1420  */
1421
1422 static int
1423 prism54_set_policy(struct net_device *ndev, struct iw_request_info *info,
1424                    __u32 * uwrq, char *extra)
1425 {
1426         islpci_private *priv = netdev_priv(ndev);
1427         struct islpci_acl *acl = &priv->acl;
1428         u32 mlmeautolevel;
1429
1430         prism54_clear_mac(acl);
1431
1432         if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT))
1433                 return -EINVAL;
1434
1435         down_write(&priv->mib_sem);
1436
1437         acl->policy = *uwrq;
1438
1439         /* the ACL code needs an intermediate mlmeautolevel */
1440         if ((priv->iw_mode == IW_MODE_MASTER) &&
1441             (acl->policy != MAC_POLICY_OPEN))
1442                 mlmeautolevel = DOT11_MLME_INTERMEDIATE;
1443         else
1444                 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
1445         if (priv->wpa)
1446                 mlmeautolevel = DOT11_MLME_EXTENDED;
1447         mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
1448         /* restart the card with our new policy */
1449         mgt_commit(priv);
1450         up_write(&priv->mib_sem);
1451
1452         return 0;
1453 }
1454
1455 static int
1456 prism54_get_policy(struct net_device *ndev, struct iw_request_info *info,
1457                    __u32 * uwrq, char *extra)
1458 {
1459         islpci_private *priv = netdev_priv(ndev);
1460         struct islpci_acl *acl = &priv->acl;
1461
1462         *uwrq = acl->policy;
1463
1464         return 0;
1465 }
1466
1467 /* Return 1 only if client should be accepted. */
1468
1469 static int
1470 prism54_mac_accept(struct islpci_acl *acl, char *mac)
1471 {
1472         struct list_head *ptr;
1473         struct mac_entry *entry;
1474         int res = 0;
1475
1476         if (down_interruptible(&acl->sem))
1477                 return -ERESTARTSYS;
1478
1479         if (acl->policy == MAC_POLICY_OPEN) {
1480                 up(&acl->sem);
1481                 return 1;
1482         }
1483
1484         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1485                 entry = list_entry(ptr, struct mac_entry, _list);
1486                 if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
1487                         res = 1;
1488                         break;
1489                 }
1490         }
1491         res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res;
1492         up(&acl->sem);
1493
1494         return res;
1495 }
1496
1497 static int
1498 prism54_kick_all(struct net_device *ndev, struct iw_request_info *info,
1499                  struct iw_point *dwrq, char *extra)
1500 {
1501         struct obj_mlme *mlme;
1502         int rvalue;
1503
1504         mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
1505         if (mlme == NULL)
1506                 return -ENOMEM;
1507
1508         /* Tell the card to kick every client */
1509         mlme->id = 0;
1510         rvalue =
1511             mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
1512         kfree(mlme);
1513
1514         return rvalue;
1515 }
1516
1517 static int
1518 prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info,
1519                  struct sockaddr *awrq, char *extra)
1520 {
1521         struct obj_mlme *mlme;
1522         struct sockaddr *addr = (struct sockaddr *) extra;
1523         int rvalue;
1524
1525         if (addr->sa_family != ARPHRD_ETHER)
1526                 return -EOPNOTSUPP;
1527
1528         mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
1529         if (mlme == NULL)
1530                 return -ENOMEM;
1531
1532         /* Tell the card to only kick the corresponding bastard */
1533         memcpy(mlme->address, addr->sa_data, ETH_ALEN);
1534         mlme->id = -1;
1535         rvalue =
1536             mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
1537
1538         kfree(mlme);
1539
1540         return rvalue;
1541 }
1542
1543 /* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */
1544
1545 static inline void
1546 format_event(islpci_private *priv, char *dest, const char *str,
1547              const struct obj_mlme *mlme, u16 *length, int error)
1548 {
1549         const u8 *a = mlme->address;
1550         int n = snprintf(dest, IW_CUSTOM_MAX,
1551                          "%s %s %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X %s (%2.2X)",
1552                          str,
1553                          ((priv->iw_mode == IW_MODE_MASTER) ? "from" : "to"),
1554                          a[0], a[1], a[2], a[3], a[4], a[5],
1555                          (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
1556                           : ""), mlme->code);
1557         BUG_ON(n > IW_CUSTOM_MAX);
1558         *length = n;
1559 }
1560
1561 static void
1562 send_formatted_event(islpci_private *priv, const char *str,
1563                      const struct obj_mlme *mlme, int error)
1564 {
1565         union iwreq_data wrqu;
1566
1567         wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
1568         if (!wrqu.data.pointer)
1569                 return;
1570         wrqu.data.length = 0;
1571         format_event(priv, wrqu.data.pointer, str, mlme, &wrqu.data.length,
1572                      error);
1573         wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
1574         kfree(wrqu.data.pointer);
1575 }
1576
1577 static void
1578 send_simple_event(islpci_private *priv, const char *str)
1579 {
1580         union iwreq_data wrqu;
1581         int n = strlen(str);
1582
1583         wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
1584         if (!wrqu.data.pointer)
1585                 return;
1586         BUG_ON(n > IW_CUSTOM_MAX);
1587         wrqu.data.length = n;
1588         strcpy(wrqu.data.pointer, str);
1589         wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
1590         kfree(wrqu.data.pointer);
1591 }
1592
1593 static void
1594 link_changed(struct net_device *ndev, u32 bitrate)
1595 {
1596         islpci_private *priv = netdev_priv(ndev);
1597
1598         if (bitrate) {
1599                 if (priv->iw_mode == IW_MODE_INFRA) {
1600                         union iwreq_data uwrq;
1601                         prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq,
1602                                         NULL);
1603                         wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL);
1604                 } else
1605                         send_simple_event(netdev_priv(ndev),
1606                                           "Link established");
1607         } else
1608                 send_simple_event(netdev_priv(ndev), "Link lost");
1609 }
1610
1611 /* Beacon/ProbeResp payload header */
1612 struct ieee80211_beacon_phdr {
1613         u8 timestamp[8];
1614         u16 beacon_int;
1615         u16 capab_info;
1616 } __attribute__ ((packed));
1617
1618 #define WLAN_EID_GENERIC 0xdd
1619 static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
1620
1621 #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
1622 #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
1623
1624 void
1625 prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
1626                    u8 *wpa_ie, size_t wpa_ie_len)
1627 {
1628         struct list_head *ptr;
1629         struct islpci_bss_wpa_ie *bss = NULL;
1630
1631         if (wpa_ie_len > MAX_WPA_IE_LEN)
1632                 wpa_ie_len = MAX_WPA_IE_LEN;
1633
1634         if (down_interruptible(&priv->wpa_sem))
1635                 return;
1636
1637         /* try to use existing entry */
1638         list_for_each(ptr, &priv->bss_wpa_list) {
1639                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1640                 if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
1641                         list_move(&bss->list, &priv->bss_wpa_list);
1642                         break;
1643                 }
1644                 bss = NULL;
1645         }
1646
1647         if (bss == NULL) {
1648                 /* add a new BSS entry; if max number of entries is already
1649                  * reached, replace the least recently updated */
1650                 if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
1651                         bss = list_entry(priv->bss_wpa_list.prev,
1652                                          struct islpci_bss_wpa_ie, list);
1653                         list_del(&bss->list);
1654                 } else {
1655                         bss = kmalloc(sizeof (*bss), GFP_ATOMIC);
1656                         if (bss != NULL) {
1657                                 priv->num_bss_wpa++;
1658                                 memset(bss, 0, sizeof (*bss));
1659                         }
1660                 }
1661                 if (bss != NULL) {
1662                         memcpy(bss->bssid, bssid, ETH_ALEN);
1663                         list_add(&bss->list, &priv->bss_wpa_list);
1664                 }
1665         }
1666
1667         if (bss != NULL) {
1668                 memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
1669                 bss->wpa_ie_len = wpa_ie_len;
1670                 bss->last_update = jiffies;
1671         } else {
1672                 printk(KERN_DEBUG "Failed to add BSS WPA entry for " MACSTR
1673                        "\n", MAC2STR(bssid));
1674         }
1675
1676         /* expire old entries from WPA list */
1677         while (priv->num_bss_wpa > 0) {
1678                 bss = list_entry(priv->bss_wpa_list.prev,
1679                                  struct islpci_bss_wpa_ie, list);
1680                 if (!time_after(jiffies, bss->last_update + 60 * HZ))
1681                         break;
1682
1683                 list_del(&bss->list);
1684                 priv->num_bss_wpa--;
1685                 kfree(bss);
1686         }
1687
1688         up(&priv->wpa_sem);
1689 }
1690
1691 size_t
1692 prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
1693 {
1694         struct list_head *ptr;
1695         struct islpci_bss_wpa_ie *bss = NULL;
1696         size_t len = 0;
1697
1698         if (down_interruptible(&priv->wpa_sem))
1699                 return 0;
1700
1701         list_for_each(ptr, &priv->bss_wpa_list) {
1702                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1703                 if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
1704                         break;
1705                 bss = NULL;
1706         }
1707         if (bss) {
1708                 len = bss->wpa_ie_len;
1709                 memcpy(wpa_ie, bss->wpa_ie, len);
1710         }
1711         up(&priv->wpa_sem);
1712
1713         return len;
1714 }
1715
1716 void
1717 prism54_wpa_ie_init(islpci_private *priv)
1718 {
1719         INIT_LIST_HEAD(&priv->bss_wpa_list);
1720         sema_init(&priv->wpa_sem, 1);
1721 }
1722
1723 void
1724 prism54_wpa_ie_clean(islpci_private *priv)
1725 {
1726         struct list_head *ptr, *n;
1727
1728         list_for_each_safe(ptr, n, &priv->bss_wpa_list) {
1729                 struct islpci_bss_wpa_ie *bss;
1730                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1731                 kfree(bss);
1732         }
1733 }
1734
1735 static void
1736 prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
1737                          u8 *payload, size_t len)
1738 {
1739         struct ieee80211_beacon_phdr *hdr;
1740         u8 *pos, *end;
1741
1742         if (!priv->wpa)
1743                 return;
1744
1745         hdr = (struct ieee80211_beacon_phdr *) payload;
1746         pos = (u8 *) (hdr + 1);
1747         end = payload + len;
1748         while (pos < end) {
1749                 if (pos + 2 + pos[1] > end) {
1750                         printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
1751                                "for " MACSTR "\n", MAC2STR(addr));
1752                         return;
1753                 }
1754                 if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
1755                     memcmp(pos + 2, wpa_oid, 4) == 0) {
1756                         prism54_wpa_ie_add(priv, addr, pos, pos[1] + 2);
1757                         return;
1758                 }
1759                 pos += 2 + pos[1];
1760         }
1761 }
1762
1763 static void
1764 handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid)
1765 {
1766         if (((mlme->state == DOT11_STATE_AUTHING) ||
1767              (mlme->state == DOT11_STATE_ASSOCING))
1768             && mgt_mlme_answer(priv)) {
1769                 /* Someone is requesting auth and we must respond. Just send back
1770                  * the trap with error code set accordingly.
1771                  */
1772                 mlme->code = prism54_mac_accept(&priv->acl,
1773                                                 mlme->address) ? 0 : 1;
1774                 mgt_set_request(priv, oid, 0, mlme);
1775         }
1776 }
1777
1778 int
1779 prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
1780                             char *data)
1781 {
1782         struct obj_mlme *mlme = (struct obj_mlme *) data;
1783         size_t len;
1784         u8 *payload, *pos = (u8 *) (mlme + 1);
1785
1786         len = pos[0] | (pos[1] << 8);   /* little endian data length */
1787         payload = pos + 2;
1788
1789         /* I think all trapable objects are listed here.
1790          * Some oids have a EX version. The difference is that they are emitted
1791          * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL)
1792          * with more info.
1793          * The few events already defined by the wireless tools are not really
1794          * suited. We use the more flexible custom event facility.
1795          */
1796
1797         /* I fear prism54_process_bss_data won't work with big endian data */
1798         if ((oid == DOT11_OID_BEACON) || (oid == DOT11_OID_PROBE))
1799                 prism54_process_bss_data(priv, oid, mlme->address,
1800                                          payload, len);
1801
1802         mgt_le_to_cpu(isl_oid[oid].flags & OID_FLAG_TYPE, (void *) mlme);
1803
1804         switch (oid) {
1805
1806         case GEN_OID_LINKSTATE:
1807                 link_changed(priv->ndev, (u32) *data);
1808                 break;
1809
1810         case DOT11_OID_MICFAILURE:
1811                 send_simple_event(priv, "Mic failure");
1812                 break;
1813
1814         case DOT11_OID_DEAUTHENTICATE:
1815                 send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
1816                 break;
1817
1818         case DOT11_OID_AUTHENTICATE:
1819                 handle_request(priv, mlme, oid);
1820                 send_formatted_event(priv, "Authenticate request", mlme, 1);
1821                 break;
1822
1823         case DOT11_OID_DISASSOCIATE:
1824                 send_formatted_event(priv, "Disassociate request", mlme, 0);
1825                 break;
1826
1827         case DOT11_OID_ASSOCIATE:
1828                 handle_request(priv, mlme, oid);
1829                 send_formatted_event(priv, "Associate request", mlme, 1);
1830                 break;
1831
1832         case DOT11_OID_REASSOCIATE:
1833                 handle_request(priv, mlme, oid);
1834                 send_formatted_event(priv, "ReAssociate request", mlme, 1);
1835                 break;
1836
1837         case DOT11_OID_BEACON:
1838                 send_formatted_event(priv,
1839                                      "Received a beacon from an unkown AP",
1840                                      mlme, 0);
1841                 break;
1842
1843         case DOT11_OID_PROBE:
1844                 /* we received a probe from a client. */
1845                 send_formatted_event(priv, "Received a probe from client", mlme,
1846                                      0);
1847                 break;
1848
1849                 /* Note : the following should never happen since we don't run the card in
1850                  * extended mode.
1851                  * Note : "mlme" is actually a "struct obj_mlmeex *" here, but this
1852                  * is backward compatible layout-wise with "struct obj_mlme".
1853                  */
1854
1855         case DOT11_OID_DEAUTHENTICATEEX:
1856                 send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
1857                 break;
1858
1859         case DOT11_OID_AUTHENTICATEEX:
1860                 handle_request(priv, mlme, oid);
1861                 send_formatted_event(priv, "Authenticate request", mlme, 1);
1862                 break;
1863
1864         case DOT11_OID_DISASSOCIATEEX:
1865                 send_formatted_event(priv, "Disassociate request", mlme, 0);
1866                 break;
1867
1868         case DOT11_OID_ASSOCIATEEX:
1869                 handle_request(priv, mlme, oid);
1870                 send_formatted_event(priv, "Associate request", mlme, 1);
1871                 break;
1872
1873         case DOT11_OID_REASSOCIATEEX:
1874                 handle_request(priv, mlme, oid);
1875                 send_formatted_event(priv, "Reassociate request", mlme, 1);
1876                 break;
1877
1878         default:
1879                 return -EINVAL;
1880         }
1881
1882         return 0;
1883 }
1884
1885 /*
1886  * Process a device trap.  This is called via schedule_work(), outside of
1887  * interrupt context, no locks held.
1888  */
1889 void
1890 prism54_process_trap(void *data)
1891 {
1892         struct islpci_mgmtframe *frame = data;
1893         struct net_device *ndev = frame->ndev;
1894         enum oid_num_t n = mgt_oidtonum(frame->header->oid);
1895
1896         prism54_process_trap_helper(netdev_priv(ndev), n, frame->data);
1897         islpci_mgt_release(frame);
1898 }
1899
1900 int
1901 prism54_set_mac_address(struct net_device *ndev, void *addr)
1902 {
1903         islpci_private *priv = netdev_priv(ndev);
1904         int ret;
1905
1906         if (ndev->addr_len != 6)
1907                 return -EINVAL;
1908         ret = mgt_set_request(priv, GEN_OID_MACADDRESS, 0,
1909                               &((struct sockaddr *) addr)->sa_data);
1910         if (!ret)
1911                 memcpy(priv->ndev->dev_addr,
1912                        &((struct sockaddr *) addr)->sa_data, 6);
1913
1914         return ret;
1915 }
1916
1917 int
1918 prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
1919                 __u32 * uwrq, char *extra)
1920 {
1921         islpci_private *priv = netdev_priv(ndev);
1922
1923         down_write(&priv->mib_sem);
1924
1925         priv->wpa = *uwrq;
1926         if (priv->wpa) {
1927                 u32 l = DOT11_MLME_EXTENDED;
1928                 mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &l);
1929         }
1930         /* restart the card with new level. Needed ? */
1931         mgt_commit(priv);
1932         up_write(&priv->mib_sem);
1933
1934         return 0;
1935 }
1936
1937 int
1938 prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
1939                 __u32 * uwrq, char *extra)
1940 {
1941         islpci_private *priv = netdev_priv(ndev);
1942         *uwrq = priv->wpa;
1943         return 0;
1944 }
1945
1946 int
1947 prism54_set_prismhdr(struct net_device *ndev, struct iw_request_info *info,
1948                      __u32 * uwrq, char *extra)
1949 {
1950         islpci_private *priv = netdev_priv(ndev);
1951         priv->monitor_type =
1952             (*uwrq ? ARPHRD_IEEE80211_PRISM : ARPHRD_IEEE80211);
1953         if (priv->iw_mode == IW_MODE_MONITOR)
1954                 priv->ndev->type = priv->monitor_type;
1955
1956         return 0;
1957 }
1958
1959 int
1960 prism54_get_prismhdr(struct net_device *ndev, struct iw_request_info *info,
1961                      __u32 * uwrq, char *extra)
1962 {
1963         islpci_private *priv = netdev_priv(ndev);
1964         *uwrq = (priv->monitor_type == ARPHRD_IEEE80211_PRISM);
1965         return 0;
1966 }
1967
1968 int
1969 prism54_set_maxframeburst(struct net_device *ndev, struct iw_request_info *info,
1970                           __u32 * uwrq, char *extra)
1971 {
1972         islpci_private *priv = netdev_priv(ndev);
1973         u32 max_burst;
1974
1975         max_burst = (*uwrq) ? *uwrq : CARD_DEFAULT_MAXFRAMEBURST;
1976         mgt_set_request(priv, DOT11_OID_MAXFRAMEBURST, 0, &max_burst);
1977
1978         return -EINPROGRESS; /* Call commit handler */
1979 }
1980
1981 int
1982 prism54_get_maxframeburst(struct net_device *ndev, struct iw_request_info *info,
1983                           __u32 * uwrq, char *extra)
1984 {
1985         islpci_private *priv = netdev_priv(ndev);
1986         union oid_res_t r;
1987         int rvalue;
1988         
1989         rvalue = mgt_get_request(priv, DOT11_OID_MAXFRAMEBURST, 0, NULL, &r);
1990         *uwrq = r.u;
1991         
1992         return rvalue;
1993 }
1994
1995 int
1996 prism54_set_profile(struct net_device *ndev, struct iw_request_info *info,
1997                     __u32 * uwrq, char *extra)
1998 {
1999         islpci_private *priv = netdev_priv(ndev);
2000         u32 profile;
2001
2002         profile = (*uwrq) ? *uwrq : CARD_DEFAULT_PROFILE;
2003         mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
2004         
2005         return -EINPROGRESS; /* Call commit handler */
2006 }
2007
2008 int
2009 prism54_get_profile(struct net_device *ndev, struct iw_request_info *info,
2010                     __u32 * uwrq, char *extra)
2011 {
2012         islpci_private *priv = netdev_priv(ndev);
2013         union oid_res_t r;
2014         int rvalue;
2015
2016         rvalue = mgt_get_request(priv, DOT11_OID_PROFILES, 0, NULL, &r);
2017         *uwrq = r.u;
2018
2019         return rvalue;
2020 }
2021
2022 int
2023 prism54_debug_oid(struct net_device *ndev, struct iw_request_info *info,
2024                   __u32 * uwrq, char *extra)
2025 {
2026         islpci_private *priv = netdev_priv(ndev);
2027         
2028         priv->priv_oid = *uwrq;
2029         printk("%s: oid 0x%08X\n", ndev->name, *uwrq);
2030
2031         return 0;
2032 }
2033
2034 int
2035 prism54_debug_get_oid(struct net_device *ndev, struct iw_request_info *info,
2036                 struct iw_point *data, char *extra)
2037 {
2038         islpci_private *priv = netdev_priv(ndev);
2039         struct islpci_mgmtframe *response = NULL;
2040         int ret = -EIO, response_op = PIMFOR_OP_ERROR;
2041         
2042         printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid);
2043         data->length = 0;
2044         
2045         if (islpci_get_state(priv) >= PRV_STATE_INIT) {
2046                 ret =
2047                     islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET,
2048                                            priv->priv_oid, extra, 256,
2049                                            &response);
2050                 response_op = response->header->operation;
2051                 printk("%s: ret: %i\n", ndev->name, ret);
2052                 printk("%s: response_op: %i\n", ndev->name, response_op);
2053                 if (ret || !response
2054                     || response->header->operation == PIMFOR_OP_ERROR) {
2055                         if (response) {
2056                                 islpci_mgt_release(response);
2057                         }
2058                         printk("%s: EIO\n", ndev->name);
2059                         ret = -EIO;
2060                 }
2061                 if (!ret) {
2062                         data->length = response->header->length;
2063                         memcpy(extra, response->data, data->length);
2064                         islpci_mgt_release(response);
2065                         printk("%s: len: %i\n", ndev->name, data->length);
2066                 }
2067         }
2068         
2069         return ret;
2070 }
2071
2072 int
2073 prism54_debug_set_oid(struct net_device *ndev, struct iw_request_info *info,
2074                 struct iw_point *data, char *extra)
2075 {
2076         islpci_private *priv = netdev_priv(ndev);
2077         struct islpci_mgmtframe *response = NULL;
2078         int ret = 0, response_op = PIMFOR_OP_ERROR;
2079         
2080         printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid,
2081                data->length);
2082         
2083         if (islpci_get_state(priv) >= PRV_STATE_INIT) {
2084                 ret =
2085                     islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET,
2086                                            priv->priv_oid, extra, data->length,
2087                                            &response);
2088                 printk("%s: ret: %i\n", ndev->name, ret);
2089                 if (!ret) {
2090                         response_op = response->header->operation;
2091                         printk("%s: response_op: %i\n", ndev->name,
2092                                response_op);
2093                         islpci_mgt_release(response);
2094                 }
2095                 if (ret || response_op == PIMFOR_OP_ERROR) {
2096                         printk("%s: EIO\n", ndev->name);
2097                         ret = -EIO;
2098                 }
2099         }
2100         
2101         return ret;
2102 }
2103
2104 static int
2105 prism54_set_spy(struct net_device *ndev,
2106                 struct iw_request_info *info,
2107                 union iwreq_data *uwrq, char *extra)
2108 {
2109         islpci_private *priv = netdev_priv(ndev);
2110         u32 u, oid = OID_INL_CONFIG;
2111
2112         down_write(&priv->mib_sem);
2113         mgt_get(priv, OID_INL_CONFIG, &u);
2114
2115         if ((uwrq->data.length == 0) && (priv->spy_data.spy_number > 0))
2116                 /* disable spy */
2117                 u &= ~INL_CONFIG_RXANNEX;
2118         else if ((uwrq->data.length > 0) && (priv->spy_data.spy_number == 0))
2119                 /* enable spy */
2120                 u |= INL_CONFIG_RXANNEX;
2121
2122         mgt_set(priv, OID_INL_CONFIG, &u);
2123         mgt_commit_list(priv, &oid, 1);
2124         up_write(&priv->mib_sem);
2125
2126         return iw_handler_set_spy(ndev, info, uwrq, extra);
2127 }
2128
2129 static const iw_handler prism54_handler[] = {
2130         (iw_handler) prism54_commit,    /* SIOCSIWCOMMIT */
2131         (iw_handler) prism54_get_name,  /* SIOCGIWNAME */
2132         (iw_handler) NULL,      /* SIOCSIWNWID */
2133         (iw_handler) NULL,      /* SIOCGIWNWID */
2134         (iw_handler) prism54_set_freq,  /* SIOCSIWFREQ */
2135         (iw_handler) prism54_get_freq,  /* SIOCGIWFREQ */
2136         (iw_handler) prism54_set_mode,  /* SIOCSIWMODE */
2137         (iw_handler) prism54_get_mode,  /* SIOCGIWMODE */
2138         (iw_handler) prism54_set_sens,  /* SIOCSIWSENS */
2139         (iw_handler) prism54_get_sens,  /* SIOCGIWSENS */
2140         (iw_handler) NULL,      /* SIOCSIWRANGE */
2141         (iw_handler) prism54_get_range, /* SIOCGIWRANGE */
2142         (iw_handler) NULL,      /* SIOCSIWPRIV */
2143         (iw_handler) NULL,      /* SIOCGIWPRIV */
2144         (iw_handler) NULL,      /* SIOCSIWSTATS */
2145         (iw_handler) NULL,      /* SIOCGIWSTATS */
2146         prism54_set_spy,        /* SIOCSIWSPY */
2147         iw_handler_get_spy,     /* SIOCGIWSPY */
2148         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2149         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2150         (iw_handler) prism54_set_wap,   /* SIOCSIWAP */
2151         (iw_handler) prism54_get_wap,   /* SIOCGIWAP */
2152         (iw_handler) NULL,      /* -- hole -- */
2153         (iw_handler) NULL,      /* SIOCGIWAPLIST depreciated */
2154         (iw_handler) prism54_set_scan,  /* SIOCSIWSCAN */
2155         (iw_handler) prism54_get_scan,  /* SIOCGIWSCAN */
2156         (iw_handler) prism54_set_essid, /* SIOCSIWESSID */
2157         (iw_handler) prism54_get_essid, /* SIOCGIWESSID */
2158         (iw_handler) prism54_set_nick,  /* SIOCSIWNICKN */
2159         (iw_handler) prism54_get_nick,  /* SIOCGIWNICKN */
2160         (iw_handler) NULL,      /* -- hole -- */
2161         (iw_handler) NULL,      /* -- hole -- */
2162         (iw_handler) prism54_set_rate,  /* SIOCSIWRATE */
2163         (iw_handler) prism54_get_rate,  /* SIOCGIWRATE */
2164         (iw_handler) prism54_set_rts,   /* SIOCSIWRTS */
2165         (iw_handler) prism54_get_rts,   /* SIOCGIWRTS */
2166         (iw_handler) prism54_set_frag,  /* SIOCSIWFRAG */
2167         (iw_handler) prism54_get_frag,  /* SIOCGIWFRAG */
2168         (iw_handler) prism54_set_txpower,       /* SIOCSIWTXPOW */
2169         (iw_handler) prism54_get_txpower,       /* SIOCGIWTXPOW */
2170         (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */
2171         (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */
2172         (iw_handler) prism54_set_encode,        /* SIOCSIWENCODE */
2173         (iw_handler) prism54_get_encode,        /* SIOCGIWENCODE */
2174         (iw_handler) NULL,      /* SIOCSIWPOWER */
2175         (iw_handler) NULL,      /* SIOCGIWPOWER */
2176 };
2177
2178 /* The low order bit identify a SET (0) or a GET (1) ioctl.  */
2179
2180 #define PRISM54_RESET           SIOCIWFIRSTPRIV
2181 #define PRISM54_GET_POLICY      SIOCIWFIRSTPRIV+1
2182 #define PRISM54_SET_POLICY      SIOCIWFIRSTPRIV+2
2183 #define PRISM54_GET_MAC         SIOCIWFIRSTPRIV+3
2184 #define PRISM54_ADD_MAC         SIOCIWFIRSTPRIV+4
2185
2186 #define PRISM54_DEL_MAC         SIOCIWFIRSTPRIV+6
2187
2188 #define PRISM54_KICK_MAC        SIOCIWFIRSTPRIV+8
2189
2190 #define PRISM54_KICK_ALL        SIOCIWFIRSTPRIV+10
2191
2192 #define PRISM54_GET_WPA         SIOCIWFIRSTPRIV+11
2193 #define PRISM54_SET_WPA         SIOCIWFIRSTPRIV+12
2194
2195 #define PRISM54_DBG_OID         SIOCIWFIRSTPRIV+14
2196 #define PRISM54_DBG_GET_OID     SIOCIWFIRSTPRIV+15
2197 #define PRISM54_DBG_SET_OID     SIOCIWFIRSTPRIV+16
2198
2199 #define PRISM54_GET_OID    SIOCIWFIRSTPRIV+17
2200 #define PRISM54_SET_OID_U32     SIOCIWFIRSTPRIV+18
2201 #define PRISM54_SET_OID_STR     SIOCIWFIRSTPRIV+20
2202 #define PRISM54_SET_OID_ADDR    SIOCIWFIRSTPRIV+22
2203
2204 #define PRISM54_GET_PRISMHDR    SIOCIWFIRSTPRIV+23
2205 #define PRISM54_SET_PRISMHDR    SIOCIWFIRSTPRIV+24
2206
2207 #define IWPRIV_SET_U32(n,x)     { n, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "set_"x }
2208 #define IWPRIV_SET_SSID(n,x)    { n, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "set_"x }
2209 #define IWPRIV_SET_ADDR(n,x)    { n, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, "set_"x }
2210 #define IWPRIV_GET(n,x) { n, 0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, "get_"x }
2211
2212 #define IWPRIV_U32(n,x)         IWPRIV_SET_U32(n,x), IWPRIV_GET(n,x)
2213 #define IWPRIV_SSID(n,x)        IWPRIV_SET_SSID(n,x), IWPRIV_GET(n,x)
2214 #define IWPRIV_ADDR(n,x)        IWPRIV_SET_ADDR(n,x), IWPRIV_GET(n,x)
2215
2216 /* Note : limited to 128 private ioctls */
2217
2218 static const struct iw_priv_args prism54_private_args[] = {
2219 /*{ cmd, set_args, get_args, name } */
2220         {PRISM54_RESET, 0, 0, "reset"},
2221         {PRISM54_GET_PRISMHDR, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2222          "get_prismhdr"},
2223         {PRISM54_SET_PRISMHDR, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2224          "set_prismhdr"},
2225         {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2226          "getPolicy"},
2227         {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2228          "setPolicy"},
2229         {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"},
2230         {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2231          "addMac"},
2232         {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2233          "delMac"},
2234         {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2235          "kickMac"},
2236         {PRISM54_KICK_ALL, 0, 0, "kickAll"},
2237         {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2238          "get_wpa"},
2239         {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2240          "set_wpa"},
2241         {PRISM54_DBG_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2242          "dbg_oid"},
2243         {PRISM54_DBG_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "dbg_get_oid"},
2244         {PRISM54_DBG_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "dbg_get_oid"},
2245         /* --- sub-ioctls handlers --- */
2246         {PRISM54_GET_OID,
2247          0, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | PRIV_STR_SIZE, ""},
2248         {PRISM54_SET_OID_U32,
2249          IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, ""},
2250         {PRISM54_SET_OID_STR,
2251          IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
2252         {PRISM54_SET_OID_ADDR,
2253          IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0, ""},
2254         /* --- sub-ioctls definitions --- */
2255         IWPRIV_ADDR(GEN_OID_MACADDRESS, "addr"),
2256         IWPRIV_GET(GEN_OID_LINKSTATE, "linkstate"),
2257         IWPRIV_U32(DOT11_OID_BSSTYPE, "bsstype"),
2258         IWPRIV_ADDR(DOT11_OID_BSSID, "bssid"),
2259         IWPRIV_U32(DOT11_OID_STATE, "state"),
2260         IWPRIV_U32(DOT11_OID_AID, "aid"),
2261
2262         IWPRIV_SSID(DOT11_OID_SSIDOVERRIDE, "ssidoverride"),
2263
2264         IWPRIV_U32(DOT11_OID_MEDIUMLIMIT, "medlimit"),
2265         IWPRIV_U32(DOT11_OID_BEACONPERIOD, "beacon"),
2266         IWPRIV_U32(DOT11_OID_DTIMPERIOD, "dtimperiod"),
2267
2268         IWPRIV_U32(DOT11_OID_AUTHENABLE, "authenable"),
2269         IWPRIV_U32(DOT11_OID_PRIVACYINVOKED, "privinvok"),
2270         IWPRIV_U32(DOT11_OID_EXUNENCRYPTED, "exunencrypt"),
2271         
2272         IWPRIV_U32(DOT11_OID_REKEYTHRESHOLD, "rekeythresh"),
2273
2274         IWPRIV_U32(DOT11_OID_MAXTXLIFETIME, "maxtxlife"),
2275         IWPRIV_U32(DOT11_OID_MAXRXLIFETIME, "maxrxlife"),
2276         IWPRIV_U32(DOT11_OID_ALOFT_FIXEDRATE, "fixedrate"),
2277         IWPRIV_U32(DOT11_OID_MAXFRAMEBURST, "frameburst"),
2278         IWPRIV_U32(DOT11_OID_PSM, "psm"),
2279
2280         IWPRIV_U32(DOT11_OID_BRIDGELOCAL, "bridge"),
2281         IWPRIV_U32(DOT11_OID_CLIENTS, "clients"),
2282         IWPRIV_U32(DOT11_OID_CLIENTSASSOCIATED, "clientassoc"),
2283         IWPRIV_U32(DOT11_OID_DOT1XENABLE, "dot1xenable"),
2284         IWPRIV_U32(DOT11_OID_ANTENNARX, "rxant"),
2285         IWPRIV_U32(DOT11_OID_ANTENNATX, "txant"),
2286         IWPRIV_U32(DOT11_OID_ANTENNADIVERSITY, "antdivers"),
2287         IWPRIV_U32(DOT11_OID_EDTHRESHOLD, "edthresh"),
2288         IWPRIV_U32(DOT11_OID_PREAMBLESETTINGS, "preamble"),
2289         IWPRIV_GET(DOT11_OID_RATES, "rates"),
2290         IWPRIV_U32(DOT11_OID_OUTPUTPOWER, ".11outpower"),
2291         IWPRIV_GET(DOT11_OID_SUPPORTEDRATES, "supprates"),
2292         IWPRIV_GET(DOT11_OID_SUPPORTEDFREQUENCIES, "suppfreq"),
2293
2294         IWPRIV_U32(DOT11_OID_NOISEFLOOR, "noisefloor"),
2295         IWPRIV_GET(DOT11_OID_FREQUENCYACTIVITY, "freqactivity"),
2296         IWPRIV_U32(DOT11_OID_NONERPPROTECTION, "nonerpprotec"),
2297         IWPRIV_U32(DOT11_OID_PROFILES, "profile"),
2298         IWPRIV_GET(DOT11_OID_EXTENDEDRATES, "extrates"),
2299         IWPRIV_U32(DOT11_OID_MLMEAUTOLEVEL, "mlmelevel"),
2300
2301         IWPRIV_GET(DOT11_OID_BSSS, "bsss"),
2302         IWPRIV_GET(DOT11_OID_BSSLIST, "bsslist"),
2303         IWPRIV_U32(OID_INL_MODE, "mode"),
2304         IWPRIV_U32(OID_INL_CONFIG, "config"),
2305         IWPRIV_U32(OID_INL_DOT11D_CONFORMANCE, ".11dconform"),
2306         IWPRIV_GET(OID_INL_PHYCAPABILITIES, "phycapa"),
2307         IWPRIV_U32(OID_INL_OUTPUTPOWER, "outpower"),
2308 };
2309
2310 static const iw_handler prism54_private_handler[] = {
2311         (iw_handler) prism54_reset,
2312         (iw_handler) prism54_get_policy,
2313         (iw_handler) prism54_set_policy,
2314         (iw_handler) prism54_get_mac,
2315         (iw_handler) prism54_add_mac,
2316         (iw_handler) NULL,
2317         (iw_handler) prism54_del_mac,
2318         (iw_handler) NULL,
2319         (iw_handler) prism54_kick_mac,
2320         (iw_handler) NULL,
2321         (iw_handler) prism54_kick_all,
2322         (iw_handler) prism54_get_wpa,
2323         (iw_handler) prism54_set_wpa,
2324         (iw_handler) NULL,
2325         (iw_handler) prism54_debug_oid,
2326         (iw_handler) prism54_debug_get_oid,
2327         (iw_handler) prism54_debug_set_oid,
2328         (iw_handler) prism54_get_oid,
2329         (iw_handler) prism54_set_u32,
2330         (iw_handler) NULL,
2331         (iw_handler) prism54_set_raw,
2332         (iw_handler) NULL,
2333         (iw_handler) prism54_set_raw,
2334         (iw_handler) prism54_get_prismhdr,
2335         (iw_handler) prism54_set_prismhdr,
2336 };
2337
2338 const struct iw_handler_def prism54_handler_def = {
2339         .num_standard = sizeof (prism54_handler) / sizeof (iw_handler),
2340         .num_private = sizeof (prism54_private_handler) / sizeof (iw_handler),
2341         .num_private_args =
2342             sizeof (prism54_private_args) / sizeof (struct iw_priv_args),
2343         .standard = (iw_handler *) prism54_handler,
2344         .private = (iw_handler *) prism54_private_handler,
2345         .private_args = (struct iw_priv_args *) prism54_private_args,
2346         .spy_offset = offsetof(islpci_private, spy_data),
2347 };
2348
2349 /* For ioctls that don't work with the new API */
2350
2351 int
2352 prism54_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
2353 {
2354         return -EOPNOTSUPP;
2355 }