1 /* $NetBSD: pckbport.c,v 1.12 2008/03/15 18:46:22 cube Exp $ */
4 * Copyright (c) 2004 Ben Harris
6 * Matthias Drochner. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pckbport.c,v 1.12 2008/03/15 18:46:22 cube Exp $");
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/callout.h>
35 #include <sys/kernel.h>
37 #include <sys/device.h>
38 #include <sys/malloc.h>
39 #include <sys/errno.h>
40 #include <sys/queue.h>
42 #include <dev/pckbport/pckbdreg.h>
43 #include <dev/pckbport/pckbportvar.h>
49 #include <dev/pckbport/pckbdvar.h>
52 /* descriptor for one device command */
53 struct pckbport_devcmd
{
54 TAILQ_ENTRY(pckbport_devcmd
) next
;
56 #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
57 #define KBC_CMDFLAG_SLOW 2
59 int cmdlen
, cmdidx
, retries
;
61 int status
, responselen
, responseidx
;
64 /* data per slave device */
65 struct pckbport_slotdata
{
66 int polling
; /* don't process data in interrupt handler */
67 TAILQ_HEAD(, pckbport_devcmd
) cmdqueue
; /* active commands */
68 TAILQ_HEAD(, pckbport_devcmd
) freequeue
; /* free commands */
70 struct pckbport_devcmd cmds
[NCMD
];
73 #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
75 static void pckbport_init_slotdata(struct pckbport_slotdata
*);
76 static int pckbportprint(void *, const char *);
78 static struct pckbport_slotdata pckbport_cons_slotdata
;
80 static int pckbport_poll_data1(pckbport_tag_t
, pckbport_slot_t
);
81 static int pckbport_send_devcmd(struct pckbport_tag
*, pckbport_slot_t
,
83 static void pckbport_poll_cmd1(struct pckbport_tag
*, pckbport_slot_t
,
84 struct pckbport_devcmd
*);
86 static void pckbport_cleanqueue(struct pckbport_slotdata
*);
87 static void pckbport_cleanup(void *);
88 static int pckbport_cmdresponse(struct pckbport_tag
*, pckbport_slot_t
,
90 static void pckbport_start(struct pckbport_tag
*, pckbport_slot_t
);
92 static const char * const pckbport_slot_names
[] = { "kbd", "aux" };
94 static struct pckbport_tag pckbport_cntag
;
96 #define KBC_DEVCMD_ACK 0xfa
97 #define KBC_DEVCMD_RESEND 0xfe
99 #define KBD_DELAY DELAY(8)
102 pckbport_poll_data1(pckbport_tag_t t
, pckbport_slot_t slot
)
105 return t
->t_ops
->t_poll_data1(t
->t_cookie
, slot
);
109 pckbport_send_devcmd(struct pckbport_tag
*t
, pckbport_slot_t slot
, u_char val
)
112 return t
->t_ops
->t_send_devcmd(t
->t_cookie
, slot
, val
);
116 pckbport_attach(void *cookie
, struct pckbport_accessops
const *ops
)
120 if (cookie
== pckbport_cntag
.t_cookie
&&
121 ops
== pckbport_cntag
.t_ops
)
122 return &pckbport_cntag
;
123 t
= malloc(sizeof(struct pckbport_tag
), M_DEVBUF
, M_NOWAIT
| M_ZERO
);
124 if (t
== NULL
) return NULL
;
125 callout_init(&t
->t_cleanup
, 0);
126 t
->t_cookie
= cookie
;
132 pckbport_attach_slot(device_t dev
, pckbport_tag_t t
,
133 pckbport_slot_t slot
)
135 struct pckbport_attach_args pa
;
139 int locs
[PCKBPORTCF_NLOCS
];
144 if (t
->t_slotdata
[slot
] == NULL
) {
145 sdata
= malloc(sizeof(struct pckbport_slotdata
),
148 aprint_error_dev(dev
, "no memory\n");
151 t
->t_slotdata
[slot
] = sdata
;
152 pckbport_init_slotdata(t
->t_slotdata
[slot
]);
156 locs
[PCKBPORTCF_SLOT
] = slot
;
158 found
= config_found_sm_loc(dev
, "pckbport", locs
, &pa
,
159 pckbportprint
, config_stdsubmatch
);
161 if (found
== NULL
&& alloced
) {
162 free(t
->t_slotdata
[slot
], M_DEVBUF
);
163 t
->t_slotdata
[slot
] = NULL
;
170 pckbportprint(void *aux
, const char *pnp
)
172 struct pckbport_attach_args
*pa
= aux
;
175 aprint_normal(" (%s slot)", pckbport_slot_names
[pa
->pa_slot
]);
180 pckbport_init_slotdata(struct pckbport_slotdata
*q
)
184 TAILQ_INIT(&q
->cmdqueue
);
185 TAILQ_INIT(&q
->freequeue
);
187 for (i
= 0; i
< NCMD
; i
++)
188 TAILQ_INSERT_TAIL(&q
->freequeue
, &(q
->cmds
[i
]), next
);
194 pckbport_flush(pckbport_tag_t t
, pckbport_slot_t slot
)
197 (void)pckbport_poll_data1(t
, slot
);
201 pckbport_poll_data(pckbport_tag_t t
, pckbport_slot_t slot
)
203 struct pckbport_slotdata
*q
= t
->t_slotdata
[slot
];
206 c
= pckbport_poll_data1(t
, slot
);
207 if (c
!= -1 && q
&& CMD_IN_QUEUE(q
))
209 * we jumped into a running command - try to deliver
212 if (pckbport_cmdresponse(t
, slot
, c
))
218 * switch scancode translation on / off
219 * return nonzero on success
222 pckbport_xt_translation(pckbport_tag_t t
, pckbport_slot_t slot
, int on
)
225 return t
->t_ops
->t_xt_translation(t
->t_cookie
, slot
, on
);
229 pckbport_slot_enable(pckbport_tag_t t
, pckbport_slot_t slot
, int on
)
232 t
->t_ops
->t_slot_enable(t
->t_cookie
, slot
, on
);
236 pckbport_set_poll(pckbport_tag_t t
, pckbport_slot_t slot
, int on
)
239 t
->t_slotdata
[slot
]->polling
= on
;
240 t
->t_ops
->t_set_poll(t
->t_cookie
, slot
, on
);
244 * Pass command to device, poll for ACK and data.
245 * to be called at spltty()
248 pckbport_poll_cmd1(struct pckbport_tag
*t
, pckbport_slot_t slot
,
249 struct pckbport_devcmd
*cmd
)
253 while (cmd
->cmdidx
< cmd
->cmdlen
) {
254 if (!pckbport_send_devcmd(t
, slot
, cmd
->cmd
[cmd
->cmdidx
])) {
255 printf("pckbport_cmd: send error\n");
259 for (i
= 10; i
; i
--) { /* 1s ??? */
260 c
= pckbport_poll_data1(t
, slot
);
265 if (c
== KBC_DEVCMD_ACK
) {
269 if (c
== KBC_DEVCMD_RESEND
) {
271 printf("pckbport_cmd: RESEND\n");
273 if (cmd
->retries
++ < 5)
277 printf("pckbport: cmd failed\n");
285 printf("pckbport_cmd: timeout\n");
291 printf("pckbport_cmd: lost 0x%x\n", c
);
295 while (cmd
->responseidx
< cmd
->responselen
) {
296 if (cmd
->flags
& KBC_CMDFLAG_SLOW
)
297 i
= 100; /* 10s ??? */
301 c
= pckbport_poll_data1(t
, slot
);
307 printf("pckbport_cmd: no data\n");
309 cmd
->status
= ETIMEDOUT
;
312 cmd
->response
[cmd
->responseidx
++] = c
;
316 /* for use in autoconfiguration */
318 pckbport_poll_cmd(pckbport_tag_t t
, pckbport_slot_t slot
, u_char
*cmd
, int len
,
319 int responselen
, u_char
*respbuf
, int slow
)
321 struct pckbport_devcmd nc
;
323 if ((len
> 4) || (responselen
> 4))
326 memset(&nc
, 0, sizeof(nc
));
327 memcpy(nc
.cmd
, cmd
, len
);
329 nc
.responselen
= responselen
;
330 nc
.flags
= (slow
? KBC_CMDFLAG_SLOW
: 0);
332 pckbport_poll_cmd1(t
, slot
, &nc
);
334 if (nc
.status
== 0 && respbuf
)
335 memcpy(respbuf
, nc
.response
, responselen
);
341 * Clean up a command queue, throw away everything.
344 pckbport_cleanqueue(struct pckbport_slotdata
*q
)
346 struct pckbport_devcmd
*cmd
;
351 while ((cmd
= TAILQ_FIRST(&q
->cmdqueue
))) {
352 TAILQ_REMOVE(&q
->cmdqueue
, cmd
, next
);
354 printf("pckbport_cleanqueue: removing");
355 for (i
= 0; i
< cmd
->cmdlen
; i
++)
356 printf(" %02x", cmd
->cmd
[i
]);
359 TAILQ_INSERT_TAIL(&q
->freequeue
, cmd
, next
);
364 * Timeout error handler: clean queues and data port.
365 * XXX could be less invasive.
368 pckbport_cleanup(void *self
)
370 struct pckbport_tag
*t
= self
;
372 u_char cmd
[1], resp
[2];
374 printf("pckbport: command timeout\n");
378 if (t
->t_slotdata
[PCKBPORT_KBD_SLOT
])
379 pckbport_cleanqueue(t
->t_slotdata
[PCKBPORT_KBD_SLOT
]);
380 if (t
->t_slotdata
[PCKBPORT_AUX_SLOT
])
381 pckbport_cleanqueue(t
->t_slotdata
[PCKBPORT_AUX_SLOT
]);
383 #if 0 /* XXXBJH Move to controller driver? */
384 while (bus_space_read_1(t
->t_iot
, t
->t_ioh_c
, 0) & KBS_DIB
) {
386 (void) bus_space_read_1(t
->t_iot
, t
->t_ioh_d
, 0);
391 (void)pckbport_poll_cmd(t
, PCKBPORT_KBD_SLOT
, cmd
, 1, 2, resp
, 1);
392 pckbport_flush(t
, PCKBPORT_KBD_SLOT
);
398 * Pass command to device during normal operation.
399 * to be called at spltty()
402 pckbport_start(struct pckbport_tag
*t
, pckbport_slot_t slot
)
404 struct pckbport_slotdata
*q
= t
->t_slotdata
[slot
];
405 struct pckbport_devcmd
*cmd
= TAILQ_FIRST(&q
->cmdqueue
);
409 pckbport_poll_cmd1(t
, slot
, cmd
);
411 printf("pckbport_start: command error\n");
413 TAILQ_REMOVE(&q
->cmdqueue
, cmd
, next
);
414 if (cmd
->flags
& KBC_CMDFLAG_SYNC
)
417 callout_stop(&t
->t_cleanup
);
418 TAILQ_INSERT_TAIL(&q
->freequeue
, cmd
, next
);
420 cmd
= TAILQ_FIRST(&q
->cmdqueue
);
425 if (!pckbport_send_devcmd(t
, slot
, cmd
->cmd
[cmd
->cmdidx
])) {
426 printf("pckbport_start: send error\n");
433 * Handle command responses coming in asynchronously,
434 * return nonzero if valid response.
435 * to be called at spltty()
438 pckbport_cmdresponse(struct pckbport_tag
*t
, pckbport_slot_t slot
, u_char data
)
440 struct pckbport_slotdata
*q
= t
->t_slotdata
[slot
];
441 struct pckbport_devcmd
*cmd
= TAILQ_FIRST(&q
->cmdqueue
);
445 panic("pckbport_cmdresponse: no active command");
447 if (cmd
->cmdidx
< cmd
->cmdlen
) {
448 if (data
!= KBC_DEVCMD_ACK
&& data
!= KBC_DEVCMD_RESEND
)
451 if (data
== KBC_DEVCMD_RESEND
) {
452 if (cmd
->retries
++ < 5)
453 /* try again last command */
457 printf("pckbport: cmd failed\n");
463 if (++cmd
->cmdidx
< cmd
->cmdlen
)
465 if (cmd
->responselen
)
469 } else if (cmd
->responseidx
< cmd
->responselen
) {
470 cmd
->response
[cmd
->responseidx
++] = data
;
471 if (cmd
->responseidx
< cmd
->responselen
)
478 TAILQ_REMOVE(&q
->cmdqueue
, cmd
, next
);
479 if (cmd
->flags
& KBC_CMDFLAG_SYNC
)
482 callout_stop(&t
->t_cleanup
);
483 TAILQ_INSERT_TAIL(&q
->freequeue
, cmd
, next
);
485 if (!CMD_IN_QUEUE(q
))
488 pckbport_start(t
, slot
);
493 * Put command into the device's command queue, return zero or errno.
496 pckbport_enqueue_cmd(pckbport_tag_t t
, pckbport_slot_t slot
, u_char
*cmd
,
497 int len
, int responselen
, int sync
, u_char
*respbuf
)
499 struct pckbport_slotdata
*q
= t
->t_slotdata
[slot
];
500 struct pckbport_devcmd
*nc
;
501 int s
, isactive
, res
= 0;
503 if ((len
> 4) || (responselen
> 4))
506 nc
= TAILQ_FIRST(&q
->freequeue
);
508 TAILQ_REMOVE(&q
->freequeue
, nc
, next
);
513 memset(nc
, 0, sizeof(*nc
));
514 memcpy(nc
->cmd
, cmd
, len
);
516 nc
->responselen
= responselen
;
517 nc
->flags
= (sync
? KBC_CMDFLAG_SYNC
: 0);
521 if (q
->polling
&& sync
)
523 * XXX We should poll until the queue is empty.
524 * But we don't come here normally, so make
525 * it simple and throw away everything.
527 pckbport_cleanqueue(q
);
529 isactive
= CMD_IN_QUEUE(q
);
530 TAILQ_INSERT_TAIL(&q
->cmdqueue
, nc
, next
);
532 pckbport_start(t
, slot
);
535 res
= (sync
? nc
->status
: 0);
537 if ((res
= tsleep(nc
, 0, "kbccmd", 1*hz
))) {
538 TAILQ_REMOVE(&q
->cmdqueue
, nc
, next
);
543 callout_reset(&t
->t_cleanup
, hz
, pckbport_cleanup
, t
);
547 memcpy(respbuf
, nc
->response
, responselen
);
548 TAILQ_INSERT_TAIL(&q
->freequeue
, nc
, next
);
557 pckbport_set_inputhandler(pckbport_tag_t t
, pckbport_slot_t slot
,
558 pckbport_inputfcn func
, void *arg
, const char *name
)
561 if (slot
>= PCKBPORT_NSLOTS
)
562 panic("pckbport_set_inputhandler: bad slot %d", slot
);
564 t
->t_ops
->t_intr_establish(t
->t_cookie
, slot
);
566 t
->t_inputhandler
[slot
] = func
;
567 t
->t_inputarg
[slot
] = arg
;
568 t
->t_subname
[slot
] = name
;
572 pckbportintr(pckbport_tag_t t
, pckbport_slot_t slot
, int data
)
574 struct pckbport_slotdata
*q
;
576 q
= t
->t_slotdata
[slot
];
579 /* XXX do something for live insertion? */
580 printf("pckbportintr: no dev for slot %d\n", slot
);
584 if (CMD_IN_QUEUE(q
) && pckbport_cmdresponse(t
, slot
, data
))
587 if (t
->t_inputhandler
[slot
])
588 (*t
->t_inputhandler
[slot
])(t
->t_inputarg
[slot
], data
);
591 printf("pckbportintr: slot %d lost %d\n", slot
, data
);
596 pckbport_cnattach(void *cookie
, struct pckbport_accessops
const *ops
,
597 pckbport_slot_t slot
)
600 pckbport_tag_t t
= &pckbport_cntag
;
602 callout_init(&t
->t_cleanup
, 0);
603 t
->t_cookie
= cookie
;
607 pckbport_flush(t
, slot
);
610 res
= pckbd_cnattach(t
, slot
);
611 #elif (NPCKBPORT_MACHDEP_CNATTACH > 0)
612 res
= pckbport_machdep_cnattach(t
, slot
);
615 #endif /* NPCKBPORT_MACHDEP_CNATTACH > 0 */
618 t
->t_slotdata
[slot
] = &pckbport_cons_slotdata
;
619 pckbport_init_slotdata(&pckbport_cons_slotdata
);