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