pckbport.c revision 1.1
1/* $NetBSD: pckbport.c,v 1.1 2004/03/13 17:31:33 bjh21 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 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed for the NetBSD Project 19 * by Matthias Drochner. 20 * 4. The name of the author may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 27 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 28 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 32 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36__KERNEL_RCSID(0, "$NetBSD: pckbport.c,v 1.1 2004/03/13 17:31:33 bjh21 Exp $"); 37 38#include <sys/param.h> 39#include <sys/systm.h> 40#include <sys/callout.h> 41#include <sys/kernel.h> 42#include <sys/proc.h> 43#include <sys/device.h> 44#include <sys/malloc.h> 45#include <sys/errno.h> 46#include <sys/queue.h> 47#include <sys/lock.h> 48 49#include <dev/pckbport/pckbportvar.h> 50 51#include "locators.h" 52 53#ifdef __HAVE_NWSCONS /* XXX: this port uses sys/dev/pckbport */ 54#include "pckbd.h" 55#else /* ie: only md drivers attach to pckbport */ 56#define NPCKBD 0 57#endif 58#if (NPCKBD > 0) 59#include <dev/pckbport/pckbdvar.h> 60#endif 61 62/* descriptor for one device command */ 63struct pckbport_devcmd { 64 TAILQ_ENTRY(pckbport_devcmd) next; 65 int flags; 66#define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */ 67#define KBC_CMDFLAG_SLOW 2 68 u_char cmd[4]; 69 int cmdlen, cmdidx, retries; 70 u_char response[4]; 71 int status, responselen, responseidx; 72}; 73 74/* data per slave device */ 75struct pckbport_slotdata { 76 int polling; /* don't process data in interrupt handler */ 77 TAILQ_HEAD(, pckbport_devcmd) cmdqueue; /* active commands */ 78 TAILQ_HEAD(, pckbport_devcmd) freequeue; /* free commands */ 79#define NCMD 5 80 struct pckbport_devcmd cmds[NCMD]; 81}; 82 83#define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL) 84 85static void pckbport_init_slotdata __P((struct pckbport_slotdata *)); 86static int pckbport_submatch __P((struct device *, struct cfdata *, void *)); 87static int pckbportprint __P((void *, const char *)); 88 89static struct pckbport_slotdata pckbport_cons_slotdata; 90 91static int pckbport_poll_data1 __P((pckbport_tag_t, pckbport_slot_t)); 92static int pckbport_send_devcmd __P((struct pckbport_tag *, pckbport_slot_t, 93 u_char)); 94static void pckbport_poll_cmd1 __P((struct pckbport_tag *, pckbport_slot_t, 95 struct pckbport_devcmd *)); 96 97static void pckbport_cleanqueue __P((struct pckbport_slotdata *)); 98static void pckbport_cleanup __P((void *)); 99static int pckbport_cmdresponse __P((struct pckbport_tag *, pckbport_slot_t, 100 u_char)); 101static void pckbport_start __P((struct pckbport_tag *, pckbport_slot_t)); 102 103static const char * const pckbport_slot_names[] = { "kbd", "aux" }; 104 105static struct pckbport_tag pckbport_cntag; 106 107#define KBC_DEVCMD_ACK 0xfa 108#define KBC_DEVCMD_RESEND 0xfe 109 110#define KBD_DELAY DELAY(8) 111 112static int 113pckbport_poll_data1(t, slot) 114 pckbport_tag_t t; 115 pckbport_slot_t slot; 116{ 117 118 return t->t_ops->t_poll_data1(t->t_cookie, slot); 119} 120 121static int 122pckbport_send_devcmd(t, slot, val) 123 struct pckbport_tag *t; 124 pckbport_slot_t slot; 125 u_char val; 126{ 127 128 return t->t_ops->t_send_devcmd(t->t_cookie, slot, val); 129} 130 131static int 132pckbport_submatch(parent, cf, aux) 133 struct device *parent; 134 struct cfdata *cf; 135 void *aux; 136{ 137 struct pckbport_attach_args *pa = aux; 138 139 if (cf->cf_loc[PCKBPORTCF_SLOT] != PCKBPORTCF_SLOT_DEFAULT && 140 cf->cf_loc[PCKBPORTCF_SLOT] != pa->pa_slot) 141 return (0); 142 return (config_match(parent, cf, aux)); 143} 144 145pckbport_tag_t 146pckbport_attach(void *cookie, struct pckbport_accessops const *ops) 147{ 148 pckbport_tag_t t; 149 150 if (cookie == pckbport_cntag.t_cookie && 151 ops == pckbport_cntag.t_ops) 152 return &pckbport_cntag; 153 t = malloc(sizeof(struct pckbport_tag), M_DEVBUF, M_NOWAIT | M_ZERO); 154 if (t == NULL) return NULL; 155 t->t_cookie = cookie; 156 t->t_ops = ops; 157 return t; 158} 159 160struct device * 161pckbport_attach_slot(dev, t, slot) 162 struct device *dev; 163 pckbport_tag_t t; 164 pckbport_slot_t slot; 165{ 166 struct pckbport_attach_args pa; 167 void *sdata; 168 struct device *found; 169 int alloced = 0; 170 171 pa.pa_tag = t; 172 pa.pa_slot = slot; 173 174 if (t->t_slotdata[slot] == NULL) { 175 sdata = malloc(sizeof(struct pckbport_slotdata), 176 M_DEVBUF, M_NOWAIT); 177 if (sdata == NULL) { 178 printf("%s: no memory\n", dev->dv_xname); 179 return (0); 180 } 181 t->t_slotdata[slot] = sdata; 182 pckbport_init_slotdata(t->t_slotdata[slot]); 183 alloced++; 184 } 185 186 found = config_found_sm(dev, &pa, pckbportprint, pckbport_submatch); 187 188 if (found == NULL && alloced) { 189 free(t->t_slotdata[slot], M_DEVBUF); 190 t->t_slotdata[slot] = NULL; 191 } 192 193 return (found); 194} 195 196int 197pckbportprint(aux, pnp) 198 void *aux; 199 const char *pnp; 200{ 201 struct pckbport_attach_args *pa = aux; 202 203 if (!pnp) 204 aprint_normal(" (%s slot)", pckbport_slot_names[pa->pa_slot]); 205 return (QUIET); 206} 207 208void 209pckbport_init_slotdata(q) 210 struct pckbport_slotdata *q; 211{ 212 int i; 213 TAILQ_INIT(&q->cmdqueue); 214 TAILQ_INIT(&q->freequeue); 215 216 for (i = 0; i < NCMD; i++) { 217 TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next); 218 } 219 q->polling = 0; 220} 221 222void 223pckbport_flush(t, slot) 224 pckbport_tag_t t; 225 pckbport_slot_t slot; 226{ 227 228 (void) pckbport_poll_data1(t, slot); 229} 230 231int 232pckbport_poll_data(t, slot) 233 pckbport_tag_t t; 234 pckbport_slot_t slot; 235{ 236 struct pckbport_slotdata *q = t->t_slotdata[slot]; 237 int c; 238 239 c = pckbport_poll_data1(t, slot); 240 if (c != -1 && q && CMD_IN_QUEUE(q)) { 241 /* we jumped into a running command - try to 242 deliver the response */ 243 if (pckbport_cmdresponse(t, slot, c)) 244 return (-1); 245 } 246 return (c); 247} 248 249/* 250 * switch scancode translation on / off 251 * return nonzero on success 252 */ 253int 254pckbport_xt_translation(t, slot, on) 255 pckbport_tag_t t; 256 pckbport_slot_t slot; 257 int on; 258{ 259 260 return t->t_ops->t_xt_translation(t->t_cookie, slot, on); 261} 262 263void 264pckbport_slot_enable(t, slot, on) 265 pckbport_tag_t t; 266 pckbport_slot_t slot; 267 int on; 268{ 269 270 t->t_ops->t_slot_enable(t->t_cookie, slot, on); 271} 272 273void 274pckbport_set_poll(t, slot, on) 275 pckbport_tag_t t; 276 pckbport_slot_t slot; 277 int on; 278{ 279 280 t->t_slotdata[slot]->polling = on; 281 t->t_ops->t_set_poll(t->t_cookie, slot, on); 282} 283 284/* 285 * Pass command to device, poll for ACK and data. 286 * to be called at spltty() 287 */ 288static void 289pckbport_poll_cmd1(t, slot, cmd) 290 struct pckbport_tag *t; 291 pckbport_slot_t slot; 292 struct pckbport_devcmd *cmd; 293{ 294 int i, c = 0; 295 296 while (cmd->cmdidx < cmd->cmdlen) { 297 if (!pckbport_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) { 298 printf("pckbport_cmd: send error\n"); 299 cmd->status = EIO; 300 return; 301 } 302 for (i = 10; i; i--) { /* 1s ??? */ 303 c = pckbport_poll_data1(t, slot); 304 if (c != -1) 305 break; 306 } 307 308 if (c == KBC_DEVCMD_ACK) { 309 cmd->cmdidx++; 310 continue; 311 } 312 if (c == KBC_DEVCMD_RESEND) { 313#ifdef PCKBPORTDEBUG 314 printf("pckbport_cmd: RESEND\n"); 315#endif 316 if (cmd->retries++ < 5) 317 continue; 318 else { 319#ifdef PCKBPORTDEBUG 320 printf("pckbport: cmd failed\n"); 321#endif 322 cmd->status = EIO; 323 return; 324 } 325 } 326 if (c == -1) { 327#ifdef PCKBPORTDEBUG 328 printf("pckbport_cmd: timeout\n"); 329#endif 330 cmd->status = EIO; 331 return; 332 } 333#ifdef PCKBPORTDEBUG 334 printf("pckbport_cmd: lost 0x%x\n", c); 335#endif 336 } 337 338 while (cmd->responseidx < cmd->responselen) { 339 if (cmd->flags & KBC_CMDFLAG_SLOW) 340 i = 100; /* 10s ??? */ 341 else 342 i = 10; /* 1s ??? */ 343 while (i--) { 344 c = pckbport_poll_data1(t, slot); 345 if (c != -1) 346 break; 347 } 348 if (c == -1) { 349#ifdef PCKBPORTDEBUG 350 printf("pckbport_cmd: no data\n"); 351#endif 352 cmd->status = ETIMEDOUT; 353 return; 354 } else 355 cmd->response[cmd->responseidx++] = c; 356 } 357} 358 359/* for use in autoconfiguration */ 360int 361pckbport_poll_cmd(t, slot, cmd, len, responselen, respbuf, slow) 362 pckbport_tag_t t; 363 pckbport_slot_t slot; 364 u_char *cmd; 365 int len, responselen; 366 u_char *respbuf; 367 int slow; 368{ 369 struct pckbport_devcmd nc; 370 371 if ((len > 4) || (responselen > 4)) 372 return (EINVAL); 373 374 memset(&nc, 0, sizeof(nc)); 375 memcpy(nc.cmd, cmd, len); 376 nc.cmdlen = len; 377 nc.responselen = responselen; 378 nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0); 379 380 pckbport_poll_cmd1(t, slot, &nc); 381 382 if (nc.status == 0 && respbuf) 383 memcpy(respbuf, nc.response, responselen); 384 385 return (nc.status); 386} 387 388/* 389 * Clean up a command queue, throw away everything. 390 */ 391void 392pckbport_cleanqueue(q) 393 struct pckbport_slotdata *q; 394{ 395 struct pckbport_devcmd *cmd; 396#ifdef PCKBPORTDEBUG 397 int i; 398#endif 399 400 while ((cmd = TAILQ_FIRST(&q->cmdqueue))) { 401 TAILQ_REMOVE(&q->cmdqueue, cmd, next); 402#ifdef PCKBPORTDEBUG 403 printf("pckbport_cleanqueue: removing"); 404 for (i = 0; i < cmd->cmdlen; i++) 405 printf(" %02x", cmd->cmd[i]); 406 printf("\n"); 407#endif 408 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); 409 } 410} 411 412/* 413 * Timeout error handler: clean queues and data port. 414 * XXX could be less invasive. 415 */ 416void 417pckbport_cleanup(self) 418 void *self; 419{ 420 struct pckbport_tag *t = self; 421 int s; 422 423 printf("pckbport: command timeout\n"); 424 425 s = spltty(); 426 427 if (t->t_slotdata[PCKBPORT_KBD_SLOT]) 428 pckbport_cleanqueue(t->t_slotdata[PCKBPORT_KBD_SLOT]); 429 if (t->t_slotdata[PCKBPORT_AUX_SLOT]) 430 pckbport_cleanqueue(t->t_slotdata[PCKBPORT_AUX_SLOT]); 431 432#if 0 /* XXXBJH Move to controller driver? */ 433 while (bus_space_read_1(t->t_iot, t->t_ioh_c, 0) & KBS_DIB) { 434 KBD_DELAY; 435 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0); 436 } 437#endif 438 439 /* reset KBC? */ 440 441 splx(s); 442} 443 444/* 445 * Pass command to device during normal operation. 446 * to be called at spltty() 447 */ 448void 449pckbport_start(t, slot) 450 struct pckbport_tag *t; 451 pckbport_slot_t slot; 452{ 453 struct pckbport_slotdata *q = t->t_slotdata[slot]; 454 struct pckbport_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue); 455 456 if (q->polling) { 457 do { 458 pckbport_poll_cmd1(t, slot, cmd); 459 if (cmd->status) 460 printf("pckbport_start: command error\n"); 461 462 TAILQ_REMOVE(&q->cmdqueue, cmd, next); 463 if (cmd->flags & KBC_CMDFLAG_SYNC) 464 wakeup(cmd); 465 else { 466 callout_stop(&t->t_cleanup); 467 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); 468 } 469 cmd = TAILQ_FIRST(&q->cmdqueue); 470 } while (cmd); 471 return; 472 } 473 474 if (!pckbport_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) { 475 printf("pckbport_start: send error\n"); 476 /* XXX what now? */ 477 return; 478 } 479} 480 481/* 482 * Handle command responses coming in asynchronously, 483 * return nonzero if valid response. 484 * to be called at spltty() 485 */ 486int 487pckbport_cmdresponse(t, slot, data) 488 struct pckbport_tag *t; 489 pckbport_slot_t slot; 490 u_char data; 491{ 492 struct pckbport_slotdata *q = t->t_slotdata[slot]; 493 struct pckbport_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue); 494#ifdef DIAGNOSTIC 495 if (!cmd) 496 panic("pckbport_cmdresponse: no active command"); 497#endif 498 if (cmd->cmdidx < cmd->cmdlen) { 499 if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND) 500 return (0); 501 502 if (data == KBC_DEVCMD_RESEND) { 503 if (cmd->retries++ < 5) { 504 /* try again last command */ 505 goto restart; 506 } else { 507#ifdef PCKBPORTDEBUG 508 printf("pckbport: cmd failed\n"); 509#endif 510 cmd->status = EIO; 511 /* dequeue */ 512 } 513 } else { 514 if (++cmd->cmdidx < cmd->cmdlen) 515 goto restart; 516 if (cmd->responselen) 517 return (1); 518 /* else dequeue */ 519 } 520 } else if (cmd->responseidx < cmd->responselen) { 521 cmd->response[cmd->responseidx++] = data; 522 if (cmd->responseidx < cmd->responselen) 523 return (1); 524 /* else dequeue */ 525 } else 526 return (0); 527 528 /* dequeue: */ 529 TAILQ_REMOVE(&q->cmdqueue, cmd, next); 530 if (cmd->flags & KBC_CMDFLAG_SYNC) 531 wakeup(cmd); 532 else { 533 callout_stop(&t->t_cleanup); 534 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next); 535 } 536 if (!CMD_IN_QUEUE(q)) 537 return (1); 538restart: 539 pckbport_start(t, slot); 540 return (1); 541} 542 543/* 544 * Put command into the device's command queue, return zero or errno. 545 */ 546int 547pckbport_enqueue_cmd(t, slot, cmd, len, responselen, sync, respbuf) 548 pckbport_tag_t t; 549 pckbport_slot_t slot; 550 u_char *cmd; 551 int len, responselen, sync; 552 u_char *respbuf; 553{ 554 struct pckbport_slotdata *q = t->t_slotdata[slot]; 555 struct pckbport_devcmd *nc; 556 int s, isactive, res = 0; 557 558 if ((len > 4) || (responselen > 4)) 559 return (EINVAL); 560 s = spltty(); 561 nc = TAILQ_FIRST(&q->freequeue); 562 if (nc) { 563 TAILQ_REMOVE(&q->freequeue, nc, next); 564 } 565 splx(s); 566 if (!nc) 567 return (ENOMEM); 568 569 memset(nc, 0, sizeof(*nc)); 570 memcpy(nc->cmd, cmd, len); 571 nc->cmdlen = len; 572 nc->responselen = responselen; 573 nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0); 574 575 s = spltty(); 576 577 if (q->polling && sync) { 578 /* 579 * XXX We should poll until the queue is empty. 580 * But we don't come here normally, so make 581 * it simple and throw away everything. 582 */ 583 pckbport_cleanqueue(q); 584 } 585 586 isactive = CMD_IN_QUEUE(q); 587 TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next); 588 if (!isactive) 589 pckbport_start(t, slot); 590 591 if (q->polling) 592 res = (sync ? nc->status : 0); 593 else if (sync) { 594 if ((res = tsleep(nc, 0, "kbccmd", 1*hz))) { 595 TAILQ_REMOVE(&q->cmdqueue, nc, next); 596 pckbport_cleanup(t); 597 } else 598 res = nc->status; 599 } else 600 callout_reset(&t->t_cleanup, hz, pckbport_cleanup, t); 601 602 if (sync) { 603 if (respbuf) 604 memcpy(respbuf, nc->response, responselen); 605 TAILQ_INSERT_TAIL(&q->freequeue, nc, next); 606 } 607 608 splx(s); 609 610 return (res); 611} 612 613void 614pckbport_set_inputhandler(t, slot, func, arg, name) 615 pckbport_tag_t t; 616 pckbport_slot_t slot; 617 pckbport_inputfcn func; 618 void *arg; 619 char *name; 620{ 621 622 if (slot >= PCKBPORT_NSLOTS) 623 panic("pckbport_set_inputhandler: bad slot %d", slot); 624 625 t->t_ops->t_intr_establish(t->t_cookie, slot); 626 627 t->t_inputhandler[slot] = func; 628 t->t_inputarg[slot] = arg; 629 t->t_subname[slot] = name; 630} 631 632void 633pckbportintr(pckbport_tag_t t, pckbport_slot_t slot, int data) 634{ 635 struct pckbport_slotdata *q; 636 637 q = t->t_slotdata[slot]; 638 639 if (!q) { 640 /* XXX do something for live insertion? */ 641 printf("pckbportintr: no dev for slot %d\n", slot); 642 return; 643 } 644 645 if (CMD_IN_QUEUE(q) && pckbport_cmdresponse(t, slot, data)) 646 return; 647 648 if (t->t_inputhandler[slot]) 649 (*t->t_inputhandler[slot])(t->t_inputarg[slot], data); 650#ifdef PCKBPORTDEBUG 651 else 652 printf("pckbportintr: slot %d lost %d\n", slot, data); 653#endif 654} 655 656int 657pckbport_cnattach(void *cookie, struct pckbport_accessops const *ops, 658 pckbport_slot_t slot) 659{ 660 int res = 0; 661 pckbport_tag_t t = &pckbport_cntag; 662 663 t->t_cookie = cookie; 664 t->t_ops = ops; 665 666 /* flush */ 667 pckbport_flush(t, slot); 668 669#if (NPCKBD > 0) 670 res = pckbd_cnattach(t, slot); 671#elif (NPCKBPORT_MACHDEP_CNATTACH > 0) 672 res = pckbport_machdep_cnattach(t, slot); 673#else 674 res = ENXIO; 675#endif /* NPCKBPORT_MACHDEP_CNATTACH > 0 */ 676 677 if (res == 0) { 678 t->t_slotdata[slot] = &pckbport_cons_slotdata; 679 pckbport_init_slotdata(&pckbport_cons_slotdata); 680 } 681 682 return res; 683} 684