vsbus.c revision 1.9 1 /* $NetBSD: vsbus.c,v 1.9 1998/01/24 14:16:11 ragge Exp $ */
2 /*
3 * Copyright (c) 1996 Ludd, University of Lule}, Sweden.
4 * All rights reserved.
5 *
6 * This code is derived from software contributed to Ludd by Bertram Barth.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed at Ludd, University of
19 * Lule}, Sweden and its contributors.
20 * 4. The name of the author may not be used to endorse or promote products
21 * derived from this software without specific prior written permission
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/buf.h>
38 #include <sys/conf.h>
39 #include <sys/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/proc.h>
42 #include <sys/user.h>
43 #include <sys/map.h>
44 #include <sys/device.h>
45 #include <sys/dkstat.h>
46 #include <sys/disklabel.h>
47 #include <sys/syslog.h>
48 #include <sys/stat.h>
49
50 #include <machine/pte.h>
51 #include <machine/sid.h>
52 #include <machine/scb.h>
53 #include <machine/cpu.h>
54 #include <machine/trap.h>
55 #include <machine/nexus.h>
56
57 #include <machine/uvax.h>
58 #include <machine/ka410.h>
59 #include <machine/ka43.h>
60
61 #include <machine/vsbus.h>
62
63 #define trace(x)
64 #define debug(x)
65
66 int vsbus_match __P((struct device *, struct cfdata *, void *));
67 void vsbus_attach __P((struct device *, struct device *, void *));
68 int vsbus_print __P((void *, const char *));
69
70 void ka410_attach __P((struct device *, struct device *, void *));
71 void ka43_attach __P((struct device *, struct device *, void *));
72
73 struct cfattach vsbus_ca = {
74 sizeof(struct device), vsbus_match, vsbus_attach
75 };
76
77 /*
78 void vsbus_intr_register __P((struct confargs *ca, int (*)(void*), void*));
79 void vsbus_intr_unregister __P((struct confargs *));
80 */
81
82 void vsbus_intr_dispatch __P((int i));
83
84 #define VSBUS_MAXDEVS 8
85 #define VSBUS_MAXINTR 8
86
87 struct confargs *vsbus_devs = NULL;
88
89 #ifdef VAX410
90 struct confargs ka410_devs[] = {
91 /* name intslot intpri intvec intbit ioaddr */
92 { "dc", 7, 7, 0x2C0, (1<<7), KA410_SER_BASE,
93 6, 6, 0x2C4, (1<<6), 0x01, },
94 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA410_SER_BASE, },
95 { "le", 5, 5, 0x250, (1<<5), KA410_LAN_BASE,
96 KA410_NWA_BASE, 0x00, },
97 { "ncr", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE,
98 KA410_SCS_DADR, KA410_SCS_DCNT, KA410_SCS_DDIR,
99 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
100 { "hdc", 0, 0, 0x3FC, (1<<0), KA410_DKC_BASE,
101 0, 0, 0,
102 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, },
103 #if 0
104 { "dc (recv)", 7, 7, 0x2C0, (1<<7), KA410_SER_BASE, },
105 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA410_SER_BASE, },
106 { "hdc9224", 0, 0, 0x3FC, (1<<0), KA410_DKC_BASE, },
107 { "ncr5380", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE, },
108 { "am7990", 5, 5, 0x250, (1<<5), KA410_LAN_BASE, },
109 { "NETOPT", 4, 4, 0x254, (1<<4), KA410_LAN_BASE, },
110 #endif
111 { "" },
112 };
113
114 /*
115 * It would be better if we could use the (provided) system config
116 * information for each CPU instead of this.
117 */
118 struct confargs ka420_devs[] = {
119 { "le", 5, 5, 0x250, (1<<5), KA410_LAN_BASE,
120 KA410_NWA_BASE, 0x00, },
121 { "ncr", 1, 1, 0x3F8, (1<<1), KA410_SCS_BASE,
122 KA410_SCS_DADR, KA410_SCS_DCNT, KA410_SCS_DDIR,
123 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
124 { "ncr", 0, 0, 0x3FC, (1<<0), 0x200C0180,
125 0x200C01A0, 0x200C01C0, 0x200C01C4,
126 KA410_DMA_BASE, KA410_DMA_SIZE, 0x00, 0x07, },
127 { "" },
128 };
129 #endif
130
131 #ifdef VAX43
132 struct confargs ka43_devs[] = {
133 /* name intslot intpri intvec intbit ioaddr */
134 { "dc", 7, 7, 0x2C0, (1<<7), KA43_SER_BASE,
135 6, 6, 0x2C4, (1<<6), 0x01, },
136 { "dc (xmit)", 6, 6, 0x2C4, (1<<6), KA43_SER_BASE, },
137 { "le", 5, 5, 0x250, (1<<5), KA43_LAN_BASE,
138 KA43_NWA_BASE, 0x00, },
139 { "ncr", 1, 1, 0x3F8, (1<<1), KA43_SC1_BASE,
140 KA43_SC1_DADR, KA43_SC1_DCNT, KA43_SC1_DDIR,
141 KA43_DMA_BASE, KA43_DMA_SIZE, 0x01, 0x06, },
142 { "ncr", 0, 0, 0x3FC, (1<<0), KA43_SC2_BASE,
143 KA43_SC2_DADR, KA43_SC2_DCNT, KA43_SC2_DDIR,
144 KA43_DMA_BASE, KA43_DMA_SIZE, 0x01, 0x06, },
145 #if 0
146 { "le (2nd)", 4, 4, 0x254, (1<<4), 0x???, },
147 { "NETOPT", 4, 4, 0x254, (1<<4), 0x???, },
148 #endif
149 { "" },
150 };
151 #endif
152
153 int
154 vsbus_print(aux, name)
155 void *aux;
156 const char *name;
157 {
158 struct confargs *ca = aux;
159
160 trace(("vsbus_print(%x, %s)\n", ca->ca_name, name));
161
162 if (name) {
163 printf ("device %s at %s", ca->ca_name, name);
164 return (UNSUPP);
165 }
166 return (UNCONF);
167 }
168
169 int
170 vsbus_match(parent, cf, aux)
171 struct device *parent;
172 struct cfdata *cf;
173 void *aux;
174 {
175 struct bp_conf *bp = aux;
176
177 trace(("vsbus_match: bp->type = \"%s\"\n", bp->type));
178
179 if (strcmp(bp->type, "vsbus"))
180 return 0;
181 /*
182 * on machines which can have it, the vsbus is always there
183 */
184 if ((vax_bustype & VAX_VSBUS) == 0)
185 return (0);
186
187 return (1);
188 }
189
190 #if 1 /*------------------------------------------------------------*/
191 #if 1
192 #define REG(name) short name; short X##name##X;
193 #else
194 #define REG(name) int name;
195 #endif
196 static volatile struct {/* base address of DZ-controller: 0x200A0000 */
197 REG(csr); /* 00 Csr: control/status register */
198 REG(rbuf); /* 04 Rbuf/Lpr: receive buffer/line param reg. */
199 REG(tcr); /* 08 Tcr: transmit console register */
200 REG(tdr); /* 0C Msr/Tdr: modem status reg/transmit data reg */
201 REG(lpr0); /* 10 Lpr0: */
202 REG(lpr1); /* 14 Lpr0: */
203 REG(lpr2); /* 18 Lpr0: */
204 REG(lpr3); /* 1C Lpr0: */
205 } *dz = (void*)0x200A0000;
206 extern int dzcnrint();
207 extern int dzcntint();
208 int hardclock_count = 0;
209 int
210 ka410_consintr_enable()
211 {
212 vsbus_intr_enable(&ka410_devs[0]);
213 vsbus_intr_enable(&ka410_devs[1]);
214 }
215
216 int
217 ka410_consRecv_intr(p)
218 void *p;
219 {
220 /* printf("ka410_consRecv_intr: hc-count=%d\n", hardclock_count); */
221 dzcnrint();
222 /* printf("gencnrint() returned.\n"); */
223 return(0);
224 }
225
226 int
227 ka410_consXmit_intr(p)
228 void *p;
229 {
230 /* printf("ka410_consXmit_intr: hc-count=%d\n", hardclock_count); */
231 dzcntint();
232 /* printf("gencntint() returned.\n"); */
233 return(0);
234 }
235 #endif /*------------------------------------------------------------*/
236
237 void
238 vsbus_attach(parent, self, aux)
239 struct device *parent, *self;
240 void *aux;
241 {
242 struct confargs *ca;
243 int i;
244
245 printf("\n");
246 trace (("vsbus_attach()\n"));
247
248 switch (vax_boardtype) {
249 case VAX_BTYP_420:
250 vsbus_devs = ka420_devs;
251 break;
252
253 case VAX_BTYP_410:
254 vsbus_devs = ka410_devs;
255 break;
256
257 case VAX_BTYP_43:
258 case VAX_BTYP_46:
259 case VAX_BTYP_49:
260 #ifdef VAX43
261 vsbus_devs = ka43_devs;
262 #endif
263 break;
264
265 default:
266 printf ("unsupported boardtype 0x%x in vsbus_attach()\n",
267 vax_boardtype);
268 return;
269 }
270
271 /*
272 * first setup interrupt-table, so that devices can register
273 * their interrupt-routines...
274 */
275 vsbus_intr_setup();
276
277 /*
278 * now check for all possible devices on this "bus"
279 */
280 for (i=0; i<VSBUS_MAXDEVS; i++) {
281 ca = &vsbus_devs[i];
282 if (*ca->ca_name == '\0')
283 break;
284 config_found(self, (void*)ca, vsbus_print);
285 }
286
287 /*
288 * as long as there's no working DZ-driver, we use this dummy
289 */
290 vsbus_intr_register(&ka410_devs[0], ka410_consRecv_intr, NULL);
291 vsbus_intr_register(&ka410_devs[1], ka410_consXmit_intr, NULL);
292 }
293
294 #define VSBUS_MAX_INTR 8 /* 64? */
295 /*
296 * interrupt service routines are given an int as argument, which is
297 * pushed onto stack as LITERAL. Thus the value is between 0-63.
298 * This array of 64 might be oversized for now, but it's all which
299 * ever will be possible.
300 */
301 struct vsbus_ivec {
302 struct ivec_dsp intr_vec; /* this is referenced in SCB */
303 int intr_count; /* keep track of interrupts */
304 int intr_flags; /* valid, etc. */
305 void (*enab)(int); /* enable interrupt */
306 void (*disab)(int); /* disable interrupt */
307 void (*prep)(int); /* need pre-processing? */
308 int (*handler)(void*); /* isr-routine to call */
309 void *hndlarg; /* args to this routine */
310 void (*postp)(int); /* need post-processing? */
311 } vsbus_ivtab[VSBUS_MAX_INTR];
312
313 /*
314 *
315 */
316 int
317 vsbus_intr_setup()
318 {
319 int i;
320 struct vsbus_ivec *ip;
321 extern struct ivec_dsp idsptch; /* subr.s */
322
323 for (i=0; i<VSBUS_MAX_INTR; i++) {
324 ip = &vsbus_ivtab[i];
325 bcopy(&idsptch, &ip->intr_vec, sizeof(struct ivec_dsp));
326 ip->intr_vec.pushlarg = i;
327 ip->intr_vec.hoppaddr = vsbus_intr_dispatch;
328 ip->intr_count = 0;
329 ip->intr_flags = 0;
330 ip->enab = NULL;
331 ip->disab = NULL;
332 ip->postp = NULL;
333 }
334 switch (vax_boardtype) {
335 case VAX_BTYP_410:
336 case VAX_BTYP_420:
337 case VAX_BTYP_43:
338 case VAX_BTYP_46:
339 case VAX_BTYP_49:
340 ka410_intr_setup();
341 return(0);
342 default:
343 printf("unsupported board-type 0x%x in vsbus_intr_setup()\n",
344 vax_boardtype);
345 return(1);
346 }
347 }
348
349 int
350 vsbus_intr_register(ca, handler, arg)
351 struct confargs *ca;
352 int (*handler)(void*);
353 void *arg;
354 {
355 /* struct device *dev = arg; */
356 int i = ca->ca_intslot;
357 struct vsbus_ivec *ip = &vsbus_ivtab[i];
358
359 trace (("vsbus_intr_register(%s/%d)\n", ca->ca_name, ca->ca_intslot));
360
361 ip->handler = handler;
362 ip->hndlarg = arg;
363 }
364
365 int
366 vsbus_intr_enable(ca)
367 struct confargs *ca;
368 {
369 int i = ca->ca_intslot;
370 struct vsbus_ivec *ip = &vsbus_ivtab[i];
371
372 trace (("vsbus_intr_enable(%s/%d)\n", ca->ca_name, ca->ca_intslot));
373
374 /* XXX check for valid handler etc. !!! */
375 if (ip->handler == NULL) {
376 printf("interrupts for \"%s\"(%d) not enabled: null-handler\n",
377 ca->ca_name, ca->ca_intslot);
378 return;
379 }
380
381 ip->enab(i);
382 }
383
384 int
385 vsbus_intr_disable(ca)
386 struct confargs *ca;
387 {
388 int i = ca->ca_intslot;
389 struct vsbus_ivec *ip = &vsbus_ivtab[i];
390
391 trace (("vsbus_intr_disable(%s/%d)\n", ca->ca_name, i));
392
393 ip->disab(i);
394 }
395
396 int
397 vsbus_intr_unregister(ca)
398 struct confargs *ca;
399 {
400 int i = ca->ca_intslot;
401 struct vsbus_ivec *ip = &vsbus_ivtab[i];
402
403 trace (("vsbus_intr_unregister(%s/%d)\n", ca->ca_name, i));
404
405 ip->handler = NULL;
406 ip->hndlarg = NULL;
407 }
408
409 void
410 vsbus_intr_dispatch(i)
411 register int i;
412 {
413 register struct vsbus_ivec *ip = &vsbus_ivtab[i];
414
415 trace (("vsbus_intr_dispatch(%d)", i));
416
417 if (i < VSBUS_MAX_INTR && ip->handler != NULL) {
418 ip->intr_count++;
419 debug (("intr-count[%d] = %d\n", i, ip->intr_count));
420 (ip->handler)(ip->hndlarg);
421 if (ip->postp)
422 (ip->postp)(i);
423 return;
424 }
425
426 if (i < 0 || i >= VSBUS_MAX_INTR) {
427 printf ("stray interrupt %d on vsbus.\n", i);
428 return;
429 }
430
431 if (!ip->handler) {
432 printf ("unhandled interrupt %d on vsbus.\n", i);
433 return;
434 }
435 }
436
437 /*
438 * These addresses are invalid and will be updated/corrected by
439 * ka410_intr_setup(), but having them this way helps debugging
440 */
441 static volatile u_char *ka410_intmsk = (void*)KA410_INTMSK;
442 static volatile u_char *ka410_intreq = (void*)KA410_INTREQ;
443 static volatile u_char *ka410_intclr = (void*)KA410_INTCLR;
444
445 static void
446 ka410_intr_enable(i)
447 int i;
448 {
449 trace (("ka410_intr_enable(%d)\n", i));
450 *ka410_intmsk |= (1<<i);
451 }
452
453 static void
454 ka410_intr_disable(i)
455 int i;
456 {
457 trace (("ka410_intr_disable(%d)\n", i));
458 *ka410_intmsk &= ~(1<<i);
459 }
460
461 static void
462 ka410_intr_clear(i)
463 int i;
464 {
465 trace (("ka410_intr_clear(%d)\n", i));
466 *ka410_intclr = (1<<i);
467 }
468
469 ka410_intr_setup()
470 {
471 int i;
472 struct vsbus_ivec *ip;
473 void **scbP = (void*)scb;
474
475 trace (("ka410_intr_setup()\n"));
476
477 ka410_intmsk = (void*)uvax_phys2virt(KA410_INTMSK);
478 ka410_intreq = (void*)uvax_phys2virt(KA410_INTREQ);
479 ka410_intclr = (void*)uvax_phys2virt(KA410_INTCLR);
480
481 *ka410_intmsk = 0; /* disable all interrupts */
482 *ka410_intclr = 0xFF; /* clear all old interrupts */
483
484 /*
485 * insert the VS2000-specific routines into ivec-table...
486 */
487 for (i=0; i<8; i++) {
488 ip = &vsbus_ivtab[i];
489 ip->enab = ka410_intr_enable;
490 ip->disab = ka410_intr_disable;
491 /* ip->postp = ka410_intr_clear; bertram XXX */
492 }
493 /*
494 * ...and register the interrupt-vectors in SCB
495 */
496 scbP[IVEC_DC/4] = &vsbus_ivtab[0].intr_vec;
497 scbP[IVEC_SC/4] = &vsbus_ivtab[1].intr_vec;
498 scbP[IVEC_VS/4] = &vsbus_ivtab[2].intr_vec;
499 scbP[IVEC_VF/4] = &vsbus_ivtab[3].intr_vec;
500 scbP[IVEC_NS/4] = &vsbus_ivtab[4].intr_vec;
501 scbP[IVEC_NP/4] = &vsbus_ivtab[5].intr_vec;
502 scbP[IVEC_ST/4] = &vsbus_ivtab[6].intr_vec;
503 scbP[IVEC_SR/4] = &vsbus_ivtab[7].intr_vec;
504 }
505
506 /*
507 *
508 *
509 */
510
511 static volatile struct dma_lock {
512 int dl_locked;
513 int dl_wanted;
514 void *dl_owner;
515 int dl_count;
516 } dmalock = { 0, 0, NULL, 0 };
517
518 int
519 vsbus_lockDMA(ca)
520 struct confargs *ca;
521 {
522 while (dmalock.dl_locked) {
523 dmalock.dl_wanted++;
524 sleep((caddr_t)&dmalock, PRIBIO); /* PLOCK or PRIBIO ? */
525 dmalock.dl_wanted--;
526 }
527 dmalock.dl_locked++;
528 dmalock.dl_owner = ca;
529
530 /*
531 * no checks yet, no timeouts, nothing...
532 */
533
534 #ifdef DEBUG
535 if ((++dmalock.dl_count % 1000) == 0)
536 printf("%d locks, owner: %s\n", dmalock.dl_count, ca->ca_name);
537 #endif
538 return (0);
539 }
540
541 int
542 vsbus_unlockDMA(ca)
543 struct confargs *ca;
544 {
545 if (dmalock.dl_locked != 1 || dmalock.dl_owner != ca) {
546 printf("locking-problem: %d, %s\n", dmalock.dl_locked,
547 (dmalock.dl_owner ? dmalock.dl_owner : "null"));
548 dmalock.dl_locked = 0;
549 return (-1);
550 }
551 dmalock.dl_owner = NULL;
552 dmalock.dl_locked = 0;
553 if (dmalock.dl_wanted) {
554 wakeup((caddr_t)&dmalock);
555 }
556 return (0);
557 }
558
559 /*----------------------------------------------------------------------*/
560 #if 0
561 /*
562 * small set of routines needed for mapping when doing pseudo-DMA,
563 * quasi-DMA or virtual-DMA (choose whatever name you like).
564 *
565 * Once I know how VS3100 is doing real DMA (I hope it does), this
566 * should be rewritten to present a general interface...
567 *
568 */
569
570 extern u_long uVAX_physmap;
571
572 u_long
573 vsdma_mapin(bp, len)
574 struct buf *bp;
575 int len;
576 {
577 pt_entry_t *pte; /* pointer to Page-Table-Entry */
578 struct pcb *pcb; /* pointer to Process-Controll-Block */
579 pt_entry_t *xpte;
580 caddr_t addr;
581 int pgoff; /* offset into 1st page */
582 int pgcnt; /* number of pages needed */
583 int pfnum;
584 int i;
585
586 trace(("mapin(bp=%x, bp->data=%x)\n", bp, bp->b_data));
587
588 addr = bp->b_data;
589 pgoff = (int)bp->b_data & PGOFSET; /* get starting offset */
590 pgcnt = btoc(bp->b_bcount + pgoff) + 1; /* one more than needed */
591
592 /*
593 * Get a pointer to the pte pointing out the first virtual address.
594 * Use different ways in kernel and user space.
595 */
596 if ((bp->b_flags & B_PHYS) == 0) {
597 pte = kvtopte(addr);
598 } else {
599 pcb = &bp->b_proc->p_addr->u_pcb;
600 pte = uvtopte(addr, pcb);
601 }
602
603 /*
604 * When we are doing DMA to user space, be sure that all pages
605 * we want to transfer to are mapped. WHY DO WE NEED THIS???
606 * SHOULDN'T THEY ALWAYS BE MAPPED WHEN DOING THIS???
607 */
608 for (i=0; i<(pgcnt-1); i++) {
609 if ((pte + i)->pg_pfn == 0) {
610 int rv;
611 rv = vm_fault(&bp->b_proc->p_vmspace->vm_map,
612 (unsigned)addr + i * NBPG,
613 VM_PROT_READ|VM_PROT_WRITE, FALSE);
614 if (rv)
615 panic("vs-DMA to nonexistent page, %d", rv);
616 }
617 }
618
619 /*
620 * now insert new mappings for this memory area into kernel's
621 * mapping-table
622 */
623 xpte = kvtopte(uVAX_physmap);
624 while (--pgcnt > 0) {
625 pfnum = pte->pg_pfn;
626 if (pfnum == 0)
627 panic("vsbus: zero entry");
628 *(int *)xpte++ = *(int *)pte++;
629 }
630 *(int *)xpte = 0; /* mark last mapped page as invalid! */
631
632 debug(("uVAX: 0x%x\n", uVAX_physmap + pgoff));
633
634 return (uVAX_physmap + pgoff); /* ??? */
635 }
636 #endif
637 /*----------------------------------------------------------------------*/
638 /*
639 * Here follows some currently(?) unused stuff. Someday this should be removed
640 */
641
642 #if 0
643 /*
644 * Configure devices on VS2000/KA410 directly attached to vsbus
645 */
646 void
647 ka410_attach(parent, self, aux)
648 struct device *parent;
649 struct device *self;
650 void *aux;
651 {
652 struct confargs *ca;
653 int i;
654
655 for (i=0; i<KA410_MAXDEVS; i++) {
656 ca = &ka410_devs[i];
657 if (*ca->ca_name == '\0')
658 break;
659 config_found(self, (void*)ca, vsbus_print);
660 }
661 /*
662 * as long as there's no real DZ-driver, we used this dummy
663 */
664 vsbus_intr_register(&ka410_devs[0], ka410_consRecv_intr, NULL);
665 vsbus_intr_register(&ka410_devs[1], ka410_consXmit_intr, NULL);
666 }
667
668 #endif
669