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