Home | History | Annotate | Line # | Download | only in pckbport
pckbport.c revision 1.1
      1 /* $NetBSD: pckbport.c,v 1.1 2004/03/13 17:31:33 bjh21 Exp $ */
      2 
      3 /*
      4  * Copyright (c) 2004 Ben Harris
      5  * Copyright (c) 1998
      6  *	Matthias Drochner.  All rights reserved.
      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 for the NetBSD Project
     19  *	by Matthias Drochner.
     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/cdefs.h>
     36 __KERNEL_RCSID(0, "$NetBSD: pckbport.c,v 1.1 2004/03/13 17:31:33 bjh21 Exp $");
     37 
     38 #include <sys/param.h>
     39 #include <sys/systm.h>
     40 #include <sys/callout.h>
     41 #include <sys/kernel.h>
     42 #include <sys/proc.h>
     43 #include <sys/device.h>
     44 #include <sys/malloc.h>
     45 #include <sys/errno.h>
     46 #include <sys/queue.h>
     47 #include <sys/lock.h>
     48 
     49 #include <dev/pckbport/pckbportvar.h>
     50 
     51 #include "locators.h"
     52 
     53 #ifdef __HAVE_NWSCONS /* XXX: this port uses sys/dev/pckbport */
     54 #include "pckbd.h"
     55 #else /* ie: only md drivers attach to pckbport */
     56 #define NPCKBD 0
     57 #endif
     58 #if (NPCKBD > 0)
     59 #include <dev/pckbport/pckbdvar.h>
     60 #endif
     61 
     62 /* descriptor for one device command */
     63 struct pckbport_devcmd {
     64 	TAILQ_ENTRY(pckbport_devcmd) next;
     65 	int flags;
     66 #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
     67 #define KBC_CMDFLAG_SLOW 2
     68 	u_char cmd[4];
     69 	int cmdlen, cmdidx, retries;
     70 	u_char response[4];
     71 	int status, responselen, responseidx;
     72 };
     73 
     74 /* data per slave device */
     75 struct pckbport_slotdata {
     76 	int polling;	/* don't process data in interrupt handler */
     77 	TAILQ_HEAD(, pckbport_devcmd) cmdqueue; /* active commands */
     78 	TAILQ_HEAD(, pckbport_devcmd) freequeue; /* free commands */
     79 #define NCMD 5
     80 	struct pckbport_devcmd cmds[NCMD];
     81 };
     82 
     83 #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
     84 
     85 static void pckbport_init_slotdata __P((struct pckbport_slotdata *));
     86 static int pckbport_submatch __P((struct device *, struct cfdata *, void *));
     87 static int pckbportprint __P((void *, const char *));
     88 
     89 static struct pckbport_slotdata pckbport_cons_slotdata;
     90 
     91 static int pckbport_poll_data1 __P((pckbport_tag_t, pckbport_slot_t));
     92 static int pckbport_send_devcmd __P((struct pckbport_tag *, pckbport_slot_t,
     93 				  u_char));
     94 static void pckbport_poll_cmd1 __P((struct pckbport_tag *, pckbport_slot_t,
     95 				 struct pckbport_devcmd *));
     96 
     97 static void pckbport_cleanqueue __P((struct pckbport_slotdata *));
     98 static void pckbport_cleanup __P((void *));
     99 static int pckbport_cmdresponse __P((struct pckbport_tag *, pckbport_slot_t,
    100 					u_char));
    101 static void pckbport_start __P((struct pckbport_tag *, pckbport_slot_t));
    102 
    103 static const char * const pckbport_slot_names[] = { "kbd", "aux" };
    104 
    105 static struct pckbport_tag pckbport_cntag;
    106 
    107 #define KBC_DEVCMD_ACK 0xfa
    108 #define KBC_DEVCMD_RESEND 0xfe
    109 
    110 #define	KBD_DELAY	DELAY(8)
    111 
    112 static int
    113 pckbport_poll_data1(t, slot)
    114 	pckbport_tag_t t;
    115 	pckbport_slot_t slot;
    116 {
    117 
    118 	return t->t_ops->t_poll_data1(t->t_cookie, slot);
    119 }
    120 
    121 static int
    122 pckbport_send_devcmd(t, slot, val)
    123 	struct pckbport_tag *t;
    124 	pckbport_slot_t slot;
    125 	u_char val;
    126 {
    127 
    128 	return t->t_ops->t_send_devcmd(t->t_cookie, slot, val);
    129 }
    130 
    131 static int
    132 pckbport_submatch(parent, cf, aux)
    133 	struct device *parent;
    134 	struct cfdata *cf;
    135 	void *aux;
    136 {
    137 	struct pckbport_attach_args *pa = aux;
    138 
    139 	if (cf->cf_loc[PCKBPORTCF_SLOT] != PCKBPORTCF_SLOT_DEFAULT &&
    140 	    cf->cf_loc[PCKBPORTCF_SLOT] != pa->pa_slot)
    141 		return (0);
    142 	return (config_match(parent, cf, aux));
    143 }
    144 
    145 pckbport_tag_t
    146 pckbport_attach(void *cookie, struct pckbport_accessops const *ops)
    147 {
    148 	pckbport_tag_t t;
    149 
    150 	if (cookie == pckbport_cntag.t_cookie &&
    151 	    ops == pckbport_cntag.t_ops)
    152 		return &pckbport_cntag;
    153 	t = malloc(sizeof(struct pckbport_tag), M_DEVBUF, M_NOWAIT | M_ZERO);
    154 	if (t == NULL) return NULL;
    155 	t->t_cookie = cookie;
    156 	t->t_ops = ops;
    157 	return t;
    158 }
    159 
    160 struct device *
    161 pckbport_attach_slot(dev, t, slot)
    162 	struct device *dev;
    163 	pckbport_tag_t t;
    164 	pckbport_slot_t slot;
    165 {
    166 	struct pckbport_attach_args pa;
    167 	void *sdata;
    168 	struct device *found;
    169 	int alloced = 0;
    170 
    171 	pa.pa_tag = t;
    172 	pa.pa_slot = slot;
    173 
    174 	if (t->t_slotdata[slot] == NULL) {
    175 		sdata = malloc(sizeof(struct pckbport_slotdata),
    176 		    M_DEVBUF, M_NOWAIT);
    177 		if (sdata == NULL) {
    178 			printf("%s: no memory\n", dev->dv_xname);
    179 			return (0);
    180 		}
    181 		t->t_slotdata[slot] = sdata;
    182 		pckbport_init_slotdata(t->t_slotdata[slot]);
    183 		alloced++;
    184 	}
    185 
    186 	found = config_found_sm(dev, &pa, pckbportprint, pckbport_submatch);
    187 
    188 	if (found == NULL && alloced) {
    189 		free(t->t_slotdata[slot], M_DEVBUF);
    190 		t->t_slotdata[slot] = NULL;
    191 	}
    192 
    193 	return (found);
    194 }
    195 
    196 int
    197 pckbportprint(aux, pnp)
    198 	void *aux;
    199 	const char *pnp;
    200 {
    201 	struct pckbport_attach_args *pa = aux;
    202 
    203 	if (!pnp)
    204 		aprint_normal(" (%s slot)", pckbport_slot_names[pa->pa_slot]);
    205 	return (QUIET);
    206 }
    207 
    208 void
    209 pckbport_init_slotdata(q)
    210 	struct pckbport_slotdata *q;
    211 {
    212 	int i;
    213 	TAILQ_INIT(&q->cmdqueue);
    214 	TAILQ_INIT(&q->freequeue);
    215 
    216 	for (i = 0; i < NCMD; i++) {
    217 		TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
    218 	}
    219 	q->polling = 0;
    220 }
    221 
    222 void
    223 pckbport_flush(t, slot)
    224 	pckbport_tag_t t;
    225 	pckbport_slot_t slot;
    226 {
    227 
    228 	(void) pckbport_poll_data1(t, slot);
    229 }
    230 
    231 int
    232 pckbport_poll_data(t, slot)
    233 	pckbport_tag_t t;
    234 	pckbport_slot_t slot;
    235 {
    236 	struct pckbport_slotdata *q = t->t_slotdata[slot];
    237 	int c;
    238 
    239 	c = pckbport_poll_data1(t, slot);
    240 	if (c != -1 && q && CMD_IN_QUEUE(q)) {
    241 		/* we jumped into a running command - try to
    242 		 deliver the response */
    243 		if (pckbport_cmdresponse(t, slot, c))
    244 			return (-1);
    245 	}
    246 	return (c);
    247 }
    248 
    249 /*
    250  * switch scancode translation on / off
    251  * return nonzero on success
    252  */
    253 int
    254 pckbport_xt_translation(t, slot, on)
    255 	pckbport_tag_t t;
    256 	pckbport_slot_t slot;
    257 	int on;
    258 {
    259 
    260 	return t->t_ops->t_xt_translation(t->t_cookie, slot, on);
    261 }
    262 
    263 void
    264 pckbport_slot_enable(t, slot, on)
    265 	pckbport_tag_t t;
    266 	pckbport_slot_t slot;
    267 	int on;
    268 {
    269 
    270 	t->t_ops->t_slot_enable(t->t_cookie, slot, on);
    271 }
    272 
    273 void
    274 pckbport_set_poll(t, slot, on)
    275 	pckbport_tag_t t;
    276 	pckbport_slot_t slot;
    277 	int on;
    278 {
    279 
    280 	t->t_slotdata[slot]->polling = on;
    281 	t->t_ops->t_set_poll(t->t_cookie, slot, on);
    282 }
    283 
    284 /*
    285  * Pass command to device, poll for ACK and data.
    286  * to be called at spltty()
    287  */
    288 static void
    289 pckbport_poll_cmd1(t, slot, cmd)
    290 	struct pckbport_tag *t;
    291 	pckbport_slot_t slot;
    292 	struct pckbport_devcmd *cmd;
    293 {
    294 	int i, c = 0;
    295 
    296 	while (cmd->cmdidx < cmd->cmdlen) {
    297 		if (!pckbport_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
    298 			printf("pckbport_cmd: send error\n");
    299 			cmd->status = EIO;
    300 			return;
    301 		}
    302 		for (i = 10; i; i--) { /* 1s ??? */
    303 			c = pckbport_poll_data1(t, slot);
    304 			if (c != -1)
    305 				break;
    306 		}
    307 
    308 		if (c == KBC_DEVCMD_ACK) {
    309 			cmd->cmdidx++;
    310 			continue;
    311 		}
    312 		if (c == KBC_DEVCMD_RESEND) {
    313 #ifdef PCKBPORTDEBUG
    314 			printf("pckbport_cmd: RESEND\n");
    315 #endif
    316 			if (cmd->retries++ < 5)
    317 				continue;
    318 			else {
    319 #ifdef PCKBPORTDEBUG
    320 				printf("pckbport: cmd failed\n");
    321 #endif
    322 				cmd->status = EIO;
    323 				return;
    324 			}
    325 		}
    326 		if (c == -1) {
    327 #ifdef PCKBPORTDEBUG
    328 			printf("pckbport_cmd: timeout\n");
    329 #endif
    330 			cmd->status = EIO;
    331 			return;
    332 		}
    333 #ifdef PCKBPORTDEBUG
    334 		printf("pckbport_cmd: lost 0x%x\n", c);
    335 #endif
    336 	}
    337 
    338 	while (cmd->responseidx < cmd->responselen) {
    339 		if (cmd->flags & KBC_CMDFLAG_SLOW)
    340 			i = 100; /* 10s ??? */
    341 		else
    342 			i = 10; /* 1s ??? */
    343 		while (i--) {
    344 			c = pckbport_poll_data1(t, slot);
    345 			if (c != -1)
    346 				break;
    347 		}
    348 		if (c == -1) {
    349 #ifdef PCKBPORTDEBUG
    350 			printf("pckbport_cmd: no data\n");
    351 #endif
    352 			cmd->status = ETIMEDOUT;
    353 			return;
    354 		} else
    355 			cmd->response[cmd->responseidx++] = c;
    356 	}
    357 }
    358 
    359 /* for use in autoconfiguration */
    360 int
    361 pckbport_poll_cmd(t, slot, cmd, len, responselen, respbuf, slow)
    362 	pckbport_tag_t t;
    363 	pckbport_slot_t slot;
    364 	u_char *cmd;
    365 	int len, responselen;
    366 	u_char *respbuf;
    367 	int slow;
    368 {
    369 	struct pckbport_devcmd nc;
    370 
    371 	if ((len > 4) || (responselen > 4))
    372 		return (EINVAL);
    373 
    374 	memset(&nc, 0, sizeof(nc));
    375 	memcpy(nc.cmd, cmd, len);
    376 	nc.cmdlen = len;
    377 	nc.responselen = responselen;
    378 	nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
    379 
    380 	pckbport_poll_cmd1(t, slot, &nc);
    381 
    382 	if (nc.status == 0 && respbuf)
    383 		memcpy(respbuf, nc.response, responselen);
    384 
    385 	return (nc.status);
    386 }
    387 
    388 /*
    389  * Clean up a command queue, throw away everything.
    390  */
    391 void
    392 pckbport_cleanqueue(q)
    393 	struct pckbport_slotdata *q;
    394 {
    395 	struct pckbport_devcmd *cmd;
    396 #ifdef PCKBPORTDEBUG
    397 	int i;
    398 #endif
    399 
    400 	while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
    401 		TAILQ_REMOVE(&q->cmdqueue, cmd, next);
    402 #ifdef PCKBPORTDEBUG
    403 		printf("pckbport_cleanqueue: removing");
    404 		for (i = 0; i < cmd->cmdlen; i++)
    405 			printf(" %02x", cmd->cmd[i]);
    406 		printf("\n");
    407 #endif
    408 		TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
    409 	}
    410 }
    411 
    412 /*
    413  * Timeout error handler: clean queues and data port.
    414  * XXX could be less invasive.
    415  */
    416 void
    417 pckbport_cleanup(self)
    418 	void *self;
    419 {
    420 	struct pckbport_tag *t = self;
    421 	int s;
    422 
    423 	printf("pckbport: command timeout\n");
    424 
    425 	s = spltty();
    426 
    427 	if (t->t_slotdata[PCKBPORT_KBD_SLOT])
    428 		pckbport_cleanqueue(t->t_slotdata[PCKBPORT_KBD_SLOT]);
    429 	if (t->t_slotdata[PCKBPORT_AUX_SLOT])
    430 		pckbport_cleanqueue(t->t_slotdata[PCKBPORT_AUX_SLOT]);
    431 
    432 #if 0 /* XXXBJH Move to controller driver? */
    433 	while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) {
    434 		KBD_DELAY;
    435 		(void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
    436 	}
    437 #endif
    438 
    439 	/* reset KBC? */
    440 
    441 	splx(s);
    442 }
    443 
    444 /*
    445  * Pass command to device during normal operation.
    446  * to be called at spltty()
    447  */
    448 void
    449 pckbport_start(t, slot)
    450 	struct pckbport_tag *t;
    451 	pckbport_slot_t slot;
    452 {
    453 	struct pckbport_slotdata *q = t->t_slotdata[slot];
    454 	struct pckbport_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
    455 
    456 	if (q->polling) {
    457 		do {
    458 			pckbport_poll_cmd1(t, slot, cmd);
    459 			if (cmd->status)
    460 				printf("pckbport_start: command error\n");
    461 
    462 			TAILQ_REMOVE(&q->cmdqueue, cmd, next);
    463 			if (cmd->flags & KBC_CMDFLAG_SYNC)
    464 				wakeup(cmd);
    465 			else {
    466 				callout_stop(&t->t_cleanup);
    467 				TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
    468 			}
    469 			cmd = TAILQ_FIRST(&q->cmdqueue);
    470 		} while (cmd);
    471 		return;
    472 	}
    473 
    474 	if (!pckbport_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
    475 		printf("pckbport_start: send error\n");
    476 		/* XXX what now? */
    477 		return;
    478 	}
    479 }
    480 
    481 /*
    482  * Handle command responses coming in asynchronously,
    483  * return nonzero if valid response.
    484  * to be called at spltty()
    485  */
    486 int
    487 pckbport_cmdresponse(t, slot, data)
    488 	struct pckbport_tag *t;
    489 	pckbport_slot_t slot;
    490 	u_char data;
    491 {
    492 	struct pckbport_slotdata *q = t->t_slotdata[slot];
    493 	struct pckbport_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
    494 #ifdef DIAGNOSTIC
    495 	if (!cmd)
    496 		panic("pckbport_cmdresponse: no active command");
    497 #endif
    498 	if (cmd->cmdidx < cmd->cmdlen) {
    499 		if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
    500 			return (0);
    501 
    502 		if (data == KBC_DEVCMD_RESEND) {
    503 			if (cmd->retries++ < 5) {
    504 				/* try again last command */
    505 				goto restart;
    506 			} else {
    507 #ifdef PCKBPORTDEBUG
    508 				printf("pckbport: cmd failed\n");
    509 #endif
    510 				cmd->status = EIO;
    511 				/* dequeue */
    512 			}
    513 		} else {
    514 			if (++cmd->cmdidx < cmd->cmdlen)
    515 				goto restart;
    516 			if (cmd->responselen)
    517 				return (1);
    518 			/* else dequeue */
    519 		}
    520 	} else if (cmd->responseidx < cmd->responselen) {
    521 		cmd->response[cmd->responseidx++] = data;
    522 		if (cmd->responseidx < cmd->responselen)
    523 			return (1);
    524 		/* else dequeue */
    525 	} else
    526 		return (0);
    527 
    528 	/* dequeue: */
    529 	TAILQ_REMOVE(&q->cmdqueue, cmd, next);
    530 	if (cmd->flags & KBC_CMDFLAG_SYNC)
    531 		wakeup(cmd);
    532 	else {
    533 		callout_stop(&t->t_cleanup);
    534 		TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
    535 	}
    536 	if (!CMD_IN_QUEUE(q))
    537 		return (1);
    538 restart:
    539 	pckbport_start(t, slot);
    540 	return (1);
    541 }
    542 
    543 /*
    544  * Put command into the device's command queue, return zero or errno.
    545  */
    546 int
    547 pckbport_enqueue_cmd(t, slot, cmd, len, responselen, sync, respbuf)
    548 	pckbport_tag_t t;
    549 	pckbport_slot_t slot;
    550 	u_char *cmd;
    551 	int len, responselen, sync;
    552 	u_char *respbuf;
    553 {
    554 	struct pckbport_slotdata *q = t->t_slotdata[slot];
    555 	struct pckbport_devcmd *nc;
    556 	int s, isactive, res = 0;
    557 
    558 	if ((len > 4) || (responselen > 4))
    559 		return (EINVAL);
    560 	s = spltty();
    561 	nc = TAILQ_FIRST(&q->freequeue);
    562 	if (nc) {
    563 		TAILQ_REMOVE(&q->freequeue, nc, next);
    564 	}
    565 	splx(s);
    566 	if (!nc)
    567 		return (ENOMEM);
    568 
    569 	memset(nc, 0, sizeof(*nc));
    570 	memcpy(nc->cmd, cmd, len);
    571 	nc->cmdlen = len;
    572 	nc->responselen = responselen;
    573 	nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
    574 
    575 	s = spltty();
    576 
    577 	if (q->polling && sync) {
    578 		/*
    579 		 * XXX We should poll until the queue is empty.
    580 		 * But we don't come here normally, so make
    581 		 * it simple and throw away everything.
    582 		 */
    583 		pckbport_cleanqueue(q);
    584 	}
    585 
    586 	isactive = CMD_IN_QUEUE(q);
    587 	TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
    588 	if (!isactive)
    589 		pckbport_start(t, slot);
    590 
    591 	if (q->polling)
    592 		res = (sync ? nc->status : 0);
    593 	else if (sync) {
    594 		if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) {
    595 			TAILQ_REMOVE(&q->cmdqueue, nc, next);
    596 			pckbport_cleanup(t);
    597 		} else
    598 			res = nc->status;
    599 	} else
    600 		callout_reset(&t->t_cleanup, hz, pckbport_cleanup, t);
    601 
    602 	if (sync) {
    603 		if (respbuf)
    604 			memcpy(respbuf, nc->response, responselen);
    605 		TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
    606 	}
    607 
    608 	splx(s);
    609 
    610 	return (res);
    611 }
    612 
    613 void
    614 pckbport_set_inputhandler(t, slot, func, arg, name)
    615 	pckbport_tag_t t;
    616 	pckbport_slot_t slot;
    617 	pckbport_inputfcn func;
    618 	void *arg;
    619 	char *name;
    620 {
    621 
    622 	if (slot >= PCKBPORT_NSLOTS)
    623 		panic("pckbport_set_inputhandler: bad slot %d", slot);
    624 
    625 	t->t_ops->t_intr_establish(t->t_cookie, slot);
    626 
    627 	t->t_inputhandler[slot] = func;
    628 	t->t_inputarg[slot] = arg;
    629 	t->t_subname[slot] = name;
    630 }
    631 
    632 void
    633 pckbportintr(pckbport_tag_t t, pckbport_slot_t slot, int data)
    634 {
    635 	struct pckbport_slotdata *q;
    636 
    637 	q = t->t_slotdata[slot];
    638 
    639 	if (!q) {
    640 		/* XXX do something for live insertion? */
    641 		printf("pckbportintr: no dev for slot %d\n", slot);
    642 		return;
    643 	}
    644 
    645 	if (CMD_IN_QUEUE(q) && pckbport_cmdresponse(t, slot, data))
    646 		return;
    647 
    648 	if (t->t_inputhandler[slot])
    649 		(*t->t_inputhandler[slot])(t->t_inputarg[slot], data);
    650 #ifdef PCKBPORTDEBUG
    651 	else
    652 		printf("pckbportintr: slot %d lost %d\n", slot, data);
    653 #endif
    654 }
    655 
    656 int
    657 pckbport_cnattach(void *cookie, struct pckbport_accessops const *ops,
    658     pckbport_slot_t slot)
    659 {
    660 	int res = 0;
    661 	pckbport_tag_t t = &pckbport_cntag;
    662 
    663 	t->t_cookie = cookie;
    664 	t->t_ops = ops;
    665 
    666 	/* flush */
    667 	pckbport_flush(t, slot);
    668 
    669 #if (NPCKBD > 0)
    670 	res = pckbd_cnattach(t, slot);
    671 #elif (NPCKBPORT_MACHDEP_CNATTACH > 0)
    672 	res = pckbport_machdep_cnattach(t, slot);
    673 #else
    674 	res = ENXIO;
    675 #endif /* NPCKBPORT_MACHDEP_CNATTACH > 0 */
    676 
    677 	if (res == 0) {
    678 		t->t_slotdata[slot] = &pckbport_cons_slotdata;
    679 		pckbport_init_slotdata(&pckbport_cons_slotdata);
    680 	}
    681 
    682 	return res;
    683 }
    684