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