Staging: wlan-ng: Remove AP-only code from MLME functions.
[firefly-linux-kernel-4.4.55.git] / drivers / staging / wlan-ng / p80211netdev.c
1 /* src/p80211/p80211knetdev.c
2 *
3 * Linux Kernel net device interface
4 *
5 * Copyright (C) 1999 AbsoluteValue Systems, Inc.  All Rights Reserved.
6 * --------------------------------------------------------------------
7 *
8 * linux-wlan
9 *
10 *   The contents of this file are subject to the Mozilla Public
11 *   License Version 1.1 (the "License"); you may not use this file
12 *   except in compliance with the License. You may obtain a copy of
13 *   the License at http://www.mozilla.org/MPL/
14 *
15 *   Software distributed under the License is distributed on an "AS
16 *   IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 *   implied. See the License for the specific language governing
18 *   rights and limitations under the License.
19 *
20 *   Alternatively, the contents of this file may be used under the
21 *   terms of the GNU Public License version 2 (the "GPL"), in which
22 *   case the provisions of the GPL are applicable instead of the
23 *   above.  If you wish to allow the use of your version of this file
24 *   only under the terms of the GPL and not to allow others to use
25 *   your version of this file under the MPL, indicate your decision
26 *   by deleting the provisions above and replace them with the notice
27 *   and other provisions required by the GPL.  If you do not delete
28 *   the provisions above, a recipient may use your version of this
29 *   file under either the MPL or the GPL.
30 *
31 * --------------------------------------------------------------------
32 *
33 * Inquiries regarding the linux-wlan Open Source project can be
34 * made directly to:
35 *
36 * AbsoluteValue Systems Inc.
37 * info@linux-wlan.com
38 * http://www.linux-wlan.com
39 *
40 * --------------------------------------------------------------------
41 *
42 * Portions of the development of this software were funded by
43 * Intersil Corporation as part of PRISM(R) chipset product development.
44 *
45 * --------------------------------------------------------------------
46 *
47 * The functions required for a Linux network device are defined here.
48 *
49 * --------------------------------------------------------------------
50 */
51
52
53 /*================================================================*/
54 /* System Includes */
55
56
57 #include <linux/version.h>
58
59 #include <linux/module.h>
60 #include <linux/kernel.h>
61 #include <linux/sched.h>
62 #include <linux/types.h>
63 #include <linux/skbuff.h>
64 #include <linux/slab.h>
65 #include <linux/proc_fs.h>
66 #include <linux/interrupt.h>
67 #include <linux/netdevice.h>
68 #include <linux/kmod.h>
69 #include <linux/if_arp.h>
70 #include <linux/wireless.h>
71 #include <linux/sockios.h>
72 #include <linux/etherdevice.h>
73
74 #include <asm/bitops.h>
75 #include <asm/uaccess.h>
76 #include <asm/byteorder.h>
77
78 #ifdef SIOCETHTOOL
79 #include <linux/ethtool.h>
80 #endif
81
82 #include <net/iw_handler.h>
83 #include <net/net_namespace.h>
84
85 /*================================================================*/
86 /* Project Includes */
87
88 #include "version.h"
89 #include "wlan_compat.h"
90 #include "p80211types.h"
91 #include "p80211hdr.h"
92 #include "p80211conv.h"
93 #include "p80211mgmt.h"
94 #include "p80211msg.h"
95 #include "p80211netdev.h"
96 #include "p80211ioctl.h"
97 #include "p80211req.h"
98 #include "p80211metastruct.h"
99 #include "p80211metadef.h"
100
101 /*================================================================*/
102 /* Local Constants */
103
104 /*================================================================*/
105 /* Local Macros */
106
107
108 /*================================================================*/
109 /* Local Types */
110
111 /*================================================================*/
112 /* Local Static Definitions */
113
114 #define __NO_VERSION__          /* prevent the static definition */
115
116 #ifdef CONFIG_PROC_FS
117 static struct proc_dir_entry    *proc_p80211;
118 #endif
119
120 /*================================================================*/
121 /* Local Function Declarations */
122
123 /* Support functions */
124 static void p80211netdev_rx_bh(unsigned long arg);
125
126 /* netdevice method functions */
127 static int p80211knetdev_init( netdevice_t *netdev);
128 static struct net_device_stats* p80211knetdev_get_stats(netdevice_t *netdev);
129 static int p80211knetdev_open( netdevice_t *netdev);
130 static int p80211knetdev_stop( netdevice_t *netdev );
131 static int p80211knetdev_hard_start_xmit( struct sk_buff *skb, netdevice_t *netdev);
132 static void p80211knetdev_set_multicast_list(netdevice_t *dev);
133 static int p80211knetdev_do_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd);
134 static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr);
135 static void p80211knetdev_tx_timeout(netdevice_t *netdev);
136 static int p80211_rx_typedrop( wlandevice_t *wlandev, UINT16 fc);
137
138 #ifdef CONFIG_PROC_FS
139 static int
140 p80211netdev_proc_read(
141         char    *page,
142         char    **start,
143         off_t   offset,
144         int     count,
145         int     *eof,
146         void    *data);
147 #endif
148
149 /*================================================================*/
150 /* Function Definitions */
151
152 /*----------------------------------------------------------------
153 * p80211knetdev_startup
154 *
155 * Initialize the wlandevice/netdevice part of 802.11 services at
156 * load time.
157 *
158 * Arguments:
159 *       none
160 *
161 * Returns:
162 *       nothing
163 ----------------------------------------------------------------*/
164 void p80211netdev_startup(void)
165 {
166         DBFENTER;
167
168 #ifdef CONFIG_PROC_FS
169         if (init_net.proc_net != NULL) {
170                 proc_p80211 = create_proc_entry(
171                                 "p80211",
172                                 (S_IFDIR|S_IRUGO|S_IXUGO),
173                                 init_net.proc_net);
174         }
175 #endif
176         DBFEXIT;
177         return;
178 }
179
180 /*----------------------------------------------------------------
181 * p80211knetdev_shutdown
182 *
183 * Shutdown the wlandevice/netdevice part of 802.11 services at
184 * unload time.
185 *
186 * Arguments:
187 *       none
188 *
189 * Returns:
190 *       nothing
191 ----------------------------------------------------------------*/
192 void
193 p80211netdev_shutdown(void)
194 {
195         DBFENTER;
196 #ifdef CONFIG_PROC_FS
197         if (proc_p80211 != NULL) {
198                 remove_proc_entry("p80211", init_net.proc_net);
199         }
200 #endif
201         DBFEXIT;
202 }
203
204 /*----------------------------------------------------------------
205 * p80211knetdev_init
206 *
207 * Init method for a Linux netdevice.  Called in response to
208 * register_netdev.
209 *
210 * Arguments:
211 *       none
212 *
213 * Returns:
214 *       nothing
215 ----------------------------------------------------------------*/
216 static int p80211knetdev_init( netdevice_t *netdev)
217 {
218         DBFENTER;
219         /* Called in response to register_netdev */
220         /* This is usually the probe function, but the probe has */
221         /* already been done by the MSD and the create_kdev */
222         /* function.  All we do here is return success */
223         DBFEXIT;
224         return 0;
225 }
226
227
228 /*----------------------------------------------------------------
229 * p80211knetdev_get_stats
230 *
231 * Statistics retrieval for linux netdevices.  Here we're reporting
232 * the Linux i/f level statistics.  Hence, for the primary numbers,
233 * we don't want to report the numbers from the MIB.  Eventually,
234 * it might be useful to collect some of the error counters though.
235 *
236 * Arguments:
237 *       netdev          Linux netdevice
238 *
239 * Returns:
240 *       the address of the statistics structure
241 ----------------------------------------------------------------*/
242 static struct net_device_stats*
243 p80211knetdev_get_stats(netdevice_t *netdev)
244 {
245         wlandevice_t    *wlandev = netdev->ml_priv;
246         DBFENTER;
247
248         /* TODO: review the MIB stats for items that correspond to
249                 linux stats */
250
251         DBFEXIT;
252         return &(wlandev->linux_stats);
253 }
254
255
256 /*----------------------------------------------------------------
257 * p80211knetdev_open
258 *
259 * Linux netdevice open method.  Following a successful call here,
260 * the device is supposed to be ready for tx and rx.  In our
261 * situation that may not be entirely true due to the state of the
262 * MAC below.
263 *
264 * Arguments:
265 *       netdev          Linux network device structure
266 *
267 * Returns:
268 *       zero on success, non-zero otherwise
269 ----------------------------------------------------------------*/
270 static int p80211knetdev_open( netdevice_t *netdev )
271 {
272         int             result = 0; /* success */
273         wlandevice_t    *wlandev = netdev->ml_priv;
274
275         DBFENTER;
276
277         /* Check to make sure the MSD is running */
278         if ( wlandev->msdstate != WLAN_MSD_RUNNING ) {
279                 return -ENODEV;
280         }
281
282         /* Tell the MSD to open */
283         if ( wlandev->open != NULL) {
284                 result = wlandev->open(wlandev);
285                 if ( result == 0 ) {
286                         p80211netdev_start_queue(wlandev);
287                         wlandev->state = WLAN_DEVICE_OPEN;
288                 }
289         } else {
290                 result = -EAGAIN;
291         }
292
293         DBFEXIT;
294         return result;
295 }
296
297
298 /*----------------------------------------------------------------
299 * p80211knetdev_stop
300 *
301 * Linux netdevice stop (close) method.  Following this call,
302 * no frames should go up or down through this interface.
303 *
304 * Arguments:
305 *       netdev          Linux network device structure
306 *
307 * Returns:
308 *       zero on success, non-zero otherwise
309 ----------------------------------------------------------------*/
310 static int p80211knetdev_stop( netdevice_t *netdev )
311 {
312         int             result = 0;
313         wlandevice_t    *wlandev = netdev->ml_priv;
314
315         DBFENTER;
316
317         if ( wlandev->close != NULL ) {
318                 result = wlandev->close(wlandev);
319         }
320
321         p80211netdev_stop_queue(wlandev);
322         wlandev->state = WLAN_DEVICE_CLOSED;
323
324         DBFEXIT;
325         return result;
326 }
327
328 /*----------------------------------------------------------------
329 * p80211netdev_rx
330 *
331 * Frame receive function called by the mac specific driver.
332 *
333 * Arguments:
334 *       wlandev         WLAN network device structure
335 *       skb             skbuff containing a full 802.11 frame.
336 * Returns:
337 *       nothing
338 * Side effects:
339 *
340 ----------------------------------------------------------------*/
341 void
342 p80211netdev_rx(wlandevice_t *wlandev, struct sk_buff *skb )
343 {
344         DBFENTER;
345
346         /* Enqueue for post-irq processing */
347         skb_queue_tail(&wlandev->nsd_rxq, skb);
348
349         tasklet_schedule(&wlandev->rx_bh);
350
351         DBFEXIT;
352         return;
353 }
354
355 /*----------------------------------------------------------------
356 * p80211netdev_rx_bh
357 *
358 * Deferred processing of all received frames.
359 *
360 * Arguments:
361 *       wlandev         WLAN network device structure
362 *       skb             skbuff containing a full 802.11 frame.
363 * Returns:
364 *       nothing
365 * Side effects:
366 *
367 ----------------------------------------------------------------*/
368 static void p80211netdev_rx_bh(unsigned long arg)
369 {
370         wlandevice_t *wlandev = (wlandevice_t *) arg;
371         struct sk_buff *skb = NULL;
372         netdevice_t     *dev = wlandev->netdev;
373         p80211_hdr_a3_t *hdr;
374         UINT16 fc;
375
376         DBFENTER;
377
378         /* Let's empty our our queue */
379         while ( (skb = skb_dequeue(&wlandev->nsd_rxq)) ) {
380                 if (wlandev->state == WLAN_DEVICE_OPEN) {
381
382                         if (dev->type != ARPHRD_ETHER) {
383                                 /* RAW frame; we shouldn't convert it */
384                                 // XXX Append the Prism Header here instead.
385
386                                 /* set up various data fields */
387                                 skb->dev = dev;
388                                 skb_reset_mac_header(skb);
389                                 skb->ip_summed = CHECKSUM_NONE;
390                                 skb->pkt_type = PACKET_OTHERHOST;
391                                 skb->protocol = htons(ETH_P_80211_RAW);
392                                 dev->last_rx = jiffies;
393
394                                 wlandev->linux_stats.rx_packets++;
395                                 wlandev->linux_stats.rx_bytes += skb->len;
396                                 netif_rx_ni(skb);
397                                 continue;
398                         } else {
399                                 hdr = (p80211_hdr_a3_t *)skb->data;
400                                 fc = ieee2host16(hdr->fc);
401                                 if (p80211_rx_typedrop(wlandev, fc)) {
402                                         dev_kfree_skb(skb);
403                                         continue;
404                                 }
405
406                                 /* perform mcast filtering */
407                                 if (wlandev->netdev->flags & IFF_ALLMULTI) {
408                                         /* allow my local address through */
409                                         if (memcmp(hdr->a1, wlandev->netdev->dev_addr, WLAN_ADDR_LEN) != 0) {
410                                                 /* but reject anything else that isn't multicast */
411                                                 if (!(hdr->a1[0] & 0x01)) {
412                                                         dev_kfree_skb(skb);
413                                                         continue;
414                                                 }
415                                         }
416                                 }
417
418                                 if ( skb_p80211_to_ether(wlandev, wlandev->ethconv, skb) == 0 ) {
419                                         skb->dev->last_rx = jiffies;
420                                         wlandev->linux_stats.rx_packets++;
421                                         wlandev->linux_stats.rx_bytes += skb->len;
422                                         netif_rx_ni(skb);
423                                         continue;
424                                 }
425                                 WLAN_LOG_DEBUG(1, "p80211_to_ether failed.\n");
426                         }
427                 }
428                 dev_kfree_skb(skb);
429         }
430
431         DBFEXIT;
432 }
433
434
435 /*----------------------------------------------------------------
436 * p80211knetdev_hard_start_xmit
437 *
438 * Linux netdevice method for transmitting a frame.
439 *
440 * Arguments:
441 *       skb     Linux sk_buff containing the frame.
442 *       netdev  Linux netdevice.
443 *
444 * Side effects:
445 *       If the lower layers report that buffers are full. netdev->tbusy
446 *       will be set to prevent higher layers from sending more traffic.
447 *
448 *       Note: If this function returns non-zero, higher layers retain
449 *             ownership of the skb.
450 *
451 * Returns:
452 *       zero on success, non-zero on failure.
453 ----------------------------------------------------------------*/
454 static int p80211knetdev_hard_start_xmit( struct sk_buff *skb, netdevice_t *netdev)
455 {
456         int             result = 0;
457         int             txresult = -1;
458         wlandevice_t    *wlandev = netdev->ml_priv;
459         p80211_hdr_t    p80211_hdr;
460         p80211_metawep_t p80211_wep;
461
462         DBFENTER;
463
464         if (skb == NULL) {
465                 return 0;
466         }
467
468         if (wlandev->state != WLAN_DEVICE_OPEN) {
469                 result = 1;
470                 goto failed;
471         }
472
473         memset(&p80211_hdr, 0, sizeof(p80211_hdr_t));
474         memset(&p80211_wep, 0, sizeof(p80211_metawep_t));
475
476         if ( netif_queue_stopped(netdev) ) {
477                 WLAN_LOG_DEBUG(1, "called when queue stopped.\n");
478                 result = 1;
479                 goto failed;
480         }
481
482         netif_stop_queue(netdev);
483
484         /* Check to see that a valid mode is set */
485         switch( wlandev->macmode ) {
486         case WLAN_MACMODE_IBSS_STA:
487         case WLAN_MACMODE_ESS_STA:
488         case WLAN_MACMODE_ESS_AP:
489                 break;
490         default:
491                 /* Mode isn't set yet, just drop the frame
492                  * and return success .
493                  * TODO: we need a saner way to handle this
494                  */
495                 if(skb->protocol != ETH_P_80211_RAW) {
496                         p80211netdev_start_queue(wlandev);
497                         WLAN_LOG_NOTICE(
498                                 "Tx attempt prior to association, frame dropped.\n");
499                         wlandev->linux_stats.tx_dropped++;
500                         result = 0;
501                         goto failed;
502                 }
503                 break;
504         }
505
506         /* Check for raw transmits */
507         if(skb->protocol == ETH_P_80211_RAW) {
508                 if (!capable(CAP_NET_ADMIN)) {
509                         result = 1;
510                         goto failed;
511                 }
512                 /* move the header over */
513                 memcpy(&p80211_hdr, skb->data, sizeof(p80211_hdr_t));
514                 skb_pull(skb, sizeof(p80211_hdr_t));
515         } else {
516                 if ( skb_ether_to_p80211(wlandev, wlandev->ethconv, skb, &p80211_hdr, &p80211_wep) != 0 ) {
517                         /* convert failed */
518                         WLAN_LOG_DEBUG(1, "ether_to_80211(%d) failed.\n",
519                                         wlandev->ethconv);
520                         result = 1;
521                         goto failed;
522                 }
523         }
524         if ( wlandev->txframe == NULL ) {
525                 result = 1;
526                 goto failed;
527         }
528
529         netdev->trans_start = jiffies;
530
531         wlandev->linux_stats.tx_packets++;
532         /* count only the packet payload */
533         wlandev->linux_stats.tx_bytes += skb->len;
534
535         txresult = wlandev->txframe(wlandev, skb, &p80211_hdr, &p80211_wep);
536
537         if ( txresult == 0) {
538                 /* success and more buf */
539                 /* avail, re: hw_txdata */
540                 p80211netdev_wake_queue(wlandev);
541                 result = 0;
542         } else if ( txresult == 1 ) {
543                 /* success, no more avail */
544                 WLAN_LOG_DEBUG(3, "txframe success, no more bufs\n");
545                 /* netdev->tbusy = 1;  don't set here, irqhdlr */
546                 /*   may have already cleared it */
547                 result = 0;
548         } else if ( txresult == 2 ) {
549                 /* alloc failure, drop frame */
550                 WLAN_LOG_DEBUG(3, "txframe returned alloc_fail\n");
551                 result = 1;
552         } else {
553                 /* buffer full or queue busy, drop frame. */
554                 WLAN_LOG_DEBUG(3, "txframe returned full or busy\n");
555                 result = 1;
556         }
557
558  failed:
559         /* Free up the WEP buffer if it's not the same as the skb */
560         if ((p80211_wep.data) && (p80211_wep.data != skb->data))
561                 kfree(p80211_wep.data);
562
563         /* we always free the skb here, never in a lower level. */
564         if (!result)
565                 dev_kfree_skb(skb);
566
567         DBFEXIT;
568         return result;
569 }
570
571
572 /*----------------------------------------------------------------
573 * p80211knetdev_set_multicast_list
574 *
575 * Called from higher lavers whenever there's a need to set/clear
576 * promiscuous mode or rewrite the multicast list.
577 *
578 * Arguments:
579 *       none
580 *
581 * Returns:
582 *       nothing
583 ----------------------------------------------------------------*/
584 static void p80211knetdev_set_multicast_list(netdevice_t *dev)
585 {
586         wlandevice_t    *wlandev = dev->ml_priv;
587
588         DBFENTER;
589
590         /* TODO:  real multicast support as well */
591
592         if (wlandev->set_multicast_list)
593                 wlandev->set_multicast_list(wlandev, dev);
594
595         DBFEXIT;
596 }
597
598 #ifdef SIOCETHTOOL
599
600 static int p80211netdev_ethtool(wlandevice_t *wlandev, void __user *useraddr)
601 {
602         UINT32 ethcmd;
603         struct ethtool_drvinfo info;
604         struct ethtool_value edata;
605
606         memset(&info, 0, sizeof(info));
607         memset(&edata, 0, sizeof(edata));
608
609         if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
610                 return -EFAULT;
611
612         switch (ethcmd) {
613         case ETHTOOL_GDRVINFO:
614                 info.cmd = ethcmd;
615                 snprintf(info.driver, sizeof(info.driver), "p80211_%s",
616                          wlandev->nsdname);
617                 snprintf(info.version, sizeof(info.version), "%s",
618                          WLAN_RELEASE);
619
620                 // info.fw_version
621                 // info.bus_info
622
623                 if (copy_to_user(useraddr, &info, sizeof(info)))
624                         return -EFAULT;
625                 return 0;
626 #ifdef ETHTOOL_GLINK
627         case ETHTOOL_GLINK:
628                 edata.cmd = ethcmd;
629
630                 if (wlandev->linkstatus &&
631                     (wlandev->macmode != WLAN_MACMODE_NONE)) {
632                         edata.data = 1;
633                 } else {
634                         edata.data = 0;
635                 }
636
637                 if (copy_to_user(useraddr, &edata, sizeof(edata)))
638                         return -EFAULT;
639                 return 0;
640         }
641 #endif
642
643         return -EOPNOTSUPP;
644 }
645
646 #endif
647
648 /*----------------------------------------------------------------
649 * p80211knetdev_do_ioctl
650 *
651 * Handle an ioctl call on one of our devices.  Everything Linux
652 * ioctl specific is done here.  Then we pass the contents of the
653 * ifr->data to the request message handler.
654 *
655 * Arguments:
656 *       dev     Linux kernel netdevice
657 *       ifr     Our private ioctl request structure, typed for the
658 *               generic struct ifreq so we can use ptr to func
659 *               w/o cast.
660 *
661 * Returns:
662 *       zero on success, a negative errno on failure.  Possible values:
663 *               -ENETDOWN Device isn't up.
664 *               -EBUSY  cmd already in progress
665 *               -ETIME  p80211 cmd timed out (MSD may have its own timers)
666 *               -EFAULT memory fault copying msg from user buffer
667 *               -ENOMEM unable to allocate kernel msg buffer
668 *               -ENOSYS bad magic, it the cmd really for us?
669 *               -EINTR  sleeping on cmd, awakened by signal, cmd cancelled.
670 *
671 * Call Context:
672 *       Process thread (ioctl caller).  TODO: SMP support may require
673 *       locks.
674 ----------------------------------------------------------------*/
675 static int p80211knetdev_do_ioctl(netdevice_t *dev, struct ifreq *ifr, int cmd)
676 {
677         int                     result = 0;
678         p80211ioctl_req_t       *req = (p80211ioctl_req_t*)ifr;
679         wlandevice_t            *wlandev = dev->ml_priv;
680         UINT8                   *msgbuf;
681         DBFENTER;
682
683         WLAN_LOG_DEBUG(2, "rx'd ioctl, cmd=%d, len=%d\n", cmd, req->len);
684
685 #ifdef SIOCETHTOOL
686         if (cmd == SIOCETHTOOL) {
687                 result = p80211netdev_ethtool(wlandev, (void __user *) ifr->ifr_data);
688                 goto bail;
689         }
690 #endif
691
692         /* Test the magic, assume ifr is good if it's there */
693         if ( req->magic != P80211_IOCTL_MAGIC ) {
694                 result = -ENOSYS;
695                 goto bail;
696         }
697
698         if ( cmd == P80211_IFTEST ) {
699                 result = 0;
700                 goto bail;
701         } else if ( cmd != P80211_IFREQ ) {
702                 result = -ENOSYS;
703                 goto bail;
704         }
705
706         /* Allocate a buf of size req->len */
707         if ((msgbuf = kmalloc( req->len, GFP_KERNEL))) {
708                 if ( copy_from_user( msgbuf, (void __user *) req->data, req->len) ) {
709                         result = -EFAULT;
710                 } else {
711                         result = p80211req_dorequest( wlandev, msgbuf);
712                 }
713
714                 if ( result == 0 ) {
715                         if ( copy_to_user( (void __user *) req->data, msgbuf, req->len)) {
716                                 result = -EFAULT;
717                         }
718                 }
719                 kfree(msgbuf);
720         } else {
721                 result = -ENOMEM;
722         }
723 bail:
724         DBFEXIT;
725
726         return result; /* If allocate,copyfrom or copyto fails, return errno */
727 }
728
729 /*----------------------------------------------------------------
730 * p80211knetdev_set_mac_address
731 *
732 * Handles the ioctl for changing the MACAddress of a netdevice
733 *
734 * references: linux/netdevice.h and drivers/net/net_init.c
735 *
736 * NOTE: [MSM] We only prevent address changes when the netdev is
737 * up.  We don't control anything based on dot11 state.  If the
738 * address is changed on a STA that's currently associated, you
739 * will probably lose the ability to send and receive data frames.
740 * Just be aware.  Therefore, this should usually only be done
741 * prior to scan/join/auth/assoc.
742 *
743 * Arguments:
744 *       dev     netdevice struct
745 *       addr    the new MACAddress (a struct)
746 *
747 * Returns:
748 *       zero on success, a negative errno on failure.  Possible values:
749 *               -EBUSY  device is bussy (cmd not possible)
750 *               -and errors returned by: p80211req_dorequest(..)
751 *
752 * by: Collin R. Mulliner <collin@mulliner.org>
753 ----------------------------------------------------------------*/
754 static int p80211knetdev_set_mac_address(netdevice_t *dev, void *addr)
755 {
756         struct sockaddr                 *new_addr = addr;
757         p80211msg_dot11req_mibset_t     dot11req;
758         p80211item_unk392_t             *mibattr;
759         p80211item_pstr6_t              *macaddr;
760         p80211item_uint32_t             *resultcode;
761         int result = 0;
762
763         DBFENTER;
764         /* If we're running, we don't allow MAC address changes */
765         if (netif_running(dev)) {
766                 return -EBUSY;
767         }
768
769         /* Set up some convenience pointers. */
770         mibattr = &dot11req.mibattribute;
771         macaddr = (p80211item_pstr6_t*)&mibattr->data;
772         resultcode = &dot11req.resultcode;
773
774         /* Set up a dot11req_mibset */
775         memset(&dot11req, 0, sizeof(p80211msg_dot11req_mibset_t));
776         dot11req.msgcode = DIDmsg_dot11req_mibset;
777         dot11req.msglen = sizeof(p80211msg_dot11req_mibset_t);
778         memcpy(dot11req.devname,
779                 ((wlandevice_t *)dev->ml_priv)->name,
780                 WLAN_DEVNAMELEN_MAX - 1);
781
782         /* Set up the mibattribute argument */
783         mibattr->did = DIDmsg_dot11req_mibset_mibattribute;
784         mibattr->status = P80211ENUM_msgitem_status_data_ok;
785         mibattr->len = sizeof(mibattr->data);
786
787         macaddr->did = DIDmib_dot11mac_dot11OperationTable_dot11MACAddress;
788         macaddr->status = P80211ENUM_msgitem_status_data_ok;
789         macaddr->len = sizeof(macaddr->data);
790         macaddr->data.len = WLAN_ADDR_LEN;
791         memcpy(&macaddr->data.data, new_addr->sa_data, WLAN_ADDR_LEN);
792
793         /* Set up the resultcode argument */
794         resultcode->did = DIDmsg_dot11req_mibset_resultcode;
795         resultcode->status = P80211ENUM_msgitem_status_no_value;
796         resultcode->len = sizeof(resultcode->data);
797         resultcode->data = 0;
798
799         /* now fire the request */
800         result = p80211req_dorequest(dev->ml_priv, (UINT8 *)&dot11req);
801
802         /* If the request wasn't successful, report an error and don't
803          * change the netdev address
804          */
805         if ( result != 0 || resultcode->data != P80211ENUM_resultcode_success) {
806                 WLAN_LOG_ERROR(
807                 "Low-level driver failed dot11req_mibset(dot11MACAddress).\n");
808                 result = -EADDRNOTAVAIL;
809         } else {
810                 /* everything's ok, change the addr in netdev */
811                 memcpy(dev->dev_addr, new_addr->sa_data, dev->addr_len);
812         }
813
814         DBFEXIT;
815         return result;
816 }
817
818 static int wlan_change_mtu(netdevice_t *dev, int new_mtu)
819 {
820         DBFENTER;
821         // 2312 is max 802.11 payload, 20 is overhead, (ether + llc +snap)
822         // and another 8 for wep.
823         if ( (new_mtu < 68) || (new_mtu > (2312 - 20 - 8)))
824                 return -EINVAL;
825
826         dev->mtu = new_mtu;
827
828         DBFEXIT;
829
830         return 0;
831 }
832
833
834
835 /*----------------------------------------------------------------
836 * wlan_setup
837 *
838 * Roughly matches the functionality of ether_setup.  Here
839 * we set up any members of the wlandevice structure that are common
840 * to all devices.  Additionally, we allocate a linux 'struct device'
841 * and perform the same setup as ether_setup.
842 *
843 * Note: It's important that the caller have setup the wlandev->name
844 *       ptr prior to calling this function.
845 *
846 * Arguments:
847 *       wlandev         ptr to the wlandev structure for the
848 *                       interface.
849 * Returns:
850 *       zero on success, non-zero otherwise.
851 * Call Context:
852 *       Should be process thread.  We'll assume it might be
853 *       interrupt though.  When we add support for statically
854 *       compiled drivers, this function will be called in the
855 *       context of the kernel startup code.
856 ----------------------------------------------------------------*/
857 int wlan_setup(wlandevice_t *wlandev)
858 {
859         int             result = 0;
860         netdevice_t     *dev;
861
862         DBFENTER;
863
864         /* Set up the wlandev */
865         wlandev->state = WLAN_DEVICE_CLOSED;
866         wlandev->ethconv = WLAN_ETHCONV_8021h;
867         wlandev->macmode = WLAN_MACMODE_NONE;
868
869         /* Set up the rx queue */
870         skb_queue_head_init(&wlandev->nsd_rxq);
871         tasklet_init(&wlandev->rx_bh,
872                      p80211netdev_rx_bh,
873                      (unsigned long)wlandev);
874
875         /* Allocate and initialize the struct device */
876         dev = kmalloc(sizeof(netdevice_t), GFP_ATOMIC);
877         if ( dev == NULL ) {
878                 WLAN_LOG_ERROR("Failed to alloc netdev.\n");
879                 result = 1;
880         } else {
881                 memset( dev, 0, sizeof(netdevice_t));
882                 ether_setup(dev);
883                 wlandev->netdev = dev;
884                 dev->ml_priv = wlandev;
885                 dev->hard_start_xmit =  p80211knetdev_hard_start_xmit;
886                 dev->get_stats =        p80211knetdev_get_stats;
887 #ifdef HAVE_PRIVATE_IOCTL
888                 dev->do_ioctl =         p80211knetdev_do_ioctl;
889 #endif
890 #ifdef HAVE_MULTICAST
891                 dev->set_multicast_list = p80211knetdev_set_multicast_list;
892 #endif
893                 dev->init =             p80211knetdev_init;
894                 dev->open =             p80211knetdev_open;
895                 dev->stop =             p80211knetdev_stop;
896
897 #ifdef CONFIG_NET_WIRELESS
898 #if (WIRELESS_EXT < 21)
899                 dev->get_wireless_stats = p80211wext_get_wireless_stats;
900 #endif
901                 dev->wireless_handlers = &p80211wext_handler_def;
902 #endif
903
904                 netif_stop_queue(dev);
905 #ifdef HAVE_CHANGE_MTU
906                 dev->change_mtu = wlan_change_mtu;
907 #endif
908 #ifdef HAVE_SET_MAC_ADDR
909                 dev->set_mac_address =  p80211knetdev_set_mac_address;
910 #endif
911 #ifdef HAVE_TX_TIMEOUT
912                 dev->tx_timeout      =  &p80211knetdev_tx_timeout;
913                 dev->watchdog_timeo  =  (wlan_watchdog * HZ) / 1000;
914 #endif
915                 netif_carrier_off(dev);
916         }
917
918         DBFEXIT;
919         return result;
920 }
921
922 /*----------------------------------------------------------------
923 * wlan_unsetup
924 *
925 * This function is paired with the wlan_setup routine.  It should
926 * be called after unregister_wlandev.  Basically, all it does is
927 * free the 'struct device' that's associated with the wlandev.
928 * We do it here because the 'struct device' isn't allocated
929 * explicitly in the driver code, it's done in wlan_setup.  To
930 * do the free in the driver might seem like 'magic'.
931 *
932 * Arguments:
933 *       wlandev         ptr to the wlandev structure for the
934 *                       interface.
935 * Returns:
936 *       zero on success, non-zero otherwise.
937 * Call Context:
938 *       Should be process thread.  We'll assume it might be
939 *       interrupt though.  When we add support for statically
940 *       compiled drivers, this function will be called in the
941 *       context of the kernel startup code.
942 ----------------------------------------------------------------*/
943 int wlan_unsetup(wlandevice_t *wlandev)
944 {
945         int             result = 0;
946
947         DBFENTER;
948
949         tasklet_kill(&wlandev->rx_bh);
950
951         if (wlandev->netdev == NULL ) {
952                 WLAN_LOG_ERROR("called without wlandev->netdev set.\n");
953                 result = 1;
954         } else {
955                 free_netdev(wlandev->netdev);
956                 wlandev->netdev = NULL;
957         }
958
959         DBFEXIT;
960         return 0;
961 }
962
963
964
965 /*----------------------------------------------------------------
966 * register_wlandev
967 *
968 * Roughly matches the functionality of register_netdev.  This function
969 * is called after the driver has successfully probed and set up the
970 * resources for the device.  It's now ready to become a named device
971 * in the Linux system.
972 *
973 * First we allocate a name for the device (if not already set), then
974 * we call the Linux function register_netdevice.
975 *
976 * Arguments:
977 *       wlandev         ptr to the wlandev structure for the
978 *                       interface.
979 * Returns:
980 *       zero on success, non-zero otherwise.
981 * Call Context:
982 *       Can be either interrupt or not.
983 ----------------------------------------------------------------*/
984 int register_wlandev(wlandevice_t *wlandev)
985 {
986         int             i = 0;
987         netdevice_t     *dev = wlandev->netdev;
988
989         DBFENTER;
990
991         i = dev_alloc_name(wlandev->netdev, "wlan%d");
992         if (i >= 0) {
993                 i = register_netdev(wlandev->netdev);
994         }
995         if (i != 0) {
996                 return -EIO;
997         }
998
999         strcpy(wlandev->name, dev->name);
1000
1001 #ifdef CONFIG_PROC_FS
1002         if (proc_p80211) {
1003                 wlandev->procdir = proc_mkdir(wlandev->name, proc_p80211);
1004                 if ( wlandev->procdir )
1005                         wlandev->procwlandev =
1006                                 create_proc_read_entry("wlandev", 0,
1007                                                        wlandev->procdir,
1008                                                        p80211netdev_proc_read,
1009                                                        wlandev);
1010                 if (wlandev->nsd_proc_read)
1011                         create_proc_read_entry("nsd", 0,
1012                                                wlandev->procdir,
1013                                                wlandev->nsd_proc_read,
1014                                                wlandev);
1015         }
1016 #endif
1017
1018         DBFEXIT;
1019         return 0;
1020 }
1021
1022
1023 /*----------------------------------------------------------------
1024 * unregister_wlandev
1025 *
1026 * Roughly matches the functionality of unregister_netdev.  This
1027 * function is called to remove a named device from the system.
1028 *
1029 * First we tell linux that the device should no longer exist.
1030 * Then we remove it from the list of known wlan devices.
1031 *
1032 * Arguments:
1033 *       wlandev         ptr to the wlandev structure for the
1034 *                       interface.
1035 * Returns:
1036 *       zero on success, non-zero otherwise.
1037 * Call Context:
1038 *       Can be either interrupt or not.
1039 ----------------------------------------------------------------*/
1040 int unregister_wlandev(wlandevice_t *wlandev)
1041 {
1042         struct sk_buff *skb;
1043
1044         DBFENTER;
1045
1046 #ifdef CONFIG_PROC_FS
1047         if ( wlandev->procwlandev ) {
1048                 remove_proc_entry("wlandev", wlandev->procdir);
1049         }
1050         if ( wlandev->nsd_proc_read ) {
1051                 remove_proc_entry("nsd", wlandev->procdir);
1052         }
1053         if (wlandev->procdir) {
1054                 remove_proc_entry(wlandev->name, proc_p80211);
1055         }
1056 #endif
1057
1058         unregister_netdev(wlandev->netdev);
1059
1060         /* Now to clean out the rx queue */
1061         while ( (skb = skb_dequeue(&wlandev->nsd_rxq)) ) {
1062                 dev_kfree_skb(skb);
1063         }
1064
1065         DBFEXIT;
1066         return 0;
1067 }
1068
1069 #ifdef CONFIG_PROC_FS
1070 /*----------------------------------------------------------------
1071 * proc_read
1072 *
1073 * Read function for /proc/net/p80211/<device>/wlandev
1074 *
1075 * Arguments:
1076 *       buf
1077 *       start
1078 *       offset
1079 *       count
1080 *       eof
1081 *       data
1082 * Returns:
1083 *       zero on success, non-zero otherwise.
1084 * Call Context:
1085 *       Can be either interrupt or not.
1086 ----------------------------------------------------------------*/
1087 static int
1088 p80211netdev_proc_read(
1089         char    *page,
1090         char    **start,
1091         off_t   offset,
1092         int     count,
1093         int     *eof,
1094         void    *data)
1095 {
1096         char     *p = page;
1097         wlandevice_t *wlandev = (wlandevice_t *) data;
1098
1099         DBFENTER;
1100         if (offset != 0) {
1101                 *eof = 1;
1102                 goto exit;
1103         }
1104
1105         p += sprintf(p, "p80211 version: %s (%s)\n\n",
1106                      WLAN_RELEASE, WLAN_BUILD_DATE);
1107         p += sprintf(p, "name       : %s\n", wlandev->name);
1108         p += sprintf(p, "nsd name   : %s\n", wlandev->nsdname);
1109         p += sprintf(p, "address    : %02x:%02x:%02x:%02x:%02x:%02x\n",
1110                      wlandev->netdev->dev_addr[0], wlandev->netdev->dev_addr[1], wlandev->netdev->dev_addr[2],
1111                      wlandev->netdev->dev_addr[3], wlandev->netdev->dev_addr[4], wlandev->netdev->dev_addr[5]);
1112         p += sprintf(p, "nsd caps   : %s%s%s%s%s%s%s%s%s%s\n",
1113                      (wlandev->nsdcaps & P80211_NSDCAP_HARDWAREWEP) ? "wep_hw " : "",
1114                      (wlandev->nsdcaps & P80211_NSDCAP_TIEDWEP) ? "wep_tied " : "",
1115                      (wlandev->nsdcaps & P80211_NSDCAP_NOHOSTWEP) ? "wep_hw_only " : "",
1116                      (wlandev->nsdcaps & P80211_NSDCAP_PBCC) ? "pbcc " : "",
1117                      (wlandev->nsdcaps & P80211_NSDCAP_SHORT_PREAMBLE) ? "short_preamble " : "",
1118                      (wlandev->nsdcaps & P80211_NSDCAP_AGILITY) ? "agility " : "",
1119                      (wlandev->nsdcaps & P80211_NSDCAP_AP_RETRANSMIT) ? "ap_retransmit " : "",
1120                      (wlandev->nsdcaps & P80211_NSDCAP_HWFRAGMENT) ? "hw_frag " : "",
1121                      (wlandev->nsdcaps & P80211_NSDCAP_AUTOJOIN) ? "autojoin " : "",
1122                      (wlandev->nsdcaps & P80211_NSDCAP_NOSCAN) ? "" : "scan ");
1123
1124
1125         p += sprintf(p, "bssid      : %02x:%02x:%02x:%02x:%02x:%02x\n",
1126                      wlandev->bssid[0], wlandev->bssid[1], wlandev->bssid[2],
1127                      wlandev->bssid[3], wlandev->bssid[4], wlandev->bssid[5]);
1128
1129         p += sprintf(p, "Enabled    : %s%s\n",
1130                      (wlandev->shortpreamble) ? "short_preamble " : "",
1131                      (wlandev->hostwep & HOSTWEP_PRIVACYINVOKED) ? "privacy" : "");
1132
1133
1134  exit:
1135         DBFEXIT;
1136         return (p - page);
1137 }
1138 #endif
1139
1140 /*----------------------------------------------------------------
1141 * p80211netdev_hwremoved
1142 *
1143 * Hardware removed notification. This function should be called
1144 * immediately after an MSD has detected that the underlying hardware
1145 * has been yanked out from under us.  The primary things we need
1146 * to do are:
1147 *   - Mark the wlandev
1148 *   - Prevent any further traffic from the knetdev i/f
1149 *   - Prevent any further requests from mgmt i/f
1150 *   - If there are any waitq'd mgmt requests or mgmt-frame exchanges,
1151 *     shut them down.
1152 *   - Call the MSD hwremoved function.
1153 *
1154 * The remainder of the cleanup will be handled by unregister().
1155 * Our primary goal here is to prevent as much tickling of the MSD
1156 * as possible since the MSD is already in a 'wounded' state.
1157 *
1158 * TODO: As new features are added, this function should be
1159 *       updated.
1160 *
1161 * Arguments:
1162 *       wlandev         WLAN network device structure
1163 * Returns:
1164 *       nothing
1165 * Side effects:
1166 *
1167 * Call context:
1168 *       Usually interrupt.
1169 ----------------------------------------------------------------*/
1170 void p80211netdev_hwremoved(wlandevice_t *wlandev)
1171 {
1172         DBFENTER;
1173         wlandev->hwremoved = 1;
1174         if ( wlandev->state == WLAN_DEVICE_OPEN) {
1175                 p80211netdev_stop_queue(wlandev);
1176         }
1177
1178         netif_device_detach(wlandev->netdev);
1179
1180         DBFEXIT;
1181 }
1182
1183
1184 /*----------------------------------------------------------------
1185 * p80211_rx_typedrop
1186 *
1187 * Classifies the frame, increments the appropriate counter, and
1188 * returns 0|1|2 indicating whether the driver should handle, ignore, or
1189 * drop the frame
1190 *
1191 * Arguments:
1192 *       wlandev         wlan device structure
1193 *       fc              frame control field
1194 *
1195 * Returns:
1196 *       zero if the frame should be handled by the driver,
1197 *       one if the frame should be ignored
1198 *       anything else means we drop it.
1199 *
1200 * Side effects:
1201 *
1202 * Call context:
1203 *       interrupt
1204 ----------------------------------------------------------------*/
1205 static int p80211_rx_typedrop( wlandevice_t *wlandev, UINT16 fc)
1206 {
1207         UINT16  ftype;
1208         UINT16  fstype;
1209         int     drop = 0;
1210         /* Classify frame, increment counter */
1211         ftype = WLAN_GET_FC_FTYPE(fc);
1212         fstype = WLAN_GET_FC_FSTYPE(fc);
1213 #if 0
1214         WLAN_LOG_DEBUG(4,
1215                 "rx_typedrop : ftype=%d fstype=%d.\n", ftype, fstype);
1216 #endif
1217         switch ( ftype ) {
1218         case WLAN_FTYPE_MGMT:
1219                 if ((wlandev->netdev->flags & IFF_PROMISC) ||
1220                         (wlandev->netdev->flags & IFF_ALLMULTI)) {
1221                         drop = 1;
1222                         break;
1223                 }
1224                 WLAN_LOG_DEBUG(3, "rx'd mgmt:\n");
1225                 wlandev->rx.mgmt++;
1226                 switch( fstype ) {
1227                 case WLAN_FSTYPE_ASSOCREQ:
1228                         /* printk("assocreq"); */
1229                         wlandev->rx.assocreq++;
1230                         break;
1231                 case WLAN_FSTYPE_ASSOCRESP:
1232                         /* printk("assocresp"); */
1233                         wlandev->rx.assocresp++;
1234                         break;
1235                 case WLAN_FSTYPE_REASSOCREQ:
1236                         /* printk("reassocreq"); */
1237                         wlandev->rx.reassocreq++;
1238                         break;
1239                 case WLAN_FSTYPE_REASSOCRESP:
1240                         /* printk("reassocresp"); */
1241                         wlandev->rx.reassocresp++;
1242                         break;
1243                 case WLAN_FSTYPE_PROBEREQ:
1244                         /* printk("probereq"); */
1245                         wlandev->rx.probereq++;
1246                         break;
1247                 case WLAN_FSTYPE_PROBERESP:
1248                         /* printk("proberesp"); */
1249                         wlandev->rx.proberesp++;
1250                         break;
1251                 case WLAN_FSTYPE_BEACON:
1252                         /* printk("beacon"); */
1253                         wlandev->rx.beacon++;
1254                         break;
1255                 case WLAN_FSTYPE_ATIM:
1256                         /* printk("atim"); */
1257                         wlandev->rx.atim++;
1258                         break;
1259                 case WLAN_FSTYPE_DISASSOC:
1260                         /* printk("disassoc"); */
1261                         wlandev->rx.disassoc++;
1262                         break;
1263                 case WLAN_FSTYPE_AUTHEN:
1264                         /* printk("authen"); */
1265                         wlandev->rx.authen++;
1266                         break;
1267                 case WLAN_FSTYPE_DEAUTHEN:
1268                         /* printk("deauthen"); */
1269                         wlandev->rx.deauthen++;
1270                         break;
1271                 default:
1272                         /* printk("unknown"); */
1273                         wlandev->rx.mgmt_unknown++;
1274                         break;
1275                 }
1276                 /* printk("\n"); */
1277                 drop = 2;
1278                 break;
1279
1280         case WLAN_FTYPE_CTL:
1281                 if ((wlandev->netdev->flags & IFF_PROMISC) ||
1282                         (wlandev->netdev->flags & IFF_ALLMULTI)) {
1283                         drop = 1;
1284                         break;
1285                 }
1286                 WLAN_LOG_DEBUG(3, "rx'd ctl:\n");
1287                 wlandev->rx.ctl++;
1288                 switch( fstype ) {
1289                 case WLAN_FSTYPE_PSPOLL:
1290                         /* printk("pspoll"); */
1291                         wlandev->rx.pspoll++;
1292                         break;
1293                 case WLAN_FSTYPE_RTS:
1294                         /* printk("rts"); */
1295                         wlandev->rx.rts++;
1296                         break;
1297                 case WLAN_FSTYPE_CTS:
1298                         /* printk("cts"); */
1299                         wlandev->rx.cts++;
1300                         break;
1301                 case WLAN_FSTYPE_ACK:
1302                         /* printk("ack"); */
1303                         wlandev->rx.ack++;
1304                         break;
1305                 case WLAN_FSTYPE_CFEND:
1306                         /* printk("cfend"); */
1307                         wlandev->rx.cfend++;
1308                         break;
1309                 case WLAN_FSTYPE_CFENDCFACK:
1310                         /* printk("cfendcfack"); */
1311                         wlandev->rx.cfendcfack++;
1312                         break;
1313                 default:
1314                         /* printk("unknown"); */
1315                         wlandev->rx.ctl_unknown++;
1316                         break;
1317                 }
1318                 /* printk("\n"); */
1319                 drop = 2;
1320                 break;
1321
1322         case WLAN_FTYPE_DATA:
1323                 wlandev->rx.data++;
1324                 switch( fstype ) {
1325                 case WLAN_FSTYPE_DATAONLY:
1326                         wlandev->rx.dataonly++;
1327                         break;
1328                 case WLAN_FSTYPE_DATA_CFACK:
1329                         wlandev->rx.data_cfack++;
1330                         break;
1331                 case WLAN_FSTYPE_DATA_CFPOLL:
1332                         wlandev->rx.data_cfpoll++;
1333                         break;
1334                 case WLAN_FSTYPE_DATA_CFACK_CFPOLL:
1335                         wlandev->rx.data__cfack_cfpoll++;
1336                         break;
1337                 case WLAN_FSTYPE_NULL:
1338                         WLAN_LOG_DEBUG(3, "rx'd data:null\n");
1339                         wlandev->rx.null++;
1340                         break;
1341                 case WLAN_FSTYPE_CFACK:
1342                         WLAN_LOG_DEBUG(3, "rx'd data:cfack\n");
1343                         wlandev->rx.cfack++;
1344                         break;
1345                 case WLAN_FSTYPE_CFPOLL:
1346                         WLAN_LOG_DEBUG(3, "rx'd data:cfpoll\n");
1347                         wlandev->rx.cfpoll++;
1348                         break;
1349                 case WLAN_FSTYPE_CFACK_CFPOLL:
1350                         WLAN_LOG_DEBUG(3, "rx'd data:cfack_cfpoll\n");
1351                         wlandev->rx.cfack_cfpoll++;
1352                         break;
1353                 default:
1354                         /* printk("unknown"); */
1355                         wlandev->rx.data_unknown++;
1356                         break;
1357                 }
1358
1359                 break;
1360         }
1361         return drop;
1362 }
1363
1364
1365 void    p80211_suspend(wlandevice_t *wlandev)
1366 {
1367         DBFENTER;
1368
1369         DBFEXIT;
1370 }
1371
1372 void    p80211_resume(wlandevice_t *wlandev)
1373 {
1374         DBFENTER;
1375
1376         DBFEXIT;
1377 }
1378
1379 static void p80211knetdev_tx_timeout( netdevice_t *netdev)
1380 {
1381         wlandevice_t    *wlandev = netdev->ml_priv;
1382         DBFENTER;
1383
1384         if (wlandev->tx_timeout) {
1385                 wlandev->tx_timeout(wlandev);
1386         } else {
1387                 WLAN_LOG_WARNING("Implement tx_timeout for %s\n",
1388                                  wlandev->nsdname);
1389                 p80211netdev_wake_queue(wlandev);
1390         }
1391
1392         DBFEXIT;
1393 }