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