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