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