Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / dev / atm / hfa / fore_load.c
1 /*
2  *
3  * ===================================
4  * HARP  |  Host ATM Research Platform
5  * ===================================
6  *
7  *
8  * This Host ATM Research Platform ("HARP") file (the "Software") is
9  * made available by Network Computing Services, Inc. ("NetworkCS")
10  * "AS IS".  NetworkCS does not provide maintenance, improvements or
11  * support of any kind.
12  *
13  * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
14  * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
15  * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
16  * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
17  * In no event shall NetworkCS be responsible for any damages, including
18  * but not limited to consequential damages, arising from or relating to
19  * any use of the Software or related support.
20  *
21  * Copyright 1994-1998 Network Computing Services, Inc.
22  *
23  * Copies of this Software may be made, however, the above copyright
24  * notice must be reproduced on all copies.
25  *
26  *      @(#) $FreeBSD: src/sys/dev/hfa/fore_load.c,v 1.13 1999/09/25 18:23:49 phk Exp $
27  *
28  */
29
30 /*
31  * FORE Systems 200-Series Adapter Support
32  * ---------------------------------------
33  *
34  * Loadable kernel module and device identification support
35  *
36  */
37
38 #include <dev/hfa/fore_include.h>
39
40 #ifndef lint
41 __RCSID("@(#) $FreeBSD: src/sys/dev/hfa/fore_load.c,v 1.13 1999/09/25 18:23:49 phk Exp $");
42 #endif
43
44
45 /*
46  * Local functions
47  */
48 static int      fore_start __P((void));
49 #ifdef sun
50 static int      fore_stop __P((void));
51 static int      fore_doload __P((void));
52 static int      fore_dounload __P((void));
53 static int      fore_identify __P((char *));
54 static int      fore_attach __P((struct devinfo *));
55 #endif
56 #ifdef __FreeBSD__
57 static const char *     fore_pci_probe __P((pcici_t, pcidi_t));
58 static void     fore_pci_attach __P((pcici_t, int));
59 #if BSD < 199506
60 static int      fore_pci_shutdown __P((struct kern_devconf *, int));
61 #else
62 static void     fore_pci_shutdown __P((void *, int));
63 #endif
64 #endif
65 static void     fore_unattach __P((Fore_unit *));
66 static void     fore_reset __P((Fore_unit *));
67
68
69 /*
70  * Local variables
71  */
72 static int      fore_inited = 0;
73
74 /*
75  * Driver entry points
76  */
77 #ifdef sun
78 static struct dev_ops   fore_ops = {
79         1,              /* revision */
80         fore_identify,  /* identify */
81         fore_attach,    /* attach */
82         NULL,           /* open */
83         NULL,           /* close */
84         NULL,           /* read */
85         NULL,           /* write */
86         NULL,           /* strategy */
87         NULL,           /* dump */
88         NULL,           /* psize */
89         NULL,           /* ioctl */
90         NULL,           /* reset */
91         NULL            /* mmap */
92 };
93 #endif
94
95 #ifdef __FreeBSD__
96 static  u_long  fore_pci_count = 0;
97
98 static struct pci_device fore_pci_device = {
99         FORE_DEV_NAME,
100         fore_pci_probe,
101         fore_pci_attach,
102         &fore_pci_count,
103 #if BSD < 199506
104         fore_pci_shutdown
105 #else
106         NULL
107 #endif
108 };
109
110 COMPAT_PCI_DRIVER(fore_pci, fore_pci_device);
111 #endif
112
113
114 /*
115  * Initialize driver processing
116  * 
117  * This will be called during module loading.  Not much to do here, as
118  * we must wait for our identify/attach routines to get called before
119  * we know what we're in for.
120  *
121  * Arguments:
122  *      none
123  *
124  * Returns:
125  *      0       startup was successful 
126  *      errno   startup failed - reason indicated
127  *
128  */
129 static int
130 fore_start()
131 {
132
133         /*
134          * Verify software version
135          */
136         if (atm_version != ATM_VERSION) {
137                 log(LOG_ERR, "version mismatch: fore=%d.%d kernel=%d.%d\n",
138                         ATM_VERS_MAJ(ATM_VERSION), ATM_VERS_MIN(ATM_VERSION),
139                         ATM_VERS_MAJ(atm_version), ATM_VERS_MIN(atm_version));
140                 return (EINVAL);
141         }
142
143         /*
144          * Initialize DMA mapping
145          */
146         DMA_INIT();
147
148         /*
149          * Start up watchdog timer
150          */
151         atm_timeout(&fore_timer, ATM_HZ * FORE_TIME_TICK, fore_timeout);
152
153         fore_inited = 1;
154
155         return (0);
156 }
157
158
159 #ifdef sun
160
161 /*
162  * Halt driver processing 
163  * 
164  * This will be called just prior to unloading the module from memory.
165  * Everything we've setup since we've been loaded must be undone here.
166  *
167  * Arguments:
168  *      none
169  *
170  * Returns:
171  *      0       shutdown was successful 
172  *      errno   shutdown failed - reason indicated
173  *
174  */
175 static int
176 fore_stop()
177 {
178         int     err = 0;
179         int     s = splimp();
180         int     i;
181
182         /*
183          * Stop the watchdog timer
184          */
185         (void) atm_untimeout(&fore_timer);
186
187         /*
188          * Clean up each device (if any)
189          */
190         for ( i = 0; i < fore_nunits; i++ ) {
191                 Fore_unit       *fup = fore_units[i];
192
193                 if (fup == NULL)
194                         continue;
195
196                 /*
197                  * Deregister device from kernel services
198                  */
199                 if (err = atm_physif_deregister((Cmn_unit *)fup)) {
200                         (void) splx(s);
201                         return (err);
202                 }
203
204                 /*
205                  * Unattach the device from the system
206                  */
207                 fore_unattach(fup);
208
209                 /*
210                  * Free any Fore-specific device resources
211                  */
212                 fore_interface_free(fup);
213
214                 /*
215                  * Free the unit structure
216                  */
217                 atm_dev_free(fup);
218                 fore_units[i] = NULL;
219         }
220
221         fore_nunits = 0;
222
223         /*
224          * Now free our global resources
225          */
226
227         /*
228          * Release our storage pools
229          */
230         atm_release_pool(&fore_vcc_pool);
231         atm_release_pool(&fore_nif_pool);
232
233         /*
234          * Release all DMA mappings
235          */
236         DMA_RELEASE();
237
238         fore_inited = 0;
239
240         (void) splx(s);
241
242         return (0);
243 }
244
245 /*
246  * Device identify routine
247  * 
248  * Determine if this driver will support the named device.  If we claim to
249  * support the device, our attach routine will (later) be called for the
250  * device.
251  *
252  * Arguments:
253  *      name    pointer to identifier string from device
254  *
255  * Returns:
256  *      1       driver claims support for this device
257  *      0       device not claimed by this driver
258  *
259  */
260 static int
261 fore_identify(name)
262         char    *name;
263 {
264         int     ret = 0;
265         int     i = 0;
266
267         /*
268          * Initialize driver stuff
269          */
270         if (fore_inited == 0) {
271                 if (fore_start())
272                         return (0);
273         }
274
275         while (fore_devices[i].fd_name) {
276                 if (strcmp(fore_devices[i].fd_name, name) == 0) {
277
278                         /*
279                          * We support this device!!
280                          */
281                         if (fore_nunits < FORE_MAX_UNITS) {
282                                 fore_nunits++;
283                                 ret = 1;
284                         } else {
285                                 log(LOG_ERR,
286                                         "fore_identify: Too many devices\n");
287                         }
288                         break;
289                 }
290                 i++;
291         }
292         return (ret);
293 }
294
295
296 /*
297  * Device attach routine
298  * 
299  * Attach a device we've previously claimed to support.  Walk through its
300  * register set and map, as required.  Determine what level the device will
301  * be interrupting at and then register an interrupt handler for it.  If we
302  * succeed, then reset the adapter and read useful info from its PROM.
303  * Last, register the interface with the kernel ATM services.
304  *
305  * Arguments:
306  *      devinfo_p       pointer to device information structure
307  *
308  * Returns:
309  *      0       attach was successful
310  *      -1      attach failed
311  *
312  */
313 static int
314 fore_attach(devinfo_p)
315         struct dev_info *devinfo_p;
316 {
317         struct dev_reg  *dev_reg_p;
318         struct dev_intr *dev_intr_p;
319         Fore_unit       *fup;
320         Atm_config      *fcp;
321         addr_t          valp;
322         int             val;
323         int             i;
324         int             err_count = BOOT_LOOPS;
325         static int      unit = 0;
326
327         /*
328          * Sanity check
329          */
330         if (devinfo_p == NULL)
331                 return (-1);
332
333         /*
334          * Make sure this isn't a duplicate unit
335          */
336         if (fore_units[unit] != NULL)
337                 return (-1);
338
339         /*
340          * Allocate a new unit structure
341          */
342         fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0);
343         if (fup == NULL)
344                 return (-1);
345
346         /*
347          * Start initializing it
348          */
349         fup->fu_unit = unit;
350         fup->fu_mtu = FORE_IFF_MTU;
351         fup->fu_devinfo = devinfo_p;
352         fup->fu_vcc_pool = &fore_vcc_pool;
353         fup->fu_nif_pool = &fore_nif_pool;
354         fup->fu_ioctl = fore_atm_ioctl;
355         fup->fu_instvcc = fore_instvcc;
356         fup->fu_openvcc = fore_openvcc;
357         fup->fu_closevcc = fore_closevcc;
358         fup->fu_output = fore_output;
359
360         /*
361          * Consider this unit assigned
362          */
363         fore_units[unit] = fup;
364         unit++;
365
366         ATM_DEBUG1("fore_attach: fup=%p\n", fup);
367         ATM_DEBUG2("\tfu_xmit_q=%p fu_xmit_head=%p\n",
368                 fup->fu_xmit_q, &fup->fu_xmit_head);
369         ATM_DEBUG2("\tfu_recv_q=%p fu_recv_head=%p\n",
370                 fup->fu_recv_q, &fup->fu_recv_head);
371         ATM_DEBUG2("\tfu_buf1s_q=%p fu_buf1s_head=%p\n",
372                 fup->fu_buf1s_q, &fup->fu_buf1s_head);
373         ATM_DEBUG2("\tfu_buf1l_q=%p fu_buf1l_head=%p\n",
374                 fup->fu_buf1l_q, &fup->fu_buf1l_head);
375         ATM_DEBUG2("\tfu_cmd_q=%p fu_cmd_head=%p\n",
376                 fup->fu_cmd_q, &fup->fu_cmd_head);
377         ATM_DEBUG1("\tfu_stats=%p\n",
378                 &fup->fu_stats);
379
380         /*
381          * Tell kernel our unit number
382          */
383         devinfo_p->devi_unit = fup->fu_unit;
384
385         /*
386          * Figure out what type of device we've got.  This should always
387          * work since we've already done this at identify time!
388          */
389         i = 0;
390         while (fore_devices[i].fd_name) {
391                 if (strcmp(fore_devices[i].fd_name, devinfo_p->devi_name) == 0)
392                         break;
393                 i++;
394         }
395         if (fore_devices[i].fd_name == NULL)
396                 return (-1);
397
398         fup->fu_config.ac_device = fore_devices[i].fd_devtyp;
399
400         /*
401          * Walk through the OPENPROM register information
402          * mapping register banks as they are found.
403          */
404         for ( dev_reg_p = devinfo_p->devi_reg, i = 1;
405                 i <= devinfo_p->devi_nreg; i++, ++dev_reg_p )
406         {
407                 if ( dev_reg_p == NULL )
408                 {
409                         /*
410                          * Can't happen...
411                          */
412                         return ( -1 );
413                 }
414
415                 /*
416                  * Each device type has different register sets
417                  */
418                 switch (fup->fu_config.ac_device) {
419
420 #ifdef FORE_SBUS
421                 case DEV_FORE_SBA200E:
422
423                         switch ( i )
424                         {
425                         /*
426                          * Host Control Register (HCR)
427                          */
428                         case 1:
429                                 if ( sizeof(Fore_reg) != dev_reg_p->reg_size )
430                                 {
431                                         return ( -1 );
432                                 }
433                                 fup->fu_ctlreg = (Fore_reg *)
434                                         map_regs ( dev_reg_p->reg_addr,
435                                                 sizeof(Fore_reg),
436                                                 dev_reg_p->reg_bustype );
437                                 if ( fup->fu_ctlreg == NULL )
438                                 {
439                                         return ( -1 );
440                                 }
441                                 break;
442
443                         /*
444                          * SBus Burst Transfer Configuration Register
445                          */
446                         case 2:
447                                 /*
448                                  * Not used
449                                  */
450                                 break;
451
452                         /*
453                          * SBus Interrupt Level Select Register
454                          */
455                         case 3:
456                                 if ( sizeof (Fore_reg) != dev_reg_p->reg_size )
457                                 {
458                                         return ( -1 );
459                                 }
460                                 fup->fu_intlvl = (Fore_reg *)
461                                         map_regs ( dev_reg_p->reg_addr,
462                                                 sizeof(Fore_reg),
463                                                 dev_reg_p->reg_bustype );
464                                 if ( fup->fu_intlvl == NULL )
465                                 {
466                                         return ( -1 );
467                                 }
468                                 break;
469
470                         /*
471                          * i960 RAM
472                          */
473                         case 4:
474                                 fup->fu_ram = (Fore_mem *)
475                                         map_regs ( dev_reg_p->reg_addr,
476                                                 dev_reg_p->reg_size,
477                                                 dev_reg_p->reg_bustype );
478                                 if ( fup->fu_ram == NULL )
479                                 {
480                                         return ( -1 );
481                                 }
482                                 fup->fu_ramsize = dev_reg_p->reg_size;
483
484                                 /*
485                                  * Various versions of the Sun PROM mess with 
486                                  * the reg_addr value in unpredictable (to me,
487                                  * at least) ways, so just use the "memoffset"
488                                  * property, which should give us the RAM 
489                                  * offset directly.
490                                  */
491                                 val = getprop(devinfo_p->devi_nodeid, 
492                                                         "memoffset", -1);
493                                 if (val == -1) {
494                                         return (-1);
495                                 }
496                                 fup->fu_config.ac_ram = val;
497                                 fup->fu_config.ac_ramsize = fup->fu_ramsize;
498
499                                 /*
500                                  * Set monitor interface for initializing
501                                  */
502                                 fup->fu_mon = (Mon960 *)
503                                         (fup->fu_ram + MON960_BASE);
504                                 break;
505
506                         default:
507                                 log(LOG_ERR, 
508                                         "fore_attach: Too many registers\n");
509                                 return ( -1 );
510                         }
511                         break;
512
513                 case DEV_FORE_SBA200:
514
515                         switch ( i )
516                         {
517                         /*
518                          * Board Control Register (BCR)
519                          */
520                         case 1:
521                                 if ( sizeof(Fore_reg) != dev_reg_p->reg_size )
522                                 {
523                                         return ( -1 );
524                                 }
525                                 fup->fu_ctlreg = (Fore_reg *)
526                                         map_regs ( dev_reg_p->reg_addr,
527                                                 sizeof(Fore_reg),
528                                                 dev_reg_p->reg_bustype );
529                                 if ( fup->fu_ctlreg == NULL )
530                                 {
531                                         return ( -1 );
532                                 }
533                                 break;
534
535                         /*
536                          * i960 RAM
537                          */
538                         case 2:
539                                 fup->fu_ram = (Fore_mem *)
540                                         map_regs ( dev_reg_p->reg_addr,
541                                                 dev_reg_p->reg_size,
542                                                 dev_reg_p->reg_bustype );
543                                 if ( fup->fu_ram == NULL )
544                                 {
545                                         return ( -1 );
546                                 }
547                                 fup->fu_ramsize = dev_reg_p->reg_size;
548
549                                 /*
550                                  * Various versions of the Sun PROM mess with 
551                                  * the reg_addr value in unpredictable (to me,
552                                  * at least) ways, so just use the "memoffset"
553                                  * property, which should give us the RAM 
554                                  * offset directly.
555                                  */
556                                 val = getprop(devinfo_p->devi_nodeid, 
557                                                         "memoffset", -1);
558                                 if (val == -1) {
559                                         return (-1);
560                                 }
561                                 fup->fu_config.ac_ram = val;
562                                 fup->fu_config.ac_ramsize = fup->fu_ramsize;
563
564                                 /*
565                                  * Set monitor interface for initializing
566                                  */
567                                 fup->fu_mon = (Mon960 *)
568                                         (fup->fu_ram + MON960_BASE);
569                                 break;
570
571                         default:
572                                 log(LOG_ERR, 
573                                         "fore_attach: Too many registers\n");
574                                 return ( -1 );
575                         }
576                         break;
577 #endif  /* FORE_SBUS */
578
579                 default:
580                         log(LOG_ERR, 
581                                 "fore_attach: Unsupported device type %d\n",
582                                 fup->fu_config.ac_device);
583                         return (-1);
584                 }
585         }
586
587         /*
588          * Install the device in the interrupt chain.
589          *
590          * dev_intr_p may be null IFF devi_nintr is zero.
591          */
592         dev_intr_p = devinfo_p->devi_intr;
593         for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p )
594         {
595
596                 if ( dev_intr_p == NULL )
597                 {
598                         /*
599                          * Can't happen.
600                          */
601                         return ( -1 );
602                 }
603
604                 /*
605                  * Convert hardware ipl (0-15) into spl level.
606                  */
607                 if ( ipltospl ( dev_intr_p->int_pri ) > fup->fu_intrpri )
608                 {
609                         fup->fu_intrpri = ipltospl ( dev_intr_p->int_pri );
610
611                         /*
612                          * If SBA-200E card, set SBus interrupt level
613                          * into board register
614                          */
615                         if ( fup->fu_intlvl ) {
616 #if defined(sun4c)
617                                 *(fup->fu_intlvl) = dev_intr_p->int_pri;
618 #elif defined(sun4m)
619                                 extern int      svimap[];
620
621                                 *(fup->fu_intlvl) = 
622                                         svimap[dev_intr_p->int_pri & 0xf];
623 #else
624                                 #error PORT ME;
625 #endif
626                         }
627                 }
628
629                 DEVICE_LOCK((Cmn_unit *)fup);
630
631                 /*
632                  * Register our interrupt routine.
633                  */
634                 (void) addintr ( dev_intr_p->int_pri, fore_poll,
635                     devinfo_p->devi_name, devinfo_p->devi_unit );
636
637                 /*
638                  * If we can do DMA (we can), then DVMA routines need
639                  * to know the highest IPL level we will interrupt at.
640                  */
641                 adddma ( dev_intr_p->int_pri );
642
643                 DEVICE_UNLOCK((Cmn_unit *)fup);
644         }
645
646         /*
647          * Poke the hardware...boot the CP and prepare it for downloading
648          */
649         fore_reset(fup);
650
651         switch (fup->fu_config.ac_device) {
652
653 #ifdef FORE_SBUS
654         case DEV_FORE_SBA200E:
655                 /*
656                  * Enable interrupts
657                  */
658                 SBA200E_HCR_SET(*fup->fu_ctlreg, SBA200E_SBUS_ENA);
659                 break;
660 #endif  /* FORE_SBUS */
661         }
662
663         /*
664          * Wait for monitor to perform self-test
665          */
666         while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) {
667                 if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) {
668                         log(LOG_ERR, "fore_attach: Unit %d failed self-test\n",
669                                 fup->fu_unit);
670                         return (-1);
671
672                 } else if ( --err_count == 0 ) {
673                         log(LOG_ERR, "fore_attach: Unit %d unable to boot\n",
674                                 fup->fu_unit);
675                         return (-1);
676                 }
677                 DELAY ( BOOT_DELAY );
678         }
679
680         /*
681          * Write a one line message to the console informing
682          * that we've attached the device.
683          */
684         report_dev ( devinfo_p );
685
686         /*
687          * Get the mac address from the card PROM
688          */
689         val = getprop ( devinfo_p->devi_nodeid, "macaddress1", -1 );
690         if ( val != -1 ) {
691                 fup->fu_pif.pif_macaddr.ma_data[0] = val & 0xff;
692                 val = getprop ( devinfo_p->devi_nodeid, "macaddress2", -1 );
693                 fup->fu_pif.pif_macaddr.ma_data[1] = val & 0xff;
694                 val = getprop ( devinfo_p->devi_nodeid, "macaddress3", -1 );
695                 fup->fu_pif.pif_macaddr.ma_data[2] = val & 0xff;
696                 val = getprop ( devinfo_p->devi_nodeid, "macaddress4", -1 );
697                 fup->fu_pif.pif_macaddr.ma_data[3] = val & 0xff;
698                 val = getprop ( devinfo_p->devi_nodeid, "macaddress5", -1 );
699                 fup->fu_pif.pif_macaddr.ma_data[4] = val & 0xff;
700                 val = getprop ( devinfo_p->devi_nodeid, "macaddress6", -1 );
701                 fup->fu_pif.pif_macaddr.ma_data[5] = val & 0xff;
702         } else {
703                 /*
704                  * Newer PROM - mac addresses have been combined. Also,
705                  * macaddrlo2 reflects the board serial number.
706                  */
707                 val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrlo2", -1));
708                 KM_COPY ( (caddr_t)&val, 
709                         (caddr_t)&fup->fu_pif.pif_macaddr.ma_data[2],
710                         sizeof(val) );
711                 val = htonl(getprop(devinfo_p->devi_nodeid, "macaddrhi4", -1));
712                 KM_COPY ( (caddr_t)&val,
713                         (caddr_t)fup->fu_pif.pif_macaddr.ma_data,
714                         sizeof(val) );
715         }
716
717         /*
718          * Setup the adapter config info
719          */
720         fcp = &fup->fu_config;
721         fcp->ac_vendor = VENDOR_FORE;
722         fcp->ac_vendapi = VENDAPI_FORE_1;
723         fcp->ac_macaddr = fup->fu_pif.pif_macaddr;
724         val = getprop ( devinfo_p->devi_nodeid, "promversion", -1 );
725         if ( val == -1 ) {
726                 val = getprop ( devinfo_p->devi_nodeid, "hw-version", -1 );
727         }
728         if (val != -1) {
729                 snprintf(fcp->ac_hard_vers,
730                     sizeof(fcp->ac_hard_vers), "%d.%d.%d",
731                         (val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff);
732         } else
733                 snprintf(fcp->ac_hard_vers,
734                     sizeof(fcp->ac_hard_vers), "Unknown");
735
736         val = getprop ( devinfo_p->devi_nodeid, "serialnumber", -1 );
737         if ( val != -1 )
738                 fcp->ac_serial = val;
739
740         valp = (addr_t)getlongprop ( devinfo_p->devi_nodeid, "model" );
741         if ( valp )
742         {
743                 /*
744                  * Media Type
745                  */
746                 switch (fcp->ac_device) {
747
748 #ifdef FORE_SBUS
749                 case DEV_FORE_SBA200E:
750                         fcp->ac_media = MEDIA_OC3C;
751                         fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
752                         break;
753
754                 case DEV_FORE_SBA200:
755                         /*
756                          * Look at the /SSS trailer to determine 4B5B speed
757                          *      TAXI-100 = 125; TAXI-140 = 175
758                          * Assume that OC3 has no /SSS speed identifier.
759                          */
760                         while (*valp && *valp != '/')
761                                 valp++;
762                         if (*valp == NULL) {
763                                 fcp->ac_media = MEDIA_OC3C;
764                                 fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
765                         } else if (strcmp(valp, "/125") == 0) {
766                                 fcp->ac_media = MEDIA_TAXI_100;
767                                 fup->fu_pif.pif_pcr = ATM_PCR_TAXI100;
768                         } else {
769                                 fcp->ac_media = MEDIA_TAXI_140;
770                                 fup->fu_pif.pif_pcr = ATM_PCR_TAXI140;
771                         }
772                         break;
773 #endif  /* FORE_SBUS */
774                 }
775
776                 /*
777                  * Free property space
778                  */
779                 KM_FREE(valp, getproplen(devinfo_p->devi_nodeid, "model"), 0);
780         }
781
782         /*
783          * Bus information
784          */
785         fcp->ac_busslot = 
786 #ifdef SBUS_SIZE
787                 (long)(devinfo_p->devi_reg->reg_addr - SBUS_BASE) / SBUS_SIZE;
788 #else
789                 sbusslot((u_long)devinfo_p->devi_reg->reg_addr);
790 #endif
791
792         val = getprop(devinfo_p->devi_parent->devi_nodeid, "burst-sizes", 0);
793         if (val & SBUS_BURST32)
794                 fcp->ac_bustype = BUS_SBUS_B32;
795         else
796                 fcp->ac_bustype = BUS_SBUS_B16;
797
798         /*
799          * Set device capabilities
800          */
801         fup->fu_pif.pif_maxvpi = FORE_MAX_VPI;
802         fup->fu_pif.pif_maxvci = FORE_MAX_VCI;
803
804         /*
805          * Register this interface with ATM core services
806          */
807         if ( atm_physif_register
808                         ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 )
809         {
810                 /*
811                  * Registration failed - back everything out
812                  */
813                 /*
814                  * Modload calls UNLOAD if it get's a failure - don't
815                  * call fore_unload() here.
816                  */
817                 return ( -1 );
818         }
819
820         /*
821          * Initialize the CP microcode program.
822          */
823         fore_initialize(fup);
824
825         return (0);
826 }
827 #endif  /* sun */
828
829
830 #ifdef __FreeBSD__
831 /*
832  * Device probe routine
833  * 
834  * Determine if this driver will support the identified device.  If we claim
835  * to support the device, our attach routine will (later) be called for the
836  * device.
837  *
838  * Arguments:
839  *      config_id       device's PCI configuration ID
840  *      device_id       device's PCI Vendor/Device ID
841  *
842  * Returns:
843  *      name    device identification string
844  *      NULL    device not claimed by this driver
845  *
846  */
847 static const char *
848 fore_pci_probe(config_id, device_id)
849         pcici_t config_id;
850         pcidi_t device_id;
851 {
852
853         /*
854          * Initialize driver stuff
855          */
856         if (fore_inited == 0) {
857                 if (fore_start())
858                         return (NULL);
859         }
860
861         if ((device_id & 0xffff) != FORE_VENDOR_ID)
862                 return (NULL);
863
864         if (((device_id >> 16) & 0xffff) == FORE_PCA200E_ID)
865                 return ("FORE Systems PCA-200E ATM");
866
867         return (NULL);
868 }
869
870
871 /*
872  * Device attach routine
873  * 
874  * Attach a device we've previously claimed to support.  Walk through its
875  * register set and map, as required.  Determine what level the device will
876  * be interrupting at and then register an interrupt handler for it.  If we
877  * succeed, then reset the adapter and initialize the microcode.
878  * Last, register the interface with the kernel ATM services.
879  *
880  * Arguments:
881  *      config_id       device's PCI configuration ID
882  *      unit            device unit number
883  *
884  * Returns:
885  *      none
886  *
887  */
888 static void
889 fore_pci_attach(config_id, unit)
890         pcici_t config_id;
891         int     unit;
892 {
893         Fore_unit       *fup;
894         vm_offset_t     va;
895         vm_offset_t     pa;
896         pcidi_t         device_id;
897         long            val;
898         int             err_count = BOOT_LOOPS;
899
900         /*
901          * Just checking...
902          */
903         if (unit >= FORE_MAX_UNITS) {
904                 log(LOG_ERR, "%s%d: too many devices\n", 
905                         FORE_DEV_NAME, unit);
906                 return;
907         }
908
909         /*
910          * Make sure this isn't a duplicate unit
911          */
912         if (fore_units[unit] != NULL)
913                 return;
914
915         /*
916          * Allocate a new unit structure
917          */
918         fup = (Fore_unit *) atm_dev_alloc(sizeof(Fore_unit), sizeof(int), 0);
919         if (fup == NULL)
920                 return;
921
922         /*
923          * Start initializing it
924          */
925         fup->fu_unit = unit;
926         fup->fu_mtu = FORE_IFF_MTU;
927         fup->fu_pcitag = config_id;
928         fup->fu_vcc_pool = &fore_vcc_pool;
929         fup->fu_nif_pool = &fore_nif_pool;
930         fup->fu_ioctl = fore_atm_ioctl;
931         fup->fu_instvcc = fore_instvcc;
932         fup->fu_openvcc = fore_openvcc;
933         fup->fu_closevcc = fore_closevcc;
934         fup->fu_output = fore_output;
935         callout_handle_init(&fup->fu_thandle);
936
937         /*
938          * Get our device type
939          */
940         device_id = pci_conf_read ( config_id, PCI_ID_REG );
941         switch ((device_id >> 16) & 0xffff) {
942
943         case FORE_PCA200E_ID:
944                 fup->fu_config.ac_device = DEV_FORE_PCA200E;
945                 break;
946
947         default:
948                 fup->fu_config.ac_device = DEV_UNKNOWN;
949         }
950
951         /*
952          * Enable Memory Mapping / Bus Mastering 
953          */
954         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
955         val |= (PCIM_CMD_MEMEN | PCIM_CMD_BUSMASTEREN);
956         pci_conf_write(config_id, PCI_COMMAND_STATUS_REG, val);
957
958         /*
959          * Map RAM
960          */
961         val = pci_conf_read(config_id, PCI_COMMAND_STATUS_REG);
962         if ((val & PCIM_CMD_MEMEN) == 0) {
963                 log(LOG_ERR, "%s%d: memory mapping not enabled\n", 
964                         FORE_DEV_NAME, unit);
965                 goto failed;
966         }
967         if ((pci_map_mem(config_id, PCA200E_PCI_MEMBASE, &va, &pa)) == 0) {
968                 log(LOG_ERR, "%s%d: unable to map memory\n", 
969                         FORE_DEV_NAME, unit);
970                 goto failed;
971         }
972         fup->fu_ram = (Fore_mem *)va;
973         fup->fu_ramsize = PCA200E_RAM_SIZE;
974         fup->fu_mon = (Mon960 *)(fup->fu_ram + MON960_BASE);
975         fup->fu_ctlreg = (Fore_reg *)(va + PCA200E_HCR_OFFSET);
976         fup->fu_imask = (Fore_reg *)(va + PCA200E_IMASK_OFFSET);
977         fup->fu_psr = (Fore_reg *)(va + PCA200E_PSR_OFFSET);
978
979         /*
980          * Convert Endianess of Slave RAM accesses
981          */
982         val = pci_conf_read(config_id, PCA200E_PCI_MCTL);
983         val |= PCA200E_MCTL_SWAP;
984         pci_conf_write(config_id, PCA200E_PCI_MCTL, val);
985
986         /*
987          * Map interrupt in
988          */
989         if ( !pci_map_int( config_id, fore_intr, fup, &net_imask ) ) {
990                 log(LOG_ERR, "%s%d: unable to map interrupt\n", 
991                         FORE_DEV_NAME, unit);
992                 goto failed;
993         }
994
995         /*
996          * Poke the hardware - boot the CP and prepare it for downloading
997          */
998         fore_reset(fup);
999
1000         /*
1001          * Wait for the monitor to perform self-test
1002          */
1003         while (CP_READ(fup->fu_mon->mon_bstat) != BOOT_MONREADY) {
1004                 if (CP_READ(fup->fu_mon->mon_bstat) == BOOT_FAILTEST) {
1005                         log(LOG_ERR, "%s%d: failed self-test\n", 
1006                                 FORE_DEV_NAME, unit);
1007                         goto failed;
1008                 } else if ( --err_count == 0 ) {
1009                         log(LOG_ERR, "%s%d: unable to boot - status=0x%lx\n", 
1010                                 FORE_DEV_NAME, unit,
1011                                 (u_long)CP_READ(fup->fu_mon->mon_bstat));
1012                         goto failed;
1013                 }
1014                 DELAY ( BOOT_DELAY );
1015         }
1016
1017         /*
1018          * Setup the adapter config info - at least as much as we can
1019          */
1020         fup->fu_config.ac_vendor = VENDOR_FORE;
1021         fup->fu_config.ac_vendapi = VENDAPI_FORE_1;
1022         fup->fu_config.ac_media = MEDIA_OC3C;
1023         fup->fu_pif.pif_pcr = ATM_PCR_OC3C;
1024         fup->fu_config.ac_bustype = BUS_PCI;
1025         fup->fu_config.ac_busslot = config_id->bus << 8 | config_id->slot;
1026
1027         /*
1028          * Save device ram info for user-level programs
1029          */
1030         fup->fu_config.ac_ram = (long)fup->fu_ram;
1031         fup->fu_config.ac_ramsize = fup->fu_ramsize;
1032
1033         /*
1034          * Set device capabilities
1035          */
1036         fup->fu_pif.pif_maxvpi = FORE_MAX_VPI;
1037         fup->fu_pif.pif_maxvci = FORE_MAX_VCI;
1038
1039         /*
1040          * Register this interface with ATM core services
1041          */
1042         if ( atm_physif_register
1043                         ((Cmn_unit *)fup, FORE_DEV_NAME, fore_services) != 0 )
1044         {
1045                 /*
1046                  * Registration failed - back everything out
1047                  */
1048                 goto failed;
1049         }
1050
1051         fore_units[unit] = fup;
1052         fore_nunits++;
1053
1054 #if BSD >= 199506
1055         /*
1056          * Add hook to our shutdown function
1057          */
1058         EVENTHANDLER_REGISTER(shutdown_post_sync, fore_pci_shutdown, fup,
1059                               SHUTDOWN_PRI_DEFAULT);
1060 #endif
1061
1062         /*
1063          * Initialize the CP microcode program.
1064          */
1065         fore_initialize(fup);
1066
1067         return;
1068
1069 failed:
1070         /*
1071          * Unattach the device from the system
1072          */
1073         fore_unattach(fup);
1074
1075         /*
1076          * Free any Fore-specific device resources
1077          */
1078         fore_interface_free(fup);
1079
1080         atm_dev_free(fup);
1081
1082         return;
1083 }
1084
1085
1086 #if BSD < 199506
1087 /*
1088  * Device shutdown routine
1089  * 
1090  * Arguments:
1091  *      kdc             pointer to device's configuration table
1092  *      force           forced shutdown flag
1093  *
1094  * Returns:
1095  *      none
1096  *
1097  */
1098 static int
1099 fore_pci_shutdown(kdc, force)
1100         struct kern_devconf     *kdc;
1101         int                     force;
1102 {
1103         Fore_unit       *fup;
1104
1105         if (kdc->kdc_unit < fore_nunits) {
1106
1107                 fup = fore_units[kdc->kdc_unit];
1108                 if (fup != NULL) {
1109                         fore_reset(fup);
1110                 }
1111         }
1112
1113         (void) dev_detach(kdc);
1114         return (0);
1115 }
1116 #else
1117 /*
1118  * Device shutdown routine
1119  * 
1120  * Arguments:
1121  *      howto           type of shutdown
1122  *      fup             pointer to device unit structure
1123  *
1124  * Returns:
1125  *      none
1126  *
1127  */
1128 static void
1129 fore_pci_shutdown(fup, howto)
1130         void            *fup;
1131         int             howto;
1132 {
1133
1134         fore_reset((Fore_unit *) fup);
1135
1136         return;
1137 }
1138 #endif  /* BSD < 199506 */
1139 #endif  /* __FreeBSD__ */
1140
1141
1142 /*
1143  * Device unattach routine
1144  * 
1145  * Reset the physical device, remove any pending timeouts, 
1146  * unmap any register sets, and unregister any interrupts.
1147  *
1148  * Arguments:
1149  *      fup             pointer to device unit structure
1150  *
1151  * Returns:
1152  *      none
1153  */ 
1154 static void
1155 fore_unattach(fup)
1156         Fore_unit       *fup;
1157 {
1158 #ifdef sun
1159         struct dev_info         *devinfo_p = fup->fu_devinfo;
1160         struct dev_reg          *dev_reg_p;
1161         struct dev_intr         *dev_intr_p;
1162         int                     i;
1163 #endif
1164
1165
1166         /*
1167          * Reset the board and return it to cold_start state.
1168          * Hopefully, this will prevent use of resources as
1169          * we're trying to free things up.
1170          */
1171         fore_reset(fup);
1172
1173         /*
1174          * Lock out all device interrupts
1175          */
1176         DEVICE_LOCK((Cmn_unit *)fup);
1177
1178         /*
1179          * Remove any pending timeout()'s
1180          */
1181         (void)untimeout((KTimeout_ret(*) __P((void *)))fore_initialize,
1182                 (void *)fup, fup->fu_thandle);
1183
1184 #ifdef sun
1185         /*
1186          * Remove any mappings of the device
1187          */
1188         for ( dev_reg_p = devinfo_p->devi_reg, i = 1;
1189                 i <= devinfo_p->devi_nreg; i++, ++dev_reg_p )
1190         {
1191                 if ( dev_reg_p == NULL )
1192                 {
1193                         /*
1194                          * Can't happen...
1195                          */
1196                         break;
1197                 }
1198
1199                 /*
1200                  * Each device type has different register sets
1201                  */
1202                 switch (fup->fu_config.ac_device) {
1203
1204 #ifdef FORE_SBUS
1205                 case DEV_FORE_SBA200E:
1206
1207                         switch ( i )
1208                         {
1209                         /*
1210                          * Host Control Register (HCR)
1211                          */
1212                         case 1:
1213                                 unmap_regs((addr_t)fup->fu_ctlreg,
1214                                         sizeof(Fore_reg));
1215                                 break;
1216
1217                         /*
1218                          * SBus Burst Transfer Configuration Register
1219                          */
1220                         case 2:
1221                                 /*
1222                                  * Not used
1223                                  */
1224                                 break;
1225
1226                         /*
1227                          * SBus Interrupt Level Select Register
1228                          */
1229                         case 3:
1230                                 unmap_regs((addr_t)fup->fu_intlvl,
1231                                         sizeof(Fore_reg));
1232                                 break;
1233
1234                         /*
1235                          * i960 RAM
1236                          */
1237                         case 4:
1238                                 unmap_regs((addr_t)fup->fu_ram,
1239                                         fup->fu_ramsize);
1240                                 break;
1241                         }
1242                         break;
1243
1244                 case DEV_FORE_SBA200:
1245
1246                         switch ( i )
1247                         {
1248                         /*
1249                          * Board Control Register (BCR)
1250                          */
1251                         case 1:
1252                                 unmap_regs((addr_t)fup->fu_ctlreg,
1253                                         sizeof(Fore_reg));
1254                                 break;
1255
1256                         /*
1257                          * i960 RAM
1258                          */
1259                         case 2:
1260                                 unmap_regs((addr_t)fup->fu_ram,
1261                                         fup->fu_ramsize);
1262                                 break;
1263                         }
1264                         break;
1265 #endif  /* FORE_SBUS */
1266                 }
1267         }
1268
1269         /*
1270          * Remove the interrupt vector(s)
1271          */
1272         dev_intr_p = devinfo_p->devi_intr;
1273         for ( i = devinfo_p->devi_nintr; i > 0; --i, ++dev_intr_p )
1274         {
1275                 if ( dev_intr_p == NULL )
1276                 {
1277                         /*
1278                          * Can't happen...
1279                          */
1280                         break;
1281                 }
1282                 (void) remintr ( dev_intr_p->int_pri, fore_poll );
1283         }
1284 #endif  /* sun */
1285
1286 #ifdef __FreeBSD__
1287         /*
1288          * Unmap the device interrupt
1289          */
1290         (void) pci_unmap_int(fup->fu_pcitag);
1291
1292         /*
1293          * Unmap memory
1294          */
1295 #ifdef notdef
1296         (void) pci_unmap_mem(fup->fu_pcitag, PCA200E_PCI_MEMBASE);
1297 #endif
1298 #endif  /* __FreeBSD__ */
1299
1300         DEVICE_UNLOCK((Cmn_unit *)fup);
1301 }
1302
1303
1304 /*
1305  * Device reset routine
1306  * 
1307  * Reset the physical device
1308  *
1309  * Arguments:
1310  *      fup             pointer to device unit structure
1311  *
1312  * Returns:
1313  *      none
1314  */ 
1315 static void
1316 fore_reset(fup)
1317         Fore_unit       *fup;
1318 {
1319         int     s = splimp();
1320
1321         /*
1322          * Reset the board and return it to cold_start state
1323          */
1324         if (fup->fu_mon)
1325                 fup->fu_mon->mon_bstat = CP_WRITE(BOOT_COLDSTART);
1326
1327         if (fup->fu_ctlreg) {
1328
1329                 switch (fup->fu_config.ac_device) {
1330
1331 #ifdef FORE_SBUS
1332                 case DEV_FORE_SBA200E:
1333                         /*
1334                          * Reset i960 by setting and clearing RESET
1335                          */
1336                         SBA200E_HCR_INIT(*fup->fu_ctlreg, SBA200E_RESET);
1337                         SBA200E_HCR_CLR(*fup->fu_ctlreg, SBA200E_RESET);
1338                         break;
1339
1340                 case DEV_FORE_SBA200:
1341                         /*
1342                          * Reset i960 by setting and clearing RESET
1343                          *
1344                          * SBA200 will NOT reset if bit is OR'd in!
1345                          */
1346                         *fup->fu_ctlreg = SBA200_RESET;
1347                         *fup->fu_ctlreg = SBA200_RESET_CLR;
1348                         break;
1349 #endif  /* FORE_SBUS */
1350 #ifdef FORE_PCI
1351                 case DEV_FORE_PCA200E:
1352                         /*
1353                          * Reset i960 by setting and clearing RESET
1354                          */
1355                         PCA200E_HCR_INIT(*fup->fu_ctlreg, PCA200E_RESET);
1356                         DELAY(10000);
1357                         PCA200E_HCR_CLR(*fup->fu_ctlreg, PCA200E_RESET);
1358                         break;
1359
1360 #endif
1361                 }
1362         }
1363
1364         (void) splx(s);
1365         return;
1366 }
1367
1368
1369 #ifndef ATM_LINKED
1370 /*
1371  *******************************************************************
1372  *
1373  * Loadable Module Support
1374  *
1375  *******************************************************************
1376  */
1377
1378 #ifdef sun
1379 /*
1380  * Generic module load processing
1381  * 
1382  * This function is called by an OS-specific function when this
1383  * module is being loaded.
1384  *
1385  * Arguments:
1386  *      none
1387  *
1388  * Returns:
1389  *      0       load was successful 
1390  *      errno   load failed - reason indicated
1391  *
1392  */
1393 static int
1394 fore_doload()
1395 {
1396         int     err = 0;
1397
1398         /*
1399          * Start us up
1400          */
1401         err = fore_start();
1402         if (err)
1403                 /* Problems, clean up */
1404                 (void)fore_stop();
1405
1406         return (err);
1407 }
1408
1409
1410 /*
1411  * Generic module unload processing
1412  * 
1413  * This function is called by an OS-specific function when this
1414  * module is being unloaded.
1415  *
1416  * Arguments:
1417  *      none
1418  *
1419  * Returns:
1420  *      0       unload was successful 
1421  *      errno   unload failed - reason indicated
1422  *
1423  */
1424 static int
1425 fore_dounload()
1426 {
1427         int     err = 0;
1428
1429         /*
1430          * OK, try to clean up our mess
1431          */
1432         err = fore_stop();
1433
1434         return (err);
1435 }
1436
1437
1438 /*
1439  * Loadable driver description
1440  */
1441 static struct vdldrv    fore_drv = {
1442         VDMAGIC_DRV,    /* Device Driver */
1443         "fore_mod",     /* name */
1444         &fore_ops,      /* dev_ops */
1445         NULL,           /* bdevsw */
1446         NULL,           /* cdevsw */
1447         0,              /* blockmajor */
1448         0               /* charmajor */
1449 };
1450
1451
1452 /*
1453  * Loadable module support entry point
1454  * 
1455  * This is the routine called by the vd driver for all loadable module
1456  * functions for this pseudo driver.  This routine name must be specified
1457  * on the modload(1) command.  This routine will be called whenever the
1458  * modload(1), modunload(1) or modstat(1) commands are issued for this
1459  * module.
1460  *
1461  * Arguments:
1462  *      cmd     vd command code
1463  *      vdp     pointer to vd driver's structure
1464  *      vdi     pointer to command-specific vdioctl_* structure
1465  *      vds     pointer to status structure (VDSTAT only)
1466  *
1467  * Returns:
1468  *      0       command was successful 
1469  *      errno   command failed - reason indicated
1470  *
1471  */
1472 int
1473 fore_mod(cmd, vdp, vdi, vds)
1474         int             cmd;
1475         struct vddrv    *vdp;
1476         caddr_t         vdi;
1477         struct vdstat   *vds;
1478 {
1479         int     err = 0;
1480
1481         switch (cmd) {
1482
1483         case VDLOAD:
1484                 /*
1485                  * Module Load
1486                  *
1487                  * We dont support any user configuration
1488                  */
1489                 err = fore_doload();
1490                 if (err == 0)
1491                         /* Let vd driver know about us */
1492                         vdp->vdd_vdtab = (struct vdlinkage *)&fore_drv;
1493                 break;
1494
1495         case VDUNLOAD:
1496                 /*
1497                  * Module Unload
1498                  */
1499                 err = fore_dounload();
1500                 break;
1501
1502         case VDSTAT:
1503                 /*
1504                  * Module Status
1505                  */
1506
1507                 /* Not much to say at the moment */
1508
1509                 break;
1510
1511         default:
1512                 log(LOG_ERR, "fore_mod: Unknown vd command 0x%x\n", cmd);
1513                 err = EINVAL;
1514         }
1515
1516         return (err);
1517 }
1518 #endif  /* sun */
1519
1520 #ifdef __FreeBSD__
1521 #ifdef notdef
1522
1523 /*
1524  * Driver entry points
1525  */
1526 static struct cdevsw fore_cdev = {
1527         /* open */      noopen,
1528         /* close */     noclose,
1529         /* read */      noread,
1530         /* write */     nowrite,
1531         /* ioctl */     noioctl,
1532         /* poll */      nopoll,
1533         /* mmap */      nommap,
1534         /* strategy */  nostrategy,
1535         /* name */      noname,
1536         /* maj */       -1,
1537         /* dump */      nodump,
1538         /* psize */     nopsize,
1539         /* flags */     0,
1540         /* bmaj */      -1
1541 };
1542
1543
1544 /*
1545  * Loadable device driver module description
1546  */
1547 #if BSD < 199506
1548 MOD_DEV("fore_mod", LM_DT_CHAR, -1, (void *)&fore_cdev);
1549 #else
1550 MOD_DEV(fore, LM_DT_CHAR, -1, (void *)&fore_cdev);
1551 #endif
1552
1553
1554 /*
1555  * Loadable module support "load" entry point
1556  * 
1557  * This is the routine called by the lkm driver whenever the
1558  * modload(1) command is issued for this module.
1559  *
1560  * Arguments:
1561  *      lkmtp   pointer to lkm drivers's structure
1562  *      cmd     lkm command code
1563  *
1564  * Returns:
1565  *      0       command was successful 
1566  *      errno   command failed - reason indicated
1567  *
1568  */
1569 static int
1570 fore_load(lkmtp, cmd)
1571         struct lkm_table        *lkmtp;
1572         int             cmd;
1573 {
1574         return(fore_doload());
1575 }
1576
1577
1578 /*
1579  * Loadable module support "unload" entry point
1580  * 
1581  * This is the routine called by the lkm driver whenever the
1582  * modunload(1) command is issued for this module.
1583  *
1584  * Arguments:
1585  *      lkmtp   pointer to lkm drivers's structure
1586  *      cmd     lkm command code
1587  *
1588  * Returns:
1589  *      0       command was successful 
1590  *      errno   command failed - reason indicated
1591  *
1592  */
1593 static int
1594 fore_unload(lkmtp, cmd)
1595         struct lkm_table        *lkmtp;
1596         int             cmd;
1597 {
1598         return(fore_dounload());
1599 }
1600
1601
1602 /*
1603  * Loadable module support entry point
1604  * 
1605  * This is the routine called by the lkm driver for all loadable module
1606  * functions for this driver.  This routine name must be specified
1607  * on the modload(1) command.  This routine will be called whenever the
1608  * modload(1), modunload(1) or modstat(1) commands are issued for this
1609  * module.
1610  *
1611  * Arguments:
1612  *      lkmtp   pointer to lkm drivers's structure
1613  *      cmd     lkm command code
1614  *      ver     lkm version
1615  *
1616  * Returns:
1617  *      0       command was successful 
1618  *      errno   command failed - reason indicated
1619  *
1620  */
1621 int
1622 fore_mod(lkmtp, cmd, ver)
1623         struct lkm_table        *lkmtp;
1624         int             cmd;
1625         int             ver;
1626 {
1627 #if BSD < 199506
1628         DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, nosys);
1629 #else
1630         DISPATCH(lkmtp, cmd, ver, fore_load, fore_unload, lkm_nullcmd);
1631 #endif
1632 }
1633 #endif  /* notdef */
1634 #endif  /* __FreeBSD__ */
1635
1636 #endif  /* ATM_LINKED */
1637