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