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