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