ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / net / wireless / prism54 / isl_ioctl.c
1 /*  $Header: /var/lib/cvs/prism54-ng/ksrc/isl_ioctl.c,v 1.140 2004/02/28 03:06:07 mcgrof Exp $
2  *  
3  *  Copyright (C) 2002 Intersil Americas Inc.
4  *            (C) 2003 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 "%s(): Sorry, Repeater mode and Secondary mode "
91                                 "are not yet supported by this driver.\n",
92                        __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 = cpu_to_le32(PSM_BUFFER_SIZE),
147                 .addr = cpu_to_le32(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 = 0;
331
332         /* prepare the structure for the set object */
333         if (fwrq->m < 1000)
334                 /* structure value contains a channel indication */
335                 c = fwrq->m;
336         else {
337                 /* structure contains a frequency indication and fwrq->e = 1 */
338                 int f = fwrq->m / 100000;
339
340                 if (fwrq->e != 1)
341                         return -EINVAL;
342                 if ((f >= 2412) && (f <= 2484)) {
343                         while ((c < 14) && (f != frequency_list_bg[c]))
344                                 c++;
345                         if (c >= 14)
346                                 return -EINVAL;
347                 } else if ((f >= (int) 5170) && (f <= (int) 5320)) {
348                         while ((c < 12) && (f != frequency_list_a[c]))
349                                 c++;
350                         if (c >= 12)
351                                 return -EINVAL;
352                 } else
353                         return -EINVAL;
354                 c++;
355         }
356
357         rvalue = mgt_set_request(priv, DOT11_OID_CHANNEL, 0, &c);
358
359         /* Call commit handler */
360         return (rvalue ? rvalue : -EINPROGRESS);
361 }
362
363 static int
364 prism54_get_freq(struct net_device *ndev, struct iw_request_info *info,
365                  struct iw_freq *fwrq, char *extra)
366 {
367         islpci_private *priv = netdev_priv(ndev);
368         union oid_res_t r;
369         int rvalue;
370
371         rvalue = mgt_get_request(priv, DOT11_OID_CHANNEL, 0, NULL, &r);
372
373         fwrq->m = r.u;
374         fwrq->e = 0;
375
376         return rvalue;
377 }
378
379 static int
380 prism54_set_mode(struct net_device *ndev, struct iw_request_info *info,
381                  __u32 * uwrq, char *extra)
382 {
383         islpci_private *priv = netdev_priv(ndev);
384         u32 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
385
386         /* Let's see if the user passed a valid Linux Wireless mode */
387         if (*uwrq > IW_MODE_MONITOR || *uwrq < IW_MODE_AUTO) {
388                 printk(KERN_DEBUG
389                        "%s: %s() You passed a non-valid init_mode.\n",
390                        priv->ndev->name, __FUNCTION__);
391                 return -EINVAL;
392         }
393
394         down_write(&priv->mib_sem);
395
396         if (prism54_mib_mode_helper(priv, *uwrq)) {
397                 up_write(&priv->mib_sem);
398                 return -EOPNOTSUPP;
399         }
400
401         /* the ACL code needs an intermediate mlmeautolevel. The wpa stuff an
402          * extended one.
403          */
404         if ((*uwrq == IW_MODE_MASTER) && (priv->acl.policy != MAC_POLICY_OPEN))
405                 mlmeautolevel = DOT11_MLME_INTERMEDIATE;
406         if (priv->wpa)
407                 mlmeautolevel = DOT11_MLME_EXTENDED;
408
409         mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
410
411         mgt_commit(priv);
412         priv->ndev->type = (priv->iw_mode == IW_MODE_MONITOR)
413             ? ARPHRD_IEEE80211 : ARPHRD_ETHER;
414         up_write(&priv->mib_sem);
415
416         return 0;
417 }
418
419 /* Use mib cache */
420 static int
421 prism54_get_mode(struct net_device *ndev, struct iw_request_info *info,
422                  __u32 * uwrq, char *extra)
423 {
424         islpci_private *priv = netdev_priv(ndev);
425
426         BUG_ON((priv->iw_mode < IW_MODE_AUTO) || (priv->iw_mode >
427                                                   IW_MODE_MONITOR));
428         *uwrq = priv->iw_mode;
429
430         return 0;
431 }
432
433 /* we use DOT11_OID_EDTHRESHOLD. From what I guess the card will not try to
434  * emit data if (sensitivity > rssi - noise) (in dBm).
435  * prism54_set_sens does not seem to work.
436  */
437
438 static int
439 prism54_set_sens(struct net_device *ndev, struct iw_request_info *info,
440                  struct iw_param *vwrq, char *extra)
441 {
442         islpci_private *priv = netdev_priv(ndev);
443         u32 sens;
444
445         /* by default  the card sets this to 20. */
446         sens = vwrq->disabled ? 20 : vwrq->value;
447
448         /* set the ed threshold. */
449         return mgt_set_request(priv, DOT11_OID_EDTHRESHOLD, 0, &sens);
450 }
451
452 static int
453 prism54_get_sens(struct net_device *ndev, struct iw_request_info *info,
454                  struct iw_param *vwrq, char *extra)
455 {
456         islpci_private *priv = netdev_priv(ndev);
457         union oid_res_t r;
458         int rvalue;
459
460         rvalue = mgt_get_request(priv, DOT11_OID_EDTHRESHOLD, 0, NULL, &r);
461
462         vwrq->value = r.u;
463         vwrq->disabled = (vwrq->value == 0);
464         vwrq->fixed = 1;
465
466         return rvalue;
467 }
468
469 static int
470 prism54_get_range(struct net_device *ndev, struct iw_request_info *info,
471                   struct iw_point *dwrq, char *extra)
472 {
473         struct iw_range *range = (struct iw_range *) extra;
474         islpci_private *priv = netdev_priv(ndev);
475         char *data;
476         int i, m, rvalue;
477         struct obj_frequencies *freq;
478         union oid_res_t r;
479
480         memset(range, 0, sizeof (struct iw_range));
481         dwrq->length = sizeof (struct iw_range);
482
483         /* set the wireless extension version number */
484         range->we_version_source = SUPPORTED_WIRELESS_EXT;
485         range->we_version_compiled = WIRELESS_EXT;
486
487         /* Now the encoding capabilities */
488         range->num_encoding_sizes = 3;
489         /* 64(40) bits WEP */
490         range->encoding_size[0] = 5;
491         /* 128(104) bits WEP */
492         range->encoding_size[1] = 13;
493         /* 256 bits for WPA-PSK */
494         range->encoding_size[2] = 32;
495         /* 4 keys are allowed */
496         range->max_encoding_tokens = 4;
497
498         /* we don't know the quality range... */
499         range->max_qual.level = 0;
500         range->max_qual.noise = 0;
501         range->max_qual.qual = 0;
502         /* these value describe an average quality. Needs more tweaking... */
503         range->avg_qual.level = -80;    /* -80 dBm */
504         range->avg_qual.noise = 0;      /* don't know what to put here */
505         range->avg_qual.qual = 0;
506
507         range->sensitivity = 200;
508
509         /* retry limit capabilities */
510         range->retry_capa = IW_RETRY_LIMIT | IW_RETRY_LIFETIME;
511         range->retry_flags = IW_RETRY_LIMIT;
512         range->r_time_flags = IW_RETRY_LIFETIME;
513
514         /* I don't know the range. Put stupid things here */
515         range->min_retry = 1;
516         range->max_retry = 65535;
517         range->min_r_time = 1024;
518         range->max_r_time = 65535 * 1024;
519
520         /* txpower is supported in dBm's */
521         range->txpower_capa = IW_TXPOW_DBM;
522
523         if (islpci_get_state(priv) < PRV_STATE_INIT)
524                 return 0;
525
526         /* Request the device for the supported frequencies
527          * not really revelant since some devices will report the 5 GHz band
528          * frequencies even if they don't support them.
529          */
530         rvalue =
531             mgt_get_request(priv, DOT11_OID_SUPPORTEDFREQUENCIES, 0, NULL, &r);
532         freq = r.ptr;
533
534         range->num_channels = le16_to_cpu(freq->nr);
535         range->num_frequency = le16_to_cpu(freq->nr);
536
537         /* Frequencies are not listed in the right order. The reordering is probably
538          * firmware dependant and thus should work for everyone.
539          */
540         m = min(IW_MAX_FREQUENCIES, (int) le16_to_cpu(freq->nr));
541         for (i = 0; i < m - 12; i++) {
542                 range->freq[i].m = le16_to_cpu(freq->mhz[12 + i]);
543                 range->freq[i].e = 6;
544                 range->freq[i].i = i + 1;
545         }
546         for (i = m - 12; i < m; i++) {
547                 range->freq[i].m = le16_to_cpu(freq->mhz[i - m + 12]);
548                 range->freq[i].e = 6;
549                 range->freq[i].i = i + 23;
550         }
551
552         kfree(freq);
553
554         rvalue |= mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r);
555         data = r.ptr;
556
557         /* We got an array of char. It is NULL terminated. */
558         i = 0;
559         while ((i < IW_MAX_BITRATES) && (*data != 0)) {
560                 /*       the result must be in bps. The card gives us 500Kbps */
561                 range->bitrate[i] = (__s32) (*data >> 1);
562                 range->bitrate[i] *= 1000000;
563                 i++;
564                 data++;
565         }
566
567         range->num_bitrates = i;
568
569         kfree(r.ptr);
570
571         return rvalue;
572 }
573
574 /* Set AP address*/
575
576 static int
577 prism54_set_wap(struct net_device *ndev, struct iw_request_info *info,
578                 struct sockaddr *awrq, char *extra)
579 {
580         islpci_private *priv = netdev_priv(ndev);
581         char bssid[6];
582         int rvalue;
583
584         if (awrq->sa_family != ARPHRD_ETHER)
585                 return -EINVAL;
586
587         /* prepare the structure for the set object */
588         memcpy(&bssid[0], awrq->sa_data, 6);
589
590         /* set the bssid -- does this make sense when in AP mode? */
591         rvalue = mgt_set_request(priv, DOT11_OID_BSSID, 0, &bssid);
592
593         return (rvalue ? rvalue : -EINPROGRESS);        /* Call commit handler */
594 }
595
596 /* get AP address*/
597
598 static int
599 prism54_get_wap(struct net_device *ndev, struct iw_request_info *info,
600                 struct sockaddr *awrq, char *extra)
601 {
602         islpci_private *priv = netdev_priv(ndev);
603         union oid_res_t r;
604         int rvalue;
605
606         rvalue = mgt_get_request(priv, DOT11_OID_BSSID, 0, NULL, &r);
607
608         memcpy(awrq->sa_data, r.ptr, 6);
609         awrq->sa_family = ARPHRD_ETHER;
610         kfree(r.ptr);
611
612         return rvalue;
613 }
614
615 static int
616 prism54_set_scan(struct net_device *dev, struct iw_request_info *info,
617                  struct iw_param *vwrq, char *extra)
618 {
619         /* hehe the device does this automagicaly */
620         return 0;
621 }
622
623 /* a little helper that will translate our data into a card independent
624  * format that the Wireless Tools will understand. This was inspired by
625  * the "Aironet driver for 4500 and 4800 series cards" (GPL)
626  */
627
628 inline char *
629 prism54_translate_bss(struct net_device *ndev, char *current_ev,
630                       char *end_buf, struct obj_bss *bss, char noise)
631 {
632         struct iw_event iwe;    /* Temporary buffer */
633         short cap;
634         islpci_private *priv = netdev_priv(ndev);
635
636         /* The first entry must be the MAC address */
637         memcpy(iwe.u.ap_addr.sa_data, bss->address, 6);
638         iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
639         iwe.cmd = SIOCGIWAP;
640         current_ev =
641             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_ADDR_LEN);
642
643         /* The following entries will be displayed in the same order we give them */
644
645         /* The ESSID. */
646         iwe.u.data.length = bss->ssid.length;
647         iwe.u.data.flags = 1;
648         iwe.cmd = SIOCGIWESSID;
649         current_ev = iwe_stream_add_point(current_ev, end_buf,
650                                           &iwe, bss->ssid.octets);
651
652         /* Capabilities */
653 #define CAP_ESS 0x01
654 #define CAP_IBSS 0x02
655 #define CAP_CRYPT 0x10
656
657         /* Mode */
658         cap = le16_to_cpu(bss->capinfo);
659         iwe.u.mode = 0;
660         if (cap & CAP_ESS)
661                 iwe.u.mode = IW_MODE_MASTER;
662         else if (cap & CAP_IBSS)
663                 iwe.u.mode = IW_MODE_ADHOC;
664         iwe.cmd = SIOCGIWMODE;
665         if (iwe.u.mode)
666                 current_ev =
667                     iwe_stream_add_event(current_ev, end_buf, &iwe,
668                                          IW_EV_UINT_LEN);
669
670         /* Encryption capability */
671         if (cap & CAP_CRYPT)
672                 iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
673         else
674                 iwe.u.data.flags = IW_ENCODE_DISABLED;
675         iwe.u.data.length = 0;
676         iwe.cmd = SIOCGIWENCODE;
677         current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe, NULL);
678
679         /* Add frequency. (short) bss->channel is the frequency in MHz */
680         iwe.u.freq.m = bss->channel;
681         iwe.u.freq.e = 6;
682         iwe.cmd = SIOCGIWFREQ;
683         current_ev =
684             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_FREQ_LEN);
685
686         /* Add quality statistics */
687         iwe.u.qual.level = bss->rssi;
688         iwe.u.qual.noise = noise;
689         /* do a simple SNR for quality */
690         iwe.u.qual.qual = bss->rssi - noise;
691         iwe.cmd = IWEVQUAL;
692         current_ev =
693             iwe_stream_add_event(current_ev, end_buf, &iwe, IW_EV_QUAL_LEN);
694
695         if (priv->wpa) {
696                 u8 wpa_ie[MAX_WPA_IE_LEN];
697                 char *buf, *p;
698                 size_t wpa_ie_len;
699                 int i;
700
701                 wpa_ie_len = prism54_wpa_ie_get(priv, bss->address, wpa_ie);
702                 if (wpa_ie_len > 0 &&
703                     (buf = kmalloc(wpa_ie_len * 2 + 10, GFP_ATOMIC))) {
704                         p = buf;
705                         p += sprintf(p, "wpa_ie=");
706                         for (i = 0; i < wpa_ie_len; i++) {
707                                 p += sprintf(p, "%02x", wpa_ie[i]);
708                         }
709                         memset(&iwe, 0, sizeof (iwe));
710                         iwe.cmd = IWEVCUSTOM;
711                         iwe.u.data.length = strlen(buf);
712                         current_ev = iwe_stream_add_point(current_ev, end_buf,
713                                                           &iwe, buf);
714                         kfree(buf);
715                 }
716         }
717
718         return current_ev;
719 }
720
721 int
722 prism54_get_scan(struct net_device *ndev, struct iw_request_info *info,
723                  struct iw_point *dwrq, char *extra)
724 {
725         islpci_private *priv = netdev_priv(ndev);
726         int i, rvalue;
727         struct obj_bsslist *bsslist;
728         u32 noise = 0;
729         char *current_ev = extra;
730         union oid_res_t r;
731
732         if (islpci_get_state(priv) < PRV_STATE_INIT) {
733                 /* device is not ready, fail gently */
734                 dwrq->length = 0;
735                 return 0;
736         }
737
738         /* first get the noise value. We will use it to report the link quality */
739         rvalue = mgt_get_request(priv, DOT11_OID_NOISEFLOOR, 0, NULL, &r);
740         noise = r.u;
741
742         /* Ask the device for a list of known bss. We can report at most
743          * IW_MAX_AP=64 to the range struct. But the device won't repport anything
744          * if you change the value of MAXBSS=24. Anyway 24 AP It is probably enough.
745          */
746         rvalue |= mgt_get_request(priv, DOT11_OID_BSSLIST, 0, NULL, &r);
747         bsslist = r.ptr;
748
749         /* ok now, scan the list and translate its info */
750         for (i = 0; i < min(IW_MAX_AP, (int) le32_to_cpu(bsslist->nr)); i++)
751                 current_ev = prism54_translate_bss(ndev, current_ev,
752                                                    extra + IW_SCAN_MAX_DATA,
753                                                    &(bsslist->bsslist[i]),
754                                                    noise);
755         kfree(bsslist);
756         dwrq->length = (current_ev - extra);
757         dwrq->flags = 0;        /* todo */
758
759         return rvalue;
760 }
761
762 static int
763 prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
764                   struct iw_point *dwrq, char *extra)
765 {
766         islpci_private *priv = netdev_priv(ndev);
767         struct obj_ssid essid;
768
769         memset(essid.octets, 0, 33);
770
771         /* Check if we were asked for `any' */
772         if (dwrq->flags && dwrq->length) {
773                 if (dwrq->length > min(33, IW_ESSID_MAX_SIZE + 1))
774                         return -E2BIG;
775                 essid.length = dwrq->length - 1;
776                 memcpy(essid.octets, extra, dwrq->length);
777         } else
778                 essid.length = 0;
779         
780         if (priv->iw_mode != IW_MODE_MONITOR)
781                 return mgt_set_request(priv, DOT11_OID_SSID, 0, &essid);
782
783         /* If in monitor mode, just save to mib */
784         mgt_set(priv, DOT11_OID_SSID, &essid);
785         return 0;
786
787 }
788
789 static int
790 prism54_get_essid(struct net_device *ndev, struct iw_request_info *info,
791                   struct iw_point *dwrq, char *extra)
792 {
793         islpci_private *priv = netdev_priv(ndev);
794         struct obj_ssid *essid;
795         union oid_res_t r;
796         int rvalue;
797
798         rvalue = mgt_get_request(priv, DOT11_OID_SSID, 0, NULL, &r);
799         essid = r.ptr;
800
801         if (essid->length) {
802                 dwrq->flags = 1;        /* set ESSID to ON for Wireless Extensions */
803                 /* if it is to big, trunk it */
804                 dwrq->length = min(IW_ESSID_MAX_SIZE, essid->length + 1);
805         } else {
806                 dwrq->flags = 0;
807                 dwrq->length = 0;
808         }
809         essid->octets[essid->length] = '\0';
810         memcpy(extra, essid->octets, dwrq->length);
811         kfree(essid);
812
813         return rvalue;
814 }
815
816 /* Provides no functionality, just completes the ioctl. In essence this is a 
817  * just a cosmetic ioctl.
818  */
819 static int
820 prism54_set_nick(struct net_device *ndev, struct iw_request_info *info,
821                  struct iw_point *dwrq, char *extra)
822 {
823         islpci_private *priv = netdev_priv(ndev);
824
825         if (dwrq->length > IW_ESSID_MAX_SIZE)
826                 return -E2BIG;
827
828         down_write(&priv->mib_sem);
829         memset(priv->nickname, 0, sizeof (priv->nickname));
830         memcpy(priv->nickname, extra, dwrq->length);
831         up_write(&priv->mib_sem);
832
833         return 0;
834 }
835
836 static int
837 prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
838                  struct iw_point *dwrq, char *extra)
839 {
840         islpci_private *priv = netdev_priv(ndev);
841
842         dwrq->length = 0;
843
844         down_read(&priv->mib_sem);
845         dwrq->length = strlen(priv->nickname) + 1;
846         memcpy(extra, priv->nickname, dwrq->length);
847         up_read(&priv->mib_sem);
848
849         return 0;
850 }
851
852 /* Set the allowed Bitrates */
853
854 static int
855 prism54_set_rate(struct net_device *ndev,
856                  struct iw_request_info *info,
857                  struct iw_param *vwrq, char *extra)
858 {
859
860         islpci_private *priv = netdev_priv(ndev);
861         u32 rate, profile;
862         char *data;
863         int ret, i;
864         union oid_res_t r;
865         
866         if (vwrq->value == -1) {
867                 /* auto mode. No limit. */
868                 profile = 1;
869                 return mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
870         }
871         
872         if((ret = mgt_get_request(priv, DOT11_OID_SUPPORTEDRATES, 0, NULL, &r)))
873                 return ret;
874                 
875         rate = (u32) (vwrq->value / 500000);
876         data = r.ptr;
877         i = 0;
878         
879         while(data[i]) {
880                 if(rate && (data[i] == rate)) {
881                         break;
882                 }
883                 if(vwrq->value == i) {
884                         break;
885                 }
886                 data[i] |= 0x80;
887                 i++;
888         }
889                 
890         if(!data[i]) {
891                 return -EINVAL;
892         }
893         
894         data[i] |= 0x80;
895         data[i + 1] = 0;
896         
897         /* Now, check if we want a fixed or auto value */
898         if (vwrq->fixed) {
899                 data[0] = data[i];
900                 data[1] = 0;
901         }
902
903 /*
904         i = 0;
905         printk("prism54 rate: ");
906         while(data[i]) {
907                 printk("%u ", data[i]);
908                 i++;
909         }
910         printk("0\n");
911 */      
912         profile = -1;
913         ret = mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
914         ret |= mgt_set_request(priv, DOT11_OID_EXTENDEDRATES, 0, data);
915         ret |= mgt_set_request(priv, DOT11_OID_RATES, 0, data);
916         
917         kfree(r.ptr);
918         
919         return ret;
920 }
921
922 /* Get the current bit rate */
923 static int
924 prism54_get_rate(struct net_device *ndev,
925                  struct iw_request_info *info,
926                  struct iw_param *vwrq, char *extra)
927 {
928         islpci_private *priv = netdev_priv(ndev);
929         int rvalue;
930         char *data;
931         union oid_res_t r;
932
933         /* Get the current bit rate */
934         if((rvalue = mgt_get_request(priv, GEN_OID_LINKSTATE, 0, NULL, &r)))
935                 return rvalue;
936         vwrq->value = r.u * 500000;
937
938         /* request the device for the enabled rates */
939         if((rvalue = mgt_get_request(priv, DOT11_OID_RATES, 0, NULL, &r)))
940                 return rvalue;
941         data = r.ptr;
942         vwrq->fixed = (data[0] != 0) && (data[1] == 0);
943         kfree(r.ptr);
944         
945         return 0;
946 }
947
948 static int
949 prism54_set_rts(struct net_device *ndev, struct iw_request_info *info,
950                 struct iw_param *vwrq, char *extra)
951 {
952         islpci_private *priv = netdev_priv(ndev);
953
954         return mgt_set_request(priv, DOT11_OID_RTSTHRESH, 0, &vwrq->value);
955 }
956
957 static int
958 prism54_get_rts(struct net_device *ndev, struct iw_request_info *info,
959                 struct iw_param *vwrq, char *extra)
960 {
961         islpci_private *priv = netdev_priv(ndev);
962         union oid_res_t r;
963         int rvalue;
964
965         /* get the rts threshold */
966         rvalue = mgt_get_request(priv, DOT11_OID_RTSTHRESH, 0, NULL, &r);
967         vwrq->value = r.u;
968
969         return rvalue;
970 }
971
972 static int
973 prism54_set_frag(struct net_device *ndev, struct iw_request_info *info,
974                  struct iw_param *vwrq, char *extra)
975 {
976         islpci_private *priv = netdev_priv(ndev);
977
978         return mgt_set_request(priv, DOT11_OID_FRAGTHRESH, 0, &vwrq->value);
979 }
980
981 static int
982 prism54_get_frag(struct net_device *ndev, struct iw_request_info *info,
983                  struct iw_param *vwrq, char *extra)
984 {
985         islpci_private *priv = netdev_priv(ndev);
986         union oid_res_t r;
987         int rvalue;
988
989         rvalue = mgt_get_request(priv, DOT11_OID_FRAGTHRESH, 0, NULL, &r);
990         vwrq->value = r.u;
991
992         return rvalue;
993 }
994
995 /* Here we have (min,max) = max retries for (small frames, big frames). Where
996  * big frame <=>  bigger than the rts threshold
997  * small frame <=>  smaller than the rts threshold
998  * This is not really the behavior expected by the wireless tool but it seems
999  * to be a common behavior in other drivers.
1000  * 
1001  * It seems that playing with this tends to hang the card -> DISABLED
1002  */
1003
1004 static int
1005 prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
1006                   struct iw_param *vwrq, char *extra)
1007 {
1008         islpci_private *priv = netdev_priv(ndev);
1009         u32 slimit = 0, llimit = 0;     /* short and long limit */
1010         u32 lifetime = 0;
1011         int rvalue = 0;
1012
1013         if (vwrq->disabled)
1014                 /* we cannot disable this feature */
1015                 return -EINVAL;
1016
1017         if (vwrq->flags & IW_RETRY_LIMIT) {
1018                 if (vwrq->flags & IW_RETRY_MIN)
1019                         slimit = vwrq->value;
1020                 else if (vwrq->flags & IW_RETRY_MAX)
1021                         llimit = vwrq->value;
1022                 else {
1023                         /* we are asked to set both */
1024                         slimit = vwrq->value;
1025                         llimit = vwrq->value;
1026                 }
1027         }
1028         if (vwrq->flags & IW_RETRY_LIFETIME)
1029                 /* Wireless tools use us unit while the device uses 1024 us unit */
1030                 lifetime = vwrq->value / 1024;
1031
1032         /* now set what is requested */
1033
1034         if (slimit != 0)
1035                 rvalue =
1036                     mgt_set_request(priv, DOT11_OID_SHORTRETRIES, 0, &slimit);
1037         if (llimit != 0)
1038                 rvalue |=
1039                     mgt_set_request(priv, DOT11_OID_LONGRETRIES, 0, &llimit);
1040         if (lifetime != 0)
1041                 rvalue |=
1042                     mgt_set_request(priv, DOT11_OID_MAXTXLIFETIME, 0,
1043                                     &lifetime);
1044
1045         return rvalue;
1046 }
1047
1048 static int
1049 prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
1050                   struct iw_param *vwrq, char *extra)
1051 {
1052         islpci_private *priv = netdev_priv(ndev);
1053         union oid_res_t r;
1054         int rvalue = 0;
1055         vwrq->disabled = 0;     /* It cannot be disabled */
1056
1057         if ((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
1058                 /* we are asked for the life time */
1059                 rvalue =
1060                     mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
1061                 vwrq->value = r.u * 1024;
1062                 vwrq->flags = IW_RETRY_LIFETIME;
1063         } else if ((vwrq->flags & IW_RETRY_MAX)) {
1064                 /* we are asked for the long retry limit */
1065                 rvalue |=
1066                     mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
1067                 vwrq->value = r.u;
1068                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
1069         } else {
1070                 /* default. get the  short retry limit */
1071                 rvalue |=
1072                     mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
1073                 vwrq->value = r.u;
1074                 vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
1075         }
1076
1077         return rvalue;
1078 }
1079
1080 static int
1081 prism54_set_encode(struct net_device *ndev, struct iw_request_info *info,
1082                    struct iw_point *dwrq, char *extra)
1083 {
1084         islpci_private *priv = netdev_priv(ndev);
1085         int rvalue = 0, force = 0;
1086         int authen = DOT11_AUTH_OS, invoke = 0, exunencrypt = 0;
1087         union oid_res_t r;
1088
1089         /* with the new API, it's impossible to get a NULL pointer.
1090          * New version of iwconfig set the IW_ENCODE_NOKEY flag
1091          * when no key is given, but older versions don't. */
1092
1093         if (dwrq->length > 0) {
1094                 /* we have a key to set */
1095                 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1096                 int current_index;
1097                 struct obj_key key = { DOT11_PRIV_WEP, 0, "" };
1098
1099                 /* get the current key index */
1100                 rvalue = mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
1101                 current_index = r.u;
1102                 /* Verify that the key is not marked as invalid */
1103                 if (!(dwrq->flags & IW_ENCODE_NOKEY)) {
1104                         key.length = dwrq->length > sizeof (key.key) ?
1105                             sizeof (key.key) : dwrq->length;
1106                         memcpy(key.key, extra, key.length);
1107                         if (key.length == 32)
1108                                 /* we want WPA-PSK */
1109                                 key.type = DOT11_PRIV_TKIP;
1110                         if ((index < 0) || (index > 3))
1111                                 /* no index provided use the current one */
1112                                 index = current_index;
1113
1114                         /* now send the key to the card  */
1115                         rvalue |=
1116                             mgt_set_request(priv, DOT11_OID_DEFKEYX, index,
1117                                             &key);
1118                 }
1119                 /*
1120                  * If a valid key is set, encryption should be enabled 
1121                  * (user may turn it off later).
1122                  * This is also how "iwconfig ethX key on" works
1123                  */
1124                 if ((index == current_index) && (key.length > 0))
1125                         force = 1;
1126         } else {
1127                 int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1128                 if ((index >= 0) && (index <= 3)) {
1129                         /* we want to set the key index */
1130                         rvalue |=
1131                             mgt_set_request(priv, DOT11_OID_DEFKEYID, 0,
1132                                             &index);
1133                 } else {
1134                         if (!dwrq->flags & IW_ENCODE_MODE) {
1135                                 /* we cannot do anything. Complain. */
1136                                 return -EINVAL;
1137                         }
1138                 }
1139         }
1140
1141         /* now read the flags     */
1142         if (dwrq->flags & IW_ENCODE_DISABLED) {
1143                 /* Encoding disabled, 
1144                  * authen = DOT11_AUTH_OS;
1145                  * invoke = 0;
1146                  * exunencrypt = 0; */
1147         }
1148         if (dwrq->flags & IW_ENCODE_OPEN)
1149                 /* Encode but accept non-encoded packets. No auth */
1150                 invoke = 1;
1151         if ((dwrq->flags & IW_ENCODE_RESTRICTED) || force) {
1152                 /* Refuse non-encoded packets. Auth */
1153                 authen = DOT11_AUTH_BOTH;
1154                 invoke = 1;
1155                 exunencrypt = 1;
1156         }
1157         /* do the change if requested  */
1158         if ((dwrq->flags & IW_ENCODE_MODE) || force) {
1159                 rvalue |=
1160                     mgt_set_request(priv, DOT11_OID_AUTHENABLE, 0, &authen);
1161                 rvalue |=
1162                     mgt_set_request(priv, DOT11_OID_PRIVACYINVOKED, 0, &invoke);
1163                 rvalue |=
1164                     mgt_set_request(priv, DOT11_OID_EXUNENCRYPTED, 0,
1165                                     &exunencrypt);
1166         }
1167         return rvalue;
1168 }
1169
1170 static int
1171 prism54_get_encode(struct net_device *ndev, struct iw_request_info *info,
1172                    struct iw_point *dwrq, char *extra)
1173 {
1174         islpci_private *priv = netdev_priv(ndev);
1175         struct obj_key *key;
1176         u32 devindex, index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
1177         u32 authen = 0, invoke = 0, exunencrypt = 0;
1178         int rvalue;
1179         union oid_res_t r;
1180
1181         /* first get the flags */
1182         rvalue = mgt_get_request(priv, DOT11_OID_AUTHENABLE, 0, NULL, &r);
1183         authen = r.u;
1184         rvalue |= mgt_get_request(priv, DOT11_OID_PRIVACYINVOKED, 0, NULL, &r);
1185         invoke = r.u;
1186         rvalue |= mgt_get_request(priv, DOT11_OID_EXUNENCRYPTED, 0, NULL, &r);
1187         exunencrypt = r.u;
1188
1189         if (invoke && (authen == DOT11_AUTH_BOTH) && exunencrypt)
1190                 dwrq->flags = IW_ENCODE_RESTRICTED;
1191         else if ((authen == DOT11_AUTH_OS) && !exunencrypt) {
1192                 if (invoke)
1193                         dwrq->flags = IW_ENCODE_OPEN;
1194                 else
1195                         dwrq->flags = IW_ENCODE_DISABLED;
1196         } else
1197                 /* The card should not work in this state */
1198                 dwrq->flags = 0;
1199
1200         /* get the current device key index */
1201         rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYID, 0, NULL, &r);
1202         devindex = r.u;
1203         /* Now get the key, return it */
1204         if ((index < 0) || (index > 3))
1205                 /* no index provided, use the current one */
1206                 index = devindex;
1207         rvalue |= mgt_get_request(priv, DOT11_OID_DEFKEYX, index, NULL, &r);
1208         key = r.ptr;
1209         dwrq->length = key->length;
1210         memcpy(extra, key->key, dwrq->length);
1211         kfree(key);
1212         /* return the used key index */
1213         dwrq->flags |= devindex + 1;
1214
1215         return rvalue;
1216 }
1217
1218 static int
1219 prism54_get_txpower(struct net_device *ndev, struct iw_request_info *info,
1220                     struct iw_param *vwrq, char *extra)
1221 {
1222         islpci_private *priv = netdev_priv(ndev);
1223         union oid_res_t r;
1224         int rvalue;
1225
1226         rvalue = mgt_get_request(priv, OID_INL_OUTPUTPOWER, 0, NULL, &r);
1227         /* intersil firmware operates in 0.25 dBm (1/4 dBm) */
1228         vwrq->value = (s32)r.u / 4;
1229         vwrq->fixed = 1;
1230         /* radio is not turned of
1231          * btw: how is possible to turn off only the radio 
1232          */
1233         vwrq->disabled = 0;
1234
1235         return rvalue;
1236 }
1237
1238 static int
1239 prism54_set_txpower(struct net_device *ndev, struct iw_request_info *info,
1240                     struct iw_param *vwrq, char *extra)
1241 {
1242         islpci_private *priv = netdev_priv(ndev);
1243         s32 u = vwrq->value;
1244
1245         /* intersil firmware operates in 0.25 dBm (1/4) */
1246         u *= 4;
1247         if (vwrq->disabled) {
1248                 /* don't know how to disable radio */
1249                 printk(KERN_DEBUG
1250                        "%s: %s() disabling radio is not yet supported.\n",
1251                        priv->ndev->name, __FUNCTION__);
1252                 return -ENOTSUPP;
1253         } else if (vwrq->fixed)
1254                 /* currently only fixed value is supported */
1255                 return mgt_set_request(priv, OID_INL_OUTPUTPOWER, 0, &u);
1256         else {
1257                 printk(KERN_DEBUG
1258                        "%s: %s() auto power will be implemented later.\n",
1259                        priv->ndev->name, __FUNCTION__);
1260                 return -ENOTSUPP;
1261         }
1262 }
1263
1264 static int
1265 prism54_reset(struct net_device *ndev, struct iw_request_info *info,
1266               __u32 * uwrq, char *extra)
1267 {
1268         islpci_reset(netdev_priv(ndev), 0);
1269
1270         return 0;
1271 }
1272
1273 static int
1274 prism54_set_beacon(struct net_device *ndev, struct iw_request_info *info,
1275                    __u32 * uwrq, char *extra)
1276 {
1277         int rvalue = mgt_set_request((islpci_private *) netdev_priv(ndev),
1278                                      DOT11_OID_BEACONPERIOD, 0, uwrq);
1279
1280         return (rvalue ? rvalue : -EINPROGRESS);
1281 }
1282
1283 static int
1284 prism54_get_beacon(struct net_device *ndev, struct iw_request_info *info,
1285                    __u32 * uwrq, char *extra)
1286 {
1287         union oid_res_t r;
1288         int rvalue;
1289
1290         rvalue =
1291             mgt_get_request((islpci_private *) netdev_priv(ndev),
1292                             DOT11_OID_BEACONPERIOD, 0, NULL, &r);
1293         *uwrq = r.u;
1294
1295         return rvalue;
1296 }
1297
1298 void
1299 prism54_acl_init(struct islpci_acl *acl)
1300 {
1301         sema_init(&acl->sem, 1);
1302         INIT_LIST_HEAD(&acl->mac_list);
1303         acl->size = 0;
1304         acl->policy = MAC_POLICY_OPEN;
1305 }
1306
1307 static void
1308 prism54_clear_mac(struct islpci_acl *acl)
1309 {
1310         struct list_head *ptr, *next;
1311         struct mac_entry *entry;
1312
1313         if (down_interruptible(&acl->sem))
1314                 return;
1315
1316         if (acl->size == 0) {
1317                 up(&acl->sem);
1318                 return;
1319         }
1320
1321         for (ptr = acl->mac_list.next, next = ptr->next;
1322              ptr != &acl->mac_list; ptr = next, next = ptr->next) {
1323                 entry = list_entry(ptr, struct mac_entry, _list);
1324                 list_del(ptr);
1325                 kfree(entry);
1326         }
1327         acl->size = 0;
1328         up(&acl->sem);
1329 }
1330
1331 void
1332 prism54_acl_clean(struct islpci_acl *acl)
1333 {
1334         prism54_clear_mac(acl);
1335 }
1336
1337 static int
1338 prism54_add_mac(struct net_device *ndev, struct iw_request_info *info,
1339                 struct sockaddr *awrq, char *extra)
1340 {
1341         islpci_private *priv = netdev_priv(ndev);
1342         struct islpci_acl *acl = &priv->acl;
1343         struct mac_entry *entry;
1344         struct sockaddr *addr = (struct sockaddr *) extra;
1345
1346         if (addr->sa_family != ARPHRD_ETHER)
1347                 return -EOPNOTSUPP;
1348
1349         entry = kmalloc(sizeof (struct mac_entry), GFP_KERNEL);
1350         if (entry == NULL)
1351                 return -ENOMEM;
1352
1353         memcpy(entry->addr, addr->sa_data, ETH_ALEN);
1354
1355         if (down_interruptible(&acl->sem)) {
1356                 kfree(entry);
1357                 return -ERESTARTSYS;
1358         }
1359         list_add_tail(&entry->_list, &acl->mac_list);
1360         acl->size++;
1361         up(&acl->sem);
1362
1363         return 0;
1364 }
1365
1366 static int
1367 prism54_del_mac(struct net_device *ndev, struct iw_request_info *info,
1368                 struct sockaddr *awrq, char *extra)
1369 {
1370         islpci_private *priv = netdev_priv(ndev);
1371         struct islpci_acl *acl = &priv->acl;
1372         struct mac_entry *entry;
1373         struct list_head *ptr;
1374         struct sockaddr *addr = (struct sockaddr *) extra;
1375
1376         if (addr->sa_family != ARPHRD_ETHER)
1377                 return -EOPNOTSUPP;
1378
1379         if (down_interruptible(&acl->sem))
1380                 return -ERESTARTSYS;
1381         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1382                 entry = list_entry(ptr, struct mac_entry, _list);
1383
1384                 if (memcmp(entry->addr, addr->sa_data, ETH_ALEN) == 0) {
1385                         list_del(ptr);
1386                         acl->size--;
1387                         kfree(entry);
1388                         up(&acl->sem);
1389                         return 0;
1390                 }
1391         }
1392         up(&acl->sem);
1393         return -EINVAL;
1394 }
1395
1396 static int
1397 prism54_get_mac(struct net_device *ndev, struct iw_request_info *info,
1398                 struct iw_point *dwrq, char *extra)
1399 {
1400         islpci_private *priv = netdev_priv(ndev);
1401         struct islpci_acl *acl = &priv->acl;
1402         struct mac_entry *entry;
1403         struct list_head *ptr;
1404         struct sockaddr *dst = (struct sockaddr *) extra;
1405
1406         dwrq->length = 0;
1407
1408         if (down_interruptible(&acl->sem))
1409                 return -ERESTARTSYS;
1410
1411         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1412                 entry = list_entry(ptr, struct mac_entry, _list);
1413
1414                 memcpy(dst->sa_data, entry->addr, ETH_ALEN);
1415                 dst->sa_family = ARPHRD_ETHER;
1416                 dwrq->length++;
1417                 dst++;
1418         }
1419         up(&acl->sem);
1420         return 0;
1421 }
1422
1423 /* Setting policy also clears the MAC acl, even if we don't change the defaut
1424  * policy
1425  */
1426
1427 static int
1428 prism54_set_policy(struct net_device *ndev, struct iw_request_info *info,
1429                    __u32 * uwrq, char *extra)
1430 {
1431         islpci_private *priv = netdev_priv(ndev);
1432         struct islpci_acl *acl = &priv->acl;
1433         u32 mlmeautolevel;
1434
1435         prism54_clear_mac(acl);
1436
1437         if ((*uwrq < MAC_POLICY_OPEN) || (*uwrq > MAC_POLICY_REJECT))
1438                 return -EINVAL;
1439
1440         down_write(&priv->mib_sem);
1441
1442         acl->policy = *uwrq;
1443
1444         /* the ACL code needs an intermediate mlmeautolevel */
1445         if ((priv->iw_mode == IW_MODE_MASTER) &&
1446             (acl->policy != MAC_POLICY_OPEN))
1447                 mlmeautolevel = DOT11_MLME_INTERMEDIATE;
1448         else
1449                 mlmeautolevel = CARD_DEFAULT_MLME_MODE;
1450         if (priv->wpa)
1451                 mlmeautolevel = DOT11_MLME_EXTENDED;
1452         mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &mlmeautolevel);
1453         /* restart the card with our new policy */
1454         mgt_commit(priv);
1455         up_write(&priv->mib_sem);
1456
1457         return 0;
1458 }
1459
1460 static int
1461 prism54_get_policy(struct net_device *ndev, struct iw_request_info *info,
1462                    __u32 * uwrq, char *extra)
1463 {
1464         islpci_private *priv = netdev_priv(ndev);
1465         struct islpci_acl *acl = &priv->acl;
1466
1467         *uwrq = acl->policy;
1468
1469         return 0;
1470 }
1471
1472 /* Return 1 only if client should be accepted. */
1473
1474 static int
1475 prism54_mac_accept(struct islpci_acl *acl, char *mac)
1476 {
1477         struct list_head *ptr;
1478         struct mac_entry *entry;
1479         int res = 0;
1480
1481         if (down_interruptible(&acl->sem))
1482                 return -ERESTARTSYS;
1483
1484         if (acl->policy == MAC_POLICY_OPEN) {
1485                 up(&acl->sem);
1486                 return 1;
1487         }
1488
1489         for (ptr = acl->mac_list.next; ptr != &acl->mac_list; ptr = ptr->next) {
1490                 entry = list_entry(ptr, struct mac_entry, _list);
1491                 if (memcmp(entry->addr, mac, ETH_ALEN) == 0) {
1492                         res = 1;
1493                         break;
1494                 }
1495         }
1496         res = (acl->policy == MAC_POLICY_ACCEPT) ? !res : res;
1497         up(&acl->sem);
1498
1499         return res;
1500 }
1501
1502 static int
1503 prism54_kick_all(struct net_device *ndev, struct iw_request_info *info,
1504                  struct iw_point *dwrq, char *extra)
1505 {
1506         struct obj_mlme *mlme;
1507         int rvalue;
1508
1509         mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
1510         if (mlme == NULL)
1511                 return -ENOMEM;
1512
1513         /* Tell the card to kick every client */
1514         mlme->id = cpu_to_le16(0);
1515         rvalue = mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
1516         kfree(mlme);
1517
1518         return rvalue;
1519 }
1520
1521 static int
1522 prism54_kick_mac(struct net_device *ndev, struct iw_request_info *info,
1523                  struct sockaddr *awrq, char *extra)
1524 {
1525         struct obj_mlme *mlme;
1526         struct sockaddr *addr = (struct sockaddr *) extra;
1527         int rvalue;
1528
1529         if (addr->sa_family != ARPHRD_ETHER)
1530                 return -EOPNOTSUPP;
1531
1532         mlme = kmalloc(sizeof (struct obj_mlme), GFP_KERNEL);
1533         if (mlme == NULL)
1534                 return -ENOMEM;
1535
1536         /* Tell the card to only kick the corresponding bastard */
1537         memcpy(mlme->address, addr->sa_data, ETH_ALEN);
1538         mlme->id = cpu_to_le16(-1);
1539         rvalue = mgt_set_request(netdev_priv(ndev), DOT11_OID_DISASSOCIATE, 0, mlme);
1540
1541         kfree(mlme);
1542
1543         return rvalue;
1544 }
1545
1546 /* Translate a TRAP oid into a wireless event. Called in islpci_mgt_receive. */
1547
1548 static inline void
1549 format_event(islpci_private *priv, char *dest, const char *str,
1550              const struct obj_mlme *mlme, u16 *length, int error)
1551 {
1552         const u8 *a = mlme->address;
1553         int n = snprintf(dest, IW_CUSTOM_MAX,
1554                          "%s %s %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X %s",
1555                          str,
1556                          ((priv->iw_mode == IW_MODE_MASTER) ? "to" : "from"),
1557                          a[0], a[1], a[2], a[3], a[4], a[5],
1558                          (error ? (mlme->code ? " : REJECTED " : " : ACCEPTED ")
1559                           : ""));
1560         BUG_ON(n > IW_CUSTOM_MAX);
1561         *length = n;
1562 }
1563
1564 static void
1565 send_formatted_event(islpci_private *priv, const char *str,
1566                      const struct obj_mlme *mlme, int error)
1567 {
1568         union iwreq_data wrqu;
1569
1570         wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
1571         if (!wrqu.data.pointer)
1572                 return;
1573         wrqu.data.length = 0;
1574         format_event(priv, wrqu.data.pointer, str, mlme, &wrqu.data.length,
1575                      error);
1576         wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
1577         kfree(wrqu.data.pointer);
1578 }
1579
1580 static void
1581 send_simple_event(islpci_private *priv, const char *str)
1582 {
1583         union iwreq_data wrqu;
1584         int n = strlen(str);
1585
1586         wrqu.data.pointer = kmalloc(IW_CUSTOM_MAX, GFP_KERNEL);
1587         if (!wrqu.data.pointer)
1588                 return;
1589         BUG_ON(n > IW_CUSTOM_MAX);
1590         wrqu.data.length = n;
1591         strcpy(wrqu.data.pointer, str);
1592         wireless_send_event(priv->ndev, IWEVCUSTOM, &wrqu, wrqu.data.pointer);
1593         kfree(wrqu.data.pointer);
1594 }
1595
1596 static void
1597 link_changed(struct net_device *ndev, u32 bitrate)
1598 {
1599         islpci_private *priv = netdev_priv(ndev);
1600
1601         if (le32_to_cpu(bitrate)) {
1602                 if (priv->iw_mode == IW_MODE_INFRA) {
1603                         union iwreq_data uwrq;
1604                         prism54_get_wap(ndev, NULL, (struct sockaddr *) &uwrq,
1605                                         NULL);
1606                         wireless_send_event(ndev, SIOCGIWAP, &uwrq, NULL);
1607                 } else
1608                         send_simple_event(netdev_priv(ndev), "Link established");
1609         } else
1610                 send_simple_event(netdev_priv(ndev), "Link lost");
1611 }
1612
1613 /* Beacon/ProbeResp payload header */
1614 struct ieee80211_beacon_phdr {
1615         u8 timestamp[8];
1616         u16 beacon_int;
1617         u16 capab_info;
1618 } __attribute__ ((packed));
1619
1620 #define WLAN_EID_GENERIC 0xdd
1621 static u8 wpa_oid[4] = { 0x00, 0x50, 0xf2, 1 };
1622
1623 #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
1624 #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
1625
1626 void
1627 prism54_wpa_ie_add(islpci_private *priv, u8 *bssid,
1628                    u8 *wpa_ie, size_t wpa_ie_len)
1629 {
1630         struct list_head *ptr;
1631         struct islpci_bss_wpa_ie *bss = NULL;
1632
1633         if (wpa_ie_len > MAX_WPA_IE_LEN)
1634                 wpa_ie_len = MAX_WPA_IE_LEN;
1635
1636         if (down_interruptible(&priv->wpa_sem))
1637                 return;
1638
1639         /* try to use existing entry */
1640         list_for_each(ptr, &priv->bss_wpa_list) {
1641                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1642                 if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0) {
1643                         list_move(&bss->list, &priv->bss_wpa_list);
1644                         break;
1645                 }
1646                 bss = NULL;
1647         }
1648
1649         if (bss == NULL) {
1650                 /* add a new BSS entry; if max number of entries is already
1651                  * reached, replace the least recently updated */
1652                 if (priv->num_bss_wpa >= MAX_BSS_WPA_IE_COUNT) {
1653                         bss = list_entry(priv->bss_wpa_list.prev,
1654                                          struct islpci_bss_wpa_ie, list);
1655                         list_del(&bss->list);
1656                 } else {
1657                         bss = kmalloc(sizeof (*bss), GFP_ATOMIC);
1658                         if (bss != NULL) {
1659                                 priv->num_bss_wpa++;
1660                                 memset(bss, 0, sizeof (*bss));
1661                         }
1662                 }
1663                 if (bss != NULL) {
1664                         memcpy(bss->bssid, bssid, ETH_ALEN);
1665                         list_add(&bss->list, &priv->bss_wpa_list);
1666                 }
1667         }
1668
1669         if (bss != NULL) {
1670                 memcpy(bss->wpa_ie, wpa_ie, wpa_ie_len);
1671                 bss->wpa_ie_len = wpa_ie_len;
1672                 bss->last_update = jiffies;
1673         } else {
1674                 printk(KERN_DEBUG "Failed to add BSS WPA entry for " MACSTR
1675                        "\n", MAC2STR(bssid));
1676         }
1677
1678         /* expire old entries from WPA list */
1679         while (priv->num_bss_wpa > 0) {
1680                 bss = list_entry(priv->bss_wpa_list.prev,
1681                                  struct islpci_bss_wpa_ie, list);
1682                 if (!time_after(jiffies, bss->last_update + 60 * HZ))
1683                         break;
1684
1685                 list_del(&bss->list);
1686                 priv->num_bss_wpa--;
1687                 kfree(bss);
1688         }
1689
1690         up(&priv->wpa_sem);
1691 }
1692
1693 size_t
1694 prism54_wpa_ie_get(islpci_private *priv, u8 *bssid, u8 *wpa_ie)
1695 {
1696         struct list_head *ptr;
1697         struct islpci_bss_wpa_ie *bss = NULL;
1698         size_t len = 0;
1699
1700         if (down_interruptible(&priv->wpa_sem))
1701                 return 0;
1702
1703         list_for_each(ptr, &priv->bss_wpa_list) {
1704                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1705                 if (memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
1706                         break;
1707                 bss = NULL;
1708         }
1709         if (bss) {
1710                 len = bss->wpa_ie_len;
1711                 memcpy(wpa_ie, bss->wpa_ie, len);
1712         }
1713         up(&priv->wpa_sem);
1714
1715         return len;
1716 }
1717
1718 void
1719 prism54_wpa_ie_init(islpci_private *priv)
1720 {
1721         INIT_LIST_HEAD(&priv->bss_wpa_list);
1722         sema_init(&priv->wpa_sem, 1);
1723 }
1724
1725 void
1726 prism54_wpa_ie_clean(islpci_private *priv)
1727 {
1728         struct list_head *ptr, *n;
1729
1730         list_for_each_safe(ptr, n, &priv->bss_wpa_list) {
1731                 struct islpci_bss_wpa_ie *bss;
1732                 bss = list_entry(ptr, struct islpci_bss_wpa_ie, list);
1733                 kfree(bss);
1734         }
1735 }
1736
1737 static void
1738 prism54_process_bss_data(islpci_private *priv, u32 oid, u8 *addr,
1739                          u8 *payload, size_t len)
1740 {
1741         struct ieee80211_beacon_phdr *hdr;
1742         u8 *pos, *end;
1743
1744         if (!priv->wpa)
1745                 return;
1746
1747         hdr = (struct ieee80211_beacon_phdr *) payload;
1748         pos = (u8 *) (hdr + 1);
1749         end = payload + len;
1750         while (pos < end) {
1751                 if (pos + 2 + pos[1] > end) {
1752                         printk(KERN_DEBUG "Parsing Beacon/ProbeResp failed "
1753                                "for " MACSTR "\n", MAC2STR(addr));
1754                         return;
1755                 }
1756                 if (pos[0] == WLAN_EID_GENERIC && pos[1] >= 4 &&
1757                     memcmp(pos + 2, wpa_oid, 4) == 0) {
1758                         prism54_wpa_ie_add(priv, addr, pos, pos[1] + 2);
1759                         return;
1760                 }
1761                 pos += 2 + pos[1];
1762         }
1763 }
1764
1765 static void
1766 handle_request(islpci_private *priv, struct obj_mlme *mlme, enum oid_num_t oid)
1767 {
1768         if (((le16_to_cpu(mlme->state) == DOT11_STATE_AUTHING) ||
1769              (le16_to_cpu(mlme->state) == DOT11_STATE_ASSOCING))
1770             && mgt_mlme_answer(priv)) {
1771                 /* Someone is requesting auth and we must respond. Just send back
1772                  * the trap with error code set accordingly.
1773                  */
1774                 mlme->code = cpu_to_le16(prism54_mac_accept(&priv->acl,
1775                                                             mlme->
1776                                                             address) ? 0 : 1);
1777                 mgt_set_request(priv, oid, 0, mlme);
1778         }
1779 }
1780
1781 int
1782 prism54_process_trap_helper(islpci_private *priv, enum oid_num_t oid,
1783                             char *data)
1784 {
1785         struct obj_mlme *mlme = (struct obj_mlme *) data;
1786         size_t len;
1787         u8 *payload, *pos = (u8 *) (mlme + 1);
1788
1789         len = pos[0] | (pos[1] << 8);   /* little endian data length */
1790         payload = pos + 2;
1791
1792         /* I think all trapable objects are listed here.
1793          * Some oids have a EX version. The difference is that they are emitted
1794          * in DOT11_MLME_EXTENDED mode (set with DOT11_OID_MLMEAUTOLEVEL)
1795          * with more info.
1796          * The few events already defined by the wireless tools are not really
1797          * suited. We use the more flexible custom event facility.
1798          */
1799
1800         switch (oid) {
1801
1802         case GEN_OID_LINKSTATE:
1803                 link_changed(priv->ndev, (u32) *data);
1804                 break;
1805
1806         case DOT11_OID_MICFAILURE:
1807                 send_simple_event(priv, "Mic failure");
1808                 break;
1809
1810         case DOT11_OID_DEAUTHENTICATE:
1811                 send_formatted_event(priv, "DeAuthenticate request", mlme, 0);
1812                 break;
1813
1814         case DOT11_OID_AUTHENTICATE:
1815                 handle_request(priv, mlme, oid);
1816                 send_formatted_event(priv, "Authenticate request", mlme, 1);
1817                 break;
1818
1819         case DOT11_OID_DISASSOCIATE:
1820                 send_formatted_event(priv, "Disassociate request", mlme, 0);
1821                 break;
1822
1823         case DOT11_OID_ASSOCIATE:
1824                 handle_request(priv, mlme, oid);
1825                 send_formatted_event(priv, "Associate request", mlme, 1);
1826                 break;
1827
1828         case DOT11_OID_REASSOCIATE:
1829                 handle_request(priv, mlme, oid);
1830                 send_formatted_event(priv, "ReAssociate request", mlme, 1);
1831                 break;
1832
1833         case DOT11_OID_BEACON:
1834                 prism54_process_bss_data(priv, oid, mlme->address,
1835                                          payload, len);
1836                 send_formatted_event(priv,
1837                                      "Received a beacon from an unkown AP",
1838                                      mlme, 0);
1839                 break;
1840
1841         case DOT11_OID_PROBE:
1842                 /* we received a probe from a client. */
1843                 prism54_process_bss_data(priv, oid, mlme->address,
1844                                          payload, len);
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_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
1919 {
1920         /* should we really support this old stuff ? */
1921         return -EOPNOTSUPP;
1922 }
1923
1924 int
1925 prism54_set_wpa(struct net_device *ndev, struct iw_request_info *info,
1926                 __u32 * uwrq, char *extra)
1927 {
1928         islpci_private *priv = netdev_priv(ndev);
1929
1930         down_write(&priv->mib_sem);
1931
1932         priv->wpa = *uwrq;
1933         if (priv->wpa) {
1934                 u32 l = DOT11_MLME_EXTENDED;
1935                 mgt_set(priv, DOT11_OID_MLMEAUTOLEVEL, &l);
1936         }
1937         /* restart the card with new level. Needed ? */
1938         mgt_commit(priv);
1939         up_write(&priv->mib_sem);
1940
1941         return 0;
1942 }
1943
1944 int
1945 prism54_get_wpa(struct net_device *ndev, struct iw_request_info *info,
1946                 __u32 * uwrq, char *extra)
1947 {
1948         islpci_private *priv = netdev_priv(ndev);
1949         *uwrq = priv->wpa;
1950         return 0;
1951 }
1952
1953 int
1954 prism54_set_maxframeburst(struct net_device *ndev, struct iw_request_info *info,
1955                 __u32 *uwrq, char *extra)
1956 {
1957         islpci_private *priv = netdev_priv(ndev);
1958         u32 max_burst;
1959
1960         max_burst = (*uwrq) ? *uwrq : CARD_DEFAULT_MAXFRAMEBURST;
1961         mgt_set_request(priv, DOT11_OID_MAXFRAMEBURST, 0, &max_burst);
1962
1963         return -EINPROGRESS; /* Call commit handler */
1964 }
1965
1966 int
1967 prism54_get_maxframeburst(struct net_device *ndev, struct iw_request_info *info,
1968                 __u32 *uwrq, char *extra)
1969 {
1970         islpci_private *priv = netdev_priv(ndev);
1971         union oid_res_t r;
1972         int rvalue;
1973         
1974         rvalue = mgt_get_request(priv, DOT11_OID_MAXFRAMEBURST, 0, NULL, &r);
1975         *uwrq = r.u;
1976         
1977         return rvalue;
1978 }
1979
1980 int
1981 prism54_set_profile(struct net_device *ndev, struct iw_request_info *info,
1982                 __u32 *uwrq, char *extra)
1983 {
1984         islpci_private *priv = netdev_priv(ndev);
1985         u32 profile;
1986
1987         profile = (*uwrq) ? *uwrq : CARD_DEFAULT_PROFILE;
1988         mgt_set_request(priv, DOT11_OID_PROFILES, 0, &profile);
1989         
1990         return -EINPROGRESS; /* Call commit handler */
1991 }
1992
1993 int
1994 prism54_get_profile(struct net_device *ndev, struct iw_request_info *info,
1995                 __u32 *uwrq, char *extra)
1996 {
1997         islpci_private *priv = netdev_priv(ndev);
1998         union oid_res_t r;
1999         int rvalue;
2000
2001         rvalue = mgt_get_request(priv, DOT11_OID_PROFILES, 0, NULL, &r);
2002         *uwrq = r.u;
2003
2004         return rvalue;
2005 }
2006
2007 int
2008 prism54_oid(struct net_device *ndev, struct iw_request_info *info,
2009                 __u32 *uwrq, char *extra)
2010 {
2011         islpci_private *priv = netdev_priv(ndev);
2012         
2013         priv->priv_oid = *uwrq;
2014         printk("%s: oid 0x%08X\n", ndev->name, *uwrq);
2015
2016         return 0;
2017 }
2018
2019 int
2020 prism54_get_oid(struct net_device *ndev, struct iw_request_info *info,
2021                 struct iw_point *data, char *extra)
2022 {
2023         islpci_private *priv = netdev_priv(ndev);
2024         struct islpci_mgmtframe *response = NULL;
2025         int ret = -EIO, response_op = PIMFOR_OP_ERROR;
2026         
2027         printk("%s: get_oid 0x%08X\n", ndev->name, priv->priv_oid);
2028         data->length = 0;
2029         
2030         if (islpci_get_state(priv) >= PRV_STATE_INIT) {
2031                 ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_GET, priv->priv_oid, extra, 256, &response);
2032                 response_op = response->header->operation;
2033                 printk("%s: ret: %i\n", ndev->name, ret);
2034                 printk("%s: response_op: %i\n", ndev->name, response_op);
2035                 if (ret || !response || response->header->operation == PIMFOR_OP_ERROR) {
2036                         if (response) {
2037                                 islpci_mgt_release(response);
2038                         }
2039                         printk("%s: EIO\n", ndev->name);
2040                         ret = -EIO;
2041                 }
2042                 if (!ret) {
2043                         data->length = response->header->length;
2044                         memcpy(extra, response->data, data->length);
2045                         islpci_mgt_release(response);
2046                         printk("%s: len: %i\n", ndev->name, data->length);
2047                 }
2048         }
2049         
2050         return ret;
2051 }
2052
2053 int
2054 prism54_set_oid(struct net_device *ndev, struct iw_request_info *info,
2055                 struct iw_point *data, char *extra)
2056 {
2057         islpci_private *priv = netdev_priv(ndev);
2058         struct islpci_mgmtframe *response = NULL;
2059         int ret = 0, response_op = PIMFOR_OP_ERROR;
2060         
2061         printk("%s: set_oid 0x%08X\tlen: %d\n", ndev->name, priv->priv_oid, data->length);
2062         
2063         if (islpci_get_state(priv) >= PRV_STATE_INIT) {
2064                 ret = islpci_mgt_transaction(priv->ndev, PIMFOR_OP_SET, priv->priv_oid, extra, data->length, &response);
2065                 printk("%s: ret: %i\n", ndev->name, ret);
2066                 if (!ret) {
2067                         response_op = response->header->operation;
2068                         printk("%s: response_op: %i\n", ndev->name, response_op);
2069                         islpci_mgt_release(response);
2070                 }
2071                 if (ret || response_op == PIMFOR_OP_ERROR) {
2072                         printk("%s: EIO\n", ndev->name);
2073                         ret = -EIO;
2074                 }
2075         }
2076         
2077         return ret;
2078 }
2079
2080 static const iw_handler prism54_handler[] = {
2081         (iw_handler) prism54_commit,    /* SIOCSIWCOMMIT */
2082         (iw_handler) prism54_get_name,  /* SIOCGIWNAME */
2083         (iw_handler) NULL,      /* SIOCSIWNWID */
2084         (iw_handler) NULL,      /* SIOCGIWNWID */
2085         (iw_handler) prism54_set_freq,  /* SIOCSIWFREQ */
2086         (iw_handler) prism54_get_freq,  /* SIOCGIWFREQ */
2087         (iw_handler) prism54_set_mode,  /* SIOCSIWMODE */
2088         (iw_handler) prism54_get_mode,  /* SIOCGIWMODE */
2089         (iw_handler) prism54_set_sens,  /* SIOCSIWSENS */
2090         (iw_handler) prism54_get_sens,  /* SIOCGIWSENS */
2091         (iw_handler) NULL,      /* SIOCSIWRANGE */
2092         (iw_handler) prism54_get_range, /* SIOCGIWRANGE */
2093         (iw_handler) NULL,      /* SIOCSIWPRIV */
2094         (iw_handler) NULL,      /* SIOCGIWPRIV */
2095         (iw_handler) NULL,      /* SIOCSIWSTATS */
2096         (iw_handler) NULL,      /* SIOCGIWSTATS */
2097         iw_handler_set_spy,     /* SIOCSIWSPY */
2098         iw_handler_get_spy,     /* SIOCGIWSPY */
2099         iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
2100         iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
2101         (iw_handler) prism54_set_wap,   /* SIOCSIWAP */
2102         (iw_handler) prism54_get_wap,   /* SIOCGIWAP */
2103         (iw_handler) NULL,      /* -- hole -- */
2104         (iw_handler) NULL,      /* SIOCGIWAPLIST depreciated */
2105         (iw_handler) prism54_set_scan,  /* SIOCSIWSCAN */
2106         (iw_handler) prism54_get_scan,  /* SIOCGIWSCAN */
2107         (iw_handler) prism54_set_essid, /* SIOCSIWESSID */
2108         (iw_handler) prism54_get_essid, /* SIOCGIWESSID */
2109         (iw_handler) prism54_set_nick,  /* SIOCSIWNICKN */
2110         (iw_handler) prism54_get_nick,  /* SIOCGIWNICKN */
2111         (iw_handler) NULL,      /* -- hole -- */
2112         (iw_handler) NULL,      /* -- hole -- */
2113         (iw_handler) prism54_set_rate,  /* SIOCSIWRATE */
2114         (iw_handler) prism54_get_rate,  /* SIOCGIWRATE */
2115         (iw_handler) prism54_set_rts,   /* SIOCSIWRTS */
2116         (iw_handler) prism54_get_rts,   /* SIOCGIWRTS */
2117         (iw_handler) prism54_set_frag,  /* SIOCSIWFRAG */
2118         (iw_handler) prism54_get_frag,  /* SIOCGIWFRAG */
2119         (iw_handler) prism54_set_txpower,       /* SIOCSIWTXPOW */
2120         (iw_handler) prism54_get_txpower,       /* SIOCGIWTXPOW */
2121         (iw_handler) prism54_set_retry, /* SIOCSIWRETRY */
2122         (iw_handler) prism54_get_retry, /* SIOCGIWRETRY */
2123         (iw_handler) prism54_set_encode,        /* SIOCSIWENCODE */
2124         (iw_handler) prism54_get_encode,        /* SIOCGIWENCODE */
2125         (iw_handler) NULL,      /* SIOCSIWPOWER */
2126         (iw_handler) NULL,      /* SIOCGIWPOWER */
2127 };
2128
2129 /* The low order bit identify a SET (0) or a GET (1) ioctl.  */
2130
2131 #define PRISM54_RESET           SIOCIWFIRSTPRIV
2132 #define PRISM54_GET_BEACON      SIOCIWFIRSTPRIV+1
2133 #define PRISM54_SET_BEACON      SIOCIWFIRSTPRIV+2
2134 #define PRISM54_GET_POLICY SIOCIWFIRSTPRIV+3
2135 #define PRISM54_SET_POLICY SIOCIWFIRSTPRIV+4
2136 #define PRISM54_GET_MAC            SIOCIWFIRSTPRIV+5
2137 #define PRISM54_ADD_MAC            SIOCIWFIRSTPRIV+6
2138
2139 #define PRISM54_DEL_MAC    SIOCIWFIRSTPRIV+8
2140
2141 #define PRISM54_KICK_MAC   SIOCIWFIRSTPRIV+10
2142
2143 #define PRISM54_KICK_ALL   SIOCIWFIRSTPRIV+12
2144
2145 #define PRISM54_GET_WPA    SIOCIWFIRSTPRIV+13
2146 #define PRISM54_SET_WPA    SIOCIWFIRSTPRIV+14
2147
2148 #define PRISM54_OID        SIOCIWFIRSTPRIV+16
2149 #define PRISM54_GET_OID    SIOCIWFIRSTPRIV+17
2150 #define PRISM54_SET_OID    SIOCIWFIRSTPRIV+18
2151
2152 static const struct iw_priv_args prism54_private_args[] = {
2153 /*{ cmd, set_args, get_args, name } */
2154         {PRISM54_RESET, 0, 0, "reset"},
2155         {PRISM54_GET_BEACON, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2156          "getBeaconPeriod"},
2157         {PRISM54_SET_BEACON, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2158          "setBeaconPeriod"},
2159         {PRISM54_GET_POLICY, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2160          "getPolicy"},
2161         {PRISM54_SET_POLICY, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2162          "setPolicy"},
2163         {PRISM54_GET_MAC, 0, IW_PRIV_TYPE_ADDR | 64, "getMac"},
2164         {PRISM54_ADD_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2165          "addMac"},
2166         {PRISM54_DEL_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2167          "delMac"},
2168         {PRISM54_KICK_MAC, IW_PRIV_TYPE_ADDR | IW_PRIV_SIZE_FIXED | 1, 0,
2169          "kickMac"},
2170         {PRISM54_KICK_ALL, 0, 0, "kickAll"},
2171         {PRISM54_GET_WPA, 0, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
2172          "get_wpa"},
2173         {PRISM54_SET_WPA, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0,
2174          "set_wpa"},
2175         {PRISM54_OID, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "oid"},
2176         {PRISM54_GET_OID, 0, IW_PRIV_TYPE_BYTE | 256, "get_oid"},
2177         {PRISM54_SET_OID, IW_PRIV_TYPE_BYTE | 256, 0, "set_oid"},
2178 };
2179
2180 static const iw_handler prism54_private_handler[] = {
2181         (iw_handler) prism54_reset,
2182         (iw_handler) prism54_get_beacon,
2183         (iw_handler) prism54_set_beacon,
2184         (iw_handler) prism54_get_policy,
2185         (iw_handler) prism54_set_policy,
2186         (iw_handler) prism54_get_mac,
2187         (iw_handler) prism54_add_mac,
2188         (iw_handler) NULL,
2189         (iw_handler) prism54_del_mac,
2190         (iw_handler) NULL,
2191         (iw_handler) prism54_kick_mac,
2192         (iw_handler) NULL,
2193         (iw_handler) prism54_kick_all,
2194         (iw_handler) prism54_get_wpa,
2195         (iw_handler) prism54_set_wpa,
2196         (iw_handler) NULL,
2197         (iw_handler) prism54_oid,
2198         (iw_handler) prism54_get_oid,
2199         (iw_handler) prism54_set_oid,
2200 };
2201
2202 const struct iw_handler_def prism54_handler_def = {
2203         .num_standard = sizeof (prism54_handler) / sizeof (iw_handler),
2204         .num_private = sizeof (prism54_private_handler) / sizeof (iw_handler),
2205         .num_private_args =
2206             sizeof (prism54_private_args) / sizeof (struct iw_priv_args),
2207         .standard = (iw_handler *) prism54_handler,
2208         .private = (iw_handler *) prism54_private_handler,
2209         .private_args = (struct iw_priv_args *) prism54_private_args,
2210 };
2211