pckbport.c revision 1.3
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 */ 57struct 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 */ 69struct 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 79static void pckbport_init_slotdata(struct pckbport_slotdata *); 80static int pckbport_submatch(struct device *, struct cfdata *, void *); 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 120static int 121pckbport_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 131pckbport_tag_t 132pckbport_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 146struct device * 147pckbport_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 180int 181pckbportprint(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 190void 191pckbport_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 204void 205pckbport_flush(pckbport_tag_t t, pckbport_slot_t slot) 206{ 207 208 (void)pckbport_poll_data1(t, slot); 209} 210 211int 212pckbport_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 */ 232int 233pckbport_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 239void 240pckbport_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 246void 247pckbport_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 */ 258static void 259pckbport_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 */ 328int 329pckbport_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 */ 354void 355pckbport_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 */ 378void 379pckbport_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 */ 409void 410pckbport_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 */ 445int 446pckbport_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; 495restart: 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 */ 503int 504pckbport_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 564void 565pckbport_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 579void 580pckbportintr(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 603int 604pckbport_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} 631