tctrl.c revision 1.6
1/* $NetBSD: tctrl.c,v 1.6 2000/03/09 07:04:08 garbled Exp $ */ 2 3/*- 4 * Copyright (c) 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Matt Thomas. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39#include <sys/param.h> 40#include <sys/systm.h> 41#include <sys/ioctl.h> 42#include <sys/select.h> 43#include <sys/tty.h> 44#include <sys/proc.h> 45#include <sys/user.h> 46#include <sys/conf.h> 47#include <sys/file.h> 48#include <sys/uio.h> 49#include <sys/kernel.h> 50#include <sys/syslog.h> 51#include <sys/types.h> 52#include <sys/device.h> 53#include <sys/envsys.h> 54#include <sys/poll.h> 55 56#include <machine/apmvar.h> 57#include <machine/autoconf.h> 58#include <machine/cpu.h> 59#include <machine/bus.h> 60#include <machine/tctrl.h> 61 62#include <sparc/dev/ts102reg.h> 63#include <sparc/dev/tctrlvar.h> 64 65cdev_decl(tctrl); 66 67extern struct cfdriver tctrl_cd; 68 69static const char *tctrl_ext_statuses[16] = { 70 "main power available", 71 "internal battery attached", 72 "external battery attached", 73 "external VGA attached", 74 "external keyboard attached", 75 "external mouse attached", 76 "lid down", 77 "internal battery charging", 78 "external battery charging", 79 "internal battery discharging", 80 "external battery discharging", 81}; 82 83struct tctrl_softc { 84 struct device sc_dev; 85 bus_space_tag_t sc_memt; 86 bus_space_handle_t sc_memh; 87 unsigned int sc_junk; 88 unsigned int sc_ext_status; 89 unsigned int sc_flags; 90#define TCTRL_SEND_REQUEST 0x0001 91#define TCTRL_APM_CTLOPEN 0x0002 92 unsigned int sc_wantdata; 93 volatile unsigned short sc_lcdstate; 94 enum { TCTRL_IDLE, TCTRL_ARGS, 95 TCTRL_ACK, TCTRL_DATA } sc_state; 96 u_int8_t sc_cmdbuf[16]; 97 u_int8_t sc_rspbuf[16]; 98 u_int8_t sc_bitport; 99 u_int8_t sc_tft_on; 100 u_int8_t sc_op; 101 u_int8_t sc_cmdoff; 102 u_int8_t sc_cmdlen; 103 u_int8_t sc_rspoff; 104 u_int8_t sc_rsplen; 105 /* APM stuff */ 106#define APM_NEVENTS 16 107 struct apm_event_info sc_event_list[APM_NEVENTS]; 108 int sc_event_count; 109 int sc_event_ptr; 110 struct selinfo sc_rsel; 111 /* ENVSYS stuff */ 112#define ENVSYS_NUMSENSORS 3 113 struct envsys_sensor sc_esensors[ENVSYS_NUMSENSORS]; 114 115 struct evcnt sc_intrcnt; /* interrupt counting */ 116}; 117 118#define TCTRL_STD_DEV 0 119#define TCTRL_APMCTL_DEV 8 120 121static int tctrl_match __P((struct device *parent, struct cfdata *cf, 122 void *aux)); 123static void tctrl_attach __P((struct device *parent, struct device *self, 124 void *aux)); 125static void tctrl_write __P((struct tctrl_softc *sc, bus_size_t off, 126 u_int8_t v)); 127static u_int8_t tctrl_read __P((struct tctrl_softc *sc, bus_size_t off)); 128static void tctrl_write_data __P((struct tctrl_softc *sc, u_int8_t v)); 129static u_int8_t tctrl_read_data __P((struct tctrl_softc *sc)); 130static int tctrl_intr __P((void *arg)); 131static void tctrl_setup_bitport __P((void)); 132static void tctrl_setup_bitport_nop __P((void)); 133static void tctrl_read_ext_status __P((void)); 134static void tctrl_read_event_status __P((void *arg)); 135static int tctrl_apm_record_event __P((struct tctrl_softc *sc, 136 u_int event_type)); 137static void tctrl_init_lcd __P((void)); 138 139struct cfattach tctrl_ca = { 140 sizeof(struct tctrl_softc), tctrl_match, tctrl_attach 141}; 142 143extern struct cfdriver tctrl_cd; 144/* XXX wtf is this? see i386/apm.c */ 145int tctrl_apm_evindex; 146 147static int 148tctrl_match(parent, cf, aux) 149 struct device *parent; 150 struct cfdata *cf; 151 void *aux; 152{ 153 union obio_attach_args *uoba = aux; 154 struct sbus_attach_args *sa = &uoba->uoba_sbus; 155 156 if (uoba->uoba_isobio4 != 0) { 157 return (0); 158 } 159 160 /* Tadpole 3GX/3GS uses "uctrl" for the Tadpole Microcontroller 161 * (who's interface is off the TS102 PCMCIA controller but there 162 * exists a OpenProm for microcontroller interface). 163 */ 164 return strcmp("uctrl", sa->sa_name) == 0; 165} 166 167static void 168tctrl_attach(parent, self, aux) 169 struct device *parent; 170 struct device *self; 171 void *aux; 172{ 173 struct tctrl_softc *sc = (void *)self; 174 union obio_attach_args *uoba = aux; 175 struct sbus_attach_args *sa = &uoba->uoba_sbus; 176 unsigned int i, v; 177#if 0 178 unsigned int ack, msb, lsb; 179#endif 180 181 /* We're living on a sbus slot that looks like an obio that 182 * looks like an sbus slot. 183 */ 184 sc->sc_memt = sa->sa_bustag; 185 if (sbus_bus_map(sc->sc_memt, sa->sa_slot, 186 sa->sa_offset - TS102_REG_UCTRL_INT, sa->sa_size, 187 BUS_SPACE_MAP_LINEAR, 0, 188 &sc->sc_memh) != 0) { 189 printf(": can't map registers\n"); 190 return; 191 } 192 193 printf("\n"); 194 195 sc->sc_tft_on = 1; 196 197 /* clear any pending data. 198 */ 199 for (i = 0; i < 10000; i++) { 200 if ((TS102_UCTRL_STS_RXNE_STA & 201 tctrl_read(sc, TS102_REG_UCTRL_STS)) == 0) { 202 break; 203 } 204 v = tctrl_read(sc, TS102_REG_UCTRL_DATA); 205 tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA); 206 } 207 208 if (sa->sa_nintr != 0) { 209 (void)bus_intr_establish(sc->sc_memt, sa->sa_pri, 210 0, tctrl_intr, sc); 211 evcnt_attach(&sc->sc_dev, "intr", &sc->sc_intrcnt); 212 } 213 214 /* See what the external status is 215 */ 216 217 tctrl_read_ext_status(); 218 if (sc->sc_ext_status != 0) { 219 const char *sep; 220 221 printf("%s: ", sc->sc_dev.dv_xname); 222 v = sc->sc_ext_status; 223 for (i = 0, sep = ""; v != 0; i++, v >>= 1) { 224 if (v & 1) { 225 printf("%s%s", sep, tctrl_ext_statuses[i]); 226 sep = ", "; 227 } 228 } 229 printf("\n"); 230 } 231 232 /* Get a current of the control bitport; 233 */ 234 tctrl_setup_bitport_nop(); 235 tctrl_write(sc, TS102_REG_UCTRL_INT, 236 TS102_UCTRL_INT_RXNE_REQ|TS102_UCTRL_INT_RXNE_MSK); 237 238 sc->sc_wantdata = 0; 239 sc->sc_event_count = 0; 240 241 /* prime the sensor data */ 242 sprintf(sc->sc_esensors[0].desc, "%s", "Internal Unit Temperature"); 243 sc->sc_esensors[0].units = ENVSYS_STEMP; 244 sprintf(sc->sc_esensors[1].desc, "%s", "Internal Battery Voltage"); 245 sc->sc_esensors[1].units = ENVSYS_SVOLTS_DC; 246 sprintf(sc->sc_esensors[2].desc, "%s", "DC-In Voltage"); 247 sc->sc_esensors[2].units = ENVSYS_SVOLTS_DC; 248 249 /* initialize the LCD */ 250 tctrl_init_lcd(); 251 252 /* initialize sc_lcdstate */ 253 sc->sc_lcdstate = 0; 254 tctrl_set_lcd(2, 0); 255} 256 257static int 258tctrl_intr(arg) 259 void *arg; 260{ 261 struct tctrl_softc *sc = arg; 262 unsigned int v, d; 263 int progress = 0; 264 265 again: 266 /* find out the cause(s) of the interrupt */ 267 v = tctrl_read(sc, TS102_REG_UCTRL_STS); 268 269 /* clear the cause(s) of the interrupt */ 270 tctrl_write(sc, TS102_REG_UCTRL_STS, v); 271 272 v &= ~(TS102_UCTRL_STS_RXO_STA|TS102_UCTRL_STS_TXE_STA); 273 if (sc->sc_cmdoff >= sc->sc_cmdlen) { 274 v &= ~TS102_UCTRL_STS_TXNF_STA; 275 if (tctrl_read(sc, TS102_REG_UCTRL_INT) & TS102_UCTRL_INT_TXNF_REQ) { 276 tctrl_write(sc, TS102_REG_UCTRL_INT, 0); 277 progress = 1; 278 } 279 } 280 if ((v == 0) && ((sc->sc_flags & TCTRL_SEND_REQUEST) == 0 || 281 sc->sc_state != TCTRL_IDLE)) { 282 wakeup(sc); 283 return progress; 284 } 285 286 progress = 1; 287 if (v & TS102_UCTRL_STS_RXNE_STA) { 288 d = tctrl_read_data(sc); 289 switch (sc->sc_state) { 290 case TCTRL_IDLE: 291 if (d == 0xfa) { 292 /* external event */ 293 timeout(tctrl_read_event_status, (void *)0, 1); 294 } else { 295 printf("%s: (op=0x%02x): unexpected data (0x%02x)\n", 296 sc->sc_dev.dv_xname, sc->sc_op, d); 297 } 298 goto again; 299 case TCTRL_ACK: 300 if (d != 0xfe) { 301 printf("%s: (op=0x%02x): unexpected ack value (0x%02x)\n", 302 sc->sc_dev.dv_xname, sc->sc_op, d); 303 } 304#ifdef TCTRLDEBUG 305 printf(" ack=0x%02x", d); 306#endif 307 sc->sc_rsplen--; 308 sc->sc_rspoff = 0; 309 sc->sc_state = sc->sc_rsplen ? TCTRL_DATA : TCTRL_IDLE; 310 sc->sc_wantdata = sc->sc_rsplen ? 1 : 0; 311#ifdef TCTRLDEBUG 312 if (sc->sc_rsplen > 0) { 313 printf(" [data(%u)]", sc->sc_rsplen); 314 } else { 315 printf(" [idle]\n"); 316 } 317#endif 318 goto again; 319 case TCTRL_DATA: 320 sc->sc_rspbuf[sc->sc_rspoff++] = d; 321#ifdef TCTRLDEBUG 322 printf(" [%d]=0x%02x", sc->sc_rspoff-1, d); 323#endif 324 if (sc->sc_rspoff == sc->sc_rsplen) { 325#ifdef TCTRLDEBUG 326 printf(" [idle]\n"); 327#endif 328 sc->sc_state = TCTRL_IDLE; 329 sc->sc_wantdata = 0; 330 } 331 goto again; 332 default: 333 printf("%s: (op=0x%02x): unexpected data (0x%02x) in state %d\n", 334 sc->sc_dev.dv_xname, sc->sc_op, d, sc->sc_state); 335 goto again; 336 } 337 } 338 if ((sc->sc_state == TCTRL_IDLE && sc->sc_wantdata == 0) || 339 sc->sc_flags & TCTRL_SEND_REQUEST) { 340 if (sc->sc_flags & TCTRL_SEND_REQUEST) { 341 sc->sc_flags &= ~TCTRL_SEND_REQUEST; 342 sc->sc_wantdata = 1; 343 } 344 if (sc->sc_cmdlen > 0) { 345 tctrl_write(sc, TS102_REG_UCTRL_INT, 346 tctrl_read(sc, TS102_REG_UCTRL_INT) 347 |TS102_UCTRL_INT_TXNF_MSK 348 |TS102_UCTRL_INT_TXNF_REQ); 349 v = tctrl_read(sc, TS102_REG_UCTRL_STS); 350 } 351 } 352 if ((sc->sc_cmdoff < sc->sc_cmdlen) && (v & TS102_UCTRL_STS_TXNF_STA)) { 353 tctrl_write_data(sc, sc->sc_cmdbuf[sc->sc_cmdoff++]); 354#ifdef TCTRLDEBUG 355 if (sc->sc_cmdoff == 1) { 356 printf("%s: op=0x%02x(l=%u)", sc->sc_dev.dv_xname, 357 sc->sc_cmdbuf[0], sc->sc_rsplen); 358 } else { 359 printf(" [%d]=0x%02x", sc->sc_cmdoff-1, 360 sc->sc_cmdbuf[sc->sc_cmdoff-1]); 361 } 362#endif 363 if (sc->sc_cmdoff == sc->sc_cmdlen) { 364 sc->sc_state = sc->sc_rsplen ? TCTRL_ACK : TCTRL_IDLE; 365#ifdef TCTRLDEBUG 366 printf(" %s", sc->sc_rsplen ? "[ack]" : "[idle]\n"); 367#endif 368 if (sc->sc_cmdoff == 1) { 369 sc->sc_op = sc->sc_cmdbuf[0]; 370 } 371 tctrl_write(sc, TS102_REG_UCTRL_INT, 372 tctrl_read(sc, TS102_REG_UCTRL_INT) 373 & (~TS102_UCTRL_INT_TXNF_MSK 374 |TS102_UCTRL_INT_TXNF_REQ)); 375 } else if (sc->sc_state == TCTRL_IDLE) { 376 sc->sc_op = sc->sc_cmdbuf[0]; 377 sc->sc_state = TCTRL_ARGS; 378#ifdef TCTRLDEBUG 379 printf(" [args]"); 380#endif 381 } 382 } 383 goto again; 384} 385 386static void 387tctrl_setup_bitport_nop(void) 388{ 389 struct tctrl_softc *sc; 390 struct tctrl_req req; 391 int s; 392 393 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 394 req.cmdbuf[0] = TS102_OP_CTL_BITPORT; 395 req.cmdbuf[1] = 0xff; 396 req.cmdbuf[2] = 0; 397 req.cmdlen = 3; 398 req.rsplen = 2; 399 req.p = NULL; 400 tadpole_request(&req, 1); 401 s = splts102(); 402 sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2]; 403 splx(s); 404} 405 406static void 407tctrl_setup_bitport(void) 408{ 409 struct tctrl_softc *sc; 410 struct tctrl_req req; 411 int s; 412 413 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 414 s = splts102(); 415 if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) 416 || (!sc->sc_tft_on)) { 417 req.cmdbuf[2] = TS102_BITPORT_TFTPWR; 418 } else { 419 req.cmdbuf[2] = 0; 420 } 421 req.cmdbuf[0] = TS102_OP_CTL_BITPORT; 422 req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR; 423 req.cmdlen = 3; 424 req.rsplen = 2; 425 req.p = NULL; 426 tadpole_request(&req, 1); 427 s = splts102(); 428 sc->sc_bitport = (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2]; 429 splx(s); 430} 431 432/* 433 * The tadpole microcontroller is not preprogrammed with icon 434 * representations. The machine boots with the DC-IN light as 435 * a blank (all 0x00) and the other lights, as 4 rows of horizontal 436 * bars. The below code initializes the icons in the system to 437 * sane values. Some of these icons could be used for any purpose 438 * desired, namely the pcmcia, LAN and WAN lights. For the disk spinner, 439 * only the backslash is unprogrammed. (sigh) 440 * 441 * programming the icons is simple. It is a 5x8 matrix, which each row a 442 * bitfield in the order 0x10 0x08 0x04 0x02 0x01. 443 */ 444 445static void 446tctrl_init_lcd(void) 447{ 448 struct tctrl_req req; 449 450 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 451 req.cmdlen = 11; 452 req.rsplen = 1; 453 req.cmdbuf[1] = 0x08; /*len*/ 454 req.cmdbuf[2] = TS102_BLK_OFF_DEF_DC_GOOD; 455 req.cmdbuf[3] = 0x00; /* ..... */ 456 req.cmdbuf[4] = 0x00; /* ..... */ 457 req.cmdbuf[5] = 0x1f; /* XXXXX */ 458 req.cmdbuf[6] = 0x00; /* ..... */ 459 req.cmdbuf[7] = 0x15; /* X.X.X */ 460 req.cmdbuf[8] = 0x00; /* ..... */ 461 req.cmdbuf[9] = 0x00; /* ..... */ 462 req.cmdbuf[10] = 0x00; /* ..... */ 463 req.p = NULL; 464 tadpole_request(&req, 1); 465 466 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 467 req.cmdlen = 11; 468 req.rsplen = 1; 469 req.cmdbuf[1] = 0x08; /*len*/ 470 req.cmdbuf[2] = TS102_BLK_OFF_DEF_BACKSLASH; 471 req.cmdbuf[3] = 0x00; /* ..... */ 472 req.cmdbuf[4] = 0x10; /* X.... */ 473 req.cmdbuf[5] = 0x08; /* .X... */ 474 req.cmdbuf[6] = 0x04; /* ..X.. */ 475 req.cmdbuf[7] = 0x02; /* ...X. */ 476 req.cmdbuf[8] = 0x01; /* ....X */ 477 req.cmdbuf[9] = 0x00; /* ..... */ 478 req.cmdbuf[10] = 0x00; /* ..... */ 479 req.p = NULL; 480 tadpole_request(&req, 1); 481 482 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 483 req.cmdlen = 11; 484 req.rsplen = 1; 485 req.cmdbuf[1] = 0x08; /*len*/ 486 req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN1; 487 req.cmdbuf[3] = 0x0c; /* .XXX. */ 488 req.cmdbuf[4] = 0x16; /* X.XX. */ 489 req.cmdbuf[5] = 0x10; /* X.... */ 490 req.cmdbuf[6] = 0x15; /* X.X.X */ 491 req.cmdbuf[7] = 0x10; /* X.... */ 492 req.cmdbuf[8] = 0x16; /* X.XX. */ 493 req.cmdbuf[9] = 0x0c; /* .XXX. */ 494 req.cmdbuf[10] = 0x00; /* ..... */ 495 req.p = NULL; 496 tadpole_request(&req, 1); 497 498 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 499 req.cmdlen = 11; 500 req.rsplen = 1; 501 req.cmdbuf[1] = 0x08; /*len*/ 502 req.cmdbuf[2] = TS102_BLK_OFF_DEF_WAN2; 503 req.cmdbuf[3] = 0x0c; /* .XXX. */ 504 req.cmdbuf[4] = 0x0d; /* .XX.X */ 505 req.cmdbuf[5] = 0x01; /* ....X */ 506 req.cmdbuf[6] = 0x15; /* X.X.X */ 507 req.cmdbuf[7] = 0x01; /* ....X */ 508 req.cmdbuf[8] = 0x0d; /* .XX.X */ 509 req.cmdbuf[9] = 0x0c; /* .XXX. */ 510 req.cmdbuf[10] = 0x00; /* ..... */ 511 req.p = NULL; 512 tadpole_request(&req, 1); 513 514 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 515 req.cmdlen = 11; 516 req.rsplen = 1; 517 req.cmdbuf[1] = 0x08; /*len*/ 518 req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN1; 519 req.cmdbuf[3] = 0x00; /* ..... */ 520 req.cmdbuf[4] = 0x04; /* ..X.. */ 521 req.cmdbuf[5] = 0x08; /* .X... */ 522 req.cmdbuf[6] = 0x13; /* X..XX */ 523 req.cmdbuf[7] = 0x08; /* .X... */ 524 req.cmdbuf[8] = 0x04; /* ..X.. */ 525 req.cmdbuf[9] = 0x00; /* ..... */ 526 req.cmdbuf[10] = 0x00; /* ..... */ 527 req.p = NULL; 528 tadpole_request(&req, 1); 529 530 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 531 req.cmdlen = 11; 532 req.rsplen = 1; 533 req.cmdbuf[1] = 0x08; /*len*/ 534 req.cmdbuf[2] = TS102_BLK_OFF_DEF_LAN2; 535 req.cmdbuf[3] = 0x00; /* ..... */ 536 req.cmdbuf[4] = 0x04; /* ..X.. */ 537 req.cmdbuf[5] = 0x02; /* ...X. */ 538 req.cmdbuf[6] = 0x19; /* XX..X */ 539 req.cmdbuf[7] = 0x02; /* ...X. */ 540 req.cmdbuf[8] = 0x04; /* ..X.. */ 541 req.cmdbuf[9] = 0x00; /* ..... */ 542 req.cmdbuf[10] = 0x00; /* ..... */ 543 req.p = NULL; 544 tadpole_request(&req, 1); 545 546 req.cmdbuf[0] = TS102_OP_BLK_DEF_SPCL_CHAR; 547 req.cmdlen = 11; 548 req.rsplen = 1; 549 req.cmdbuf[1] = 0x08; /*len*/ 550 req.cmdbuf[2] = TS102_BLK_OFF_DEF_PCMCIA; 551 req.cmdbuf[3] = 0x00; /* ..... */ 552 req.cmdbuf[4] = 0x0c; /* .XXX. */ 553 req.cmdbuf[5] = 0x1f; /* XXXXX */ 554 req.cmdbuf[6] = 0x1f; /* XXXXX */ 555 req.cmdbuf[7] = 0x1f; /* XXXXX */ 556 req.cmdbuf[8] = 0x1f; /* XXXXX */ 557 req.cmdbuf[9] = 0x00; /* ..... */ 558 req.cmdbuf[10] = 0x00; /* ..... */ 559 req.p = NULL; 560 tadpole_request(&req, 1); 561} 562 563 564 565/* 566 * set the blinken-lights on the lcd. what: 567 * what = 0 off, what = 1 on, what = 2 toggle 568 */ 569 570void 571tctrl_set_lcd(what, which) 572 int what; 573 unsigned short which; 574{ 575 struct tctrl_softc *sc; 576 struct tctrl_req req; 577 int s; 578 579 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 580 s = splts102(); 581 582 /* provide a quick exit to save cpu time */ 583 if ((what == 1 && sc->sc_lcdstate & which) || 584 (what == 0 && !(sc->sc_lcdstate & which))) { 585 splx(s); 586 return; 587 } 588 /* 589 * the mask setup on this particular command is *very* bizzare 590 * and totally undocumented. 591 */ 592 if ((what == 1) || (what == 2 && !(sc->sc_lcdstate & which))) { 593 req.cmdbuf[2] = (u_int8_t)(which&0xff); 594 req.cmdbuf[3] = (u_int8_t)(which>>8); 595 } else { 596 req.cmdbuf[2] = 0; 597 req.cmdbuf[3] = 0; 598 } 599 req.cmdbuf[0] = TS102_OP_CTL_LCD; 600 req.cmdbuf[4] = (u_int8_t)(~which>>8); 601 req.cmdbuf[1] = (u_int8_t)(~which&0xff); 602 603 /* XXX this thing is wierd.... */ 604 req.cmdlen = 3; 605 req.rsplen = 2; 606#if 0 607 req.cmdlen = 5; 608 req.rsplen = 4; 609#endif 610 req.p = NULL; 611 tadpole_request(&req, 1); 612 s = splts102(); 613 sc->sc_lcdstate = (unsigned short)req.rspbuf[0]; 614 splx(s); 615} 616 617static void 618tctrl_read_ext_status(void) 619{ 620 struct tctrl_softc *sc; 621 struct tctrl_req req; 622 int s; 623 624 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 625 req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS; 626 req.cmdlen = 1; 627 req.rsplen = 3; 628 req.p = NULL; 629#ifdef TCTRLDEBUG 630 printf("pre read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status); 631#endif 632 tadpole_request(&req, 1); 633 s = splts102(); 634 sc->sc_ext_status = req.rspbuf[0] * 256 + req.rspbuf[1]; 635 splx(s); 636#ifdef TCTRLDEBUG 637 printf("post read: sc->sc_ext_status = 0x%x\n", sc->sc_ext_status); 638#endif 639} 640 641/* 642 * return 0 if the user will notice and handle the event, 643 * return 1 if the kernel driver should do so. 644 */ 645static int 646tctrl_apm_record_event(sc, event_type) 647 struct tctrl_softc *sc; 648 u_int event_type; 649{ 650 struct apm_event_info *evp; 651 652 if ((sc->sc_flags & TCTRL_APM_CTLOPEN) && 653 (sc->sc_event_count < APM_NEVENTS)) { 654 evp = &sc->sc_event_list[sc->sc_event_ptr]; 655 sc->sc_event_count++; 656 sc->sc_event_ptr++; 657 sc->sc_event_ptr %= APM_NEVENTS; 658 evp->type = event_type; 659 evp->index = ++tctrl_apm_evindex; 660 selwakeup(&sc->sc_rsel); 661 return(sc->sc_flags & TCTRL_APM_CTLOPEN) ? 0 : 1; 662 } 663 return(1); 664} 665 666static void 667tctrl_read_event_status(arg) 668 void *arg; 669{ 670 struct tctrl_softc *sc; 671 struct tctrl_req req; 672 int s; 673 unsigned int v; 674 675 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 676 req.cmdbuf[0] = TS102_OP_RD_EVENT_STATUS; 677 req.cmdlen = 1; 678 req.rsplen = 3; 679 req.p = NULL; 680 tadpole_request(&req, 1); 681 s = splts102(); 682 v = req.rspbuf[0] * 256 + req.rspbuf[1]; 683 if (v & TS102_EVENT_STATUS_SHUTDOWN_REQUEST) { 684 printf("%s: SHUTDOWN REQUEST!\n", sc->sc_dev.dv_xname); 685 } 686 if (v & TS102_EVENT_STATUS_VERY_LOW_POWER_WARNING) { 687/*printf("%s: VERY LOW POWER WARNING!\n", sc->sc_dev.dv_xname);*/ 688/* according to a tadpole header, and observation */ 689#ifdef TCTRLDEBUG 690 printf("%s: Battery charge level change\n", sc->sc_dev.dv_xname); 691#endif 692 } 693 if (v & TS102_EVENT_STATUS_LOW_POWER_WARNING) { 694 if (tctrl_apm_record_event(sc, APM_BATTERY_LOW)) 695 printf("%s: LOW POWER WARNING!\n", sc->sc_dev.dv_xname); 696 } 697 if (v & TS102_EVENT_STATUS_DC_STATUS_CHANGE) { 698 splx(s); 699 tctrl_read_ext_status(); 700 s = splts102(); 701 if (tctrl_apm_record_event(sc, APM_POWER_CHANGE)) 702 printf("%s: main power %s\n", sc->sc_dev.dv_xname, 703 (sc->sc_ext_status & 704 TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) ? 705 "restored" : "removed"); 706 } 707 if (v & TS102_EVENT_STATUS_LID_STATUS_CHANGE) { 708 splx(s); 709 tctrl_read_ext_status(); 710 tctrl_setup_bitport(); 711#ifdef TCTRLDEBUG 712 printf("%s: lid %s\n", sc->sc_dev.dv_xname, 713 (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) 714 ? "closed" : "opened"); 715#endif 716 } 717 splx(s); 718} 719 720void 721tadpole_request(req, spin) 722 struct tctrl_req *req; 723 int spin; 724{ 725 struct tctrl_softc *sc; 726 int i, s; 727 728 if (tctrl_cd.cd_devs == NULL 729 || tctrl_cd.cd_ndevs == 0 730 || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) { 731 return; 732 } 733 734 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 735 while (sc->sc_wantdata != 0) { 736 if (req->p != NULL) 737 tsleep(&sc->sc_wantdata, PLOCK, "tctrl_lock", 10); 738 else 739 DELAY(1); 740 } 741 if (spin) 742 s = splhigh(); 743 else 744 s = splts102(); 745 sc->sc_flags |= TCTRL_SEND_REQUEST; 746 memcpy(sc->sc_cmdbuf, req->cmdbuf, req->cmdlen); 747 sc->sc_wantdata = 1; 748 sc->sc_rsplen = req->rsplen; 749 sc->sc_cmdlen = req->cmdlen; 750 sc->sc_cmdoff = sc->sc_rspoff = 0; 751 752 /* we spin for certain commands, like poweroffs */ 753 if (spin) { 754/* for (i = 0; i < 30000; i++) {*/ 755 while (sc->sc_wantdata == 1) { 756 tctrl_intr(sc); 757 DELAY(1); 758 } 759 } else { 760 tctrl_intr(sc); 761 i = 0; 762 while (((sc->sc_rspoff != sc->sc_rsplen) || 763 (sc->sc_cmdoff != sc->sc_cmdlen)) && 764 (i < (5 * sc->sc_rsplen + sc->sc_cmdlen))) 765 if (req->p != NULL) { 766 tsleep(sc, PWAIT, "tctrl_data", 15); 767 i++; 768 } 769 else 770 DELAY(1); 771 } 772 /* 773 * we give the user a reasonable amount of time for a command 774 * to complete. If it doesn't complete in time, we hand them 775 * garbage. This is here to stop things like setting the 776 * rsplen too long, and sleeping forever in a CMD_REQ ioctl. 777 */ 778 sc->sc_wantdata = 0; 779 memcpy(req->rspbuf, sc->sc_rspbuf, req->rsplen); 780 splx(s); 781} 782 783void 784tadpole_powerdown(void) 785{ 786 struct tctrl_req req; 787 788 req.cmdbuf[0] = TS102_OP_ADMIN_POWER_OFF; 789 req.cmdlen = 1; 790 req.rsplen = 1; 791 req.p = NULL; 792 tadpole_request(&req, 1); 793} 794 795void 796tadpole_set_video(enabled) 797 int enabled; 798{ 799 struct tctrl_softc *sc; 800 struct tctrl_req req; 801 int s; 802 803 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 804 while (sc->sc_wantdata != 0) 805 DELAY(1); 806 s = splts102(); 807 req.p = NULL; 808 if ((sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN && !enabled) 809 || (sc->sc_tft_on)) { 810 req.cmdbuf[2] = TS102_BITPORT_TFTPWR; 811 } else { 812 req.cmdbuf[2] = 0; 813 } 814 req.cmdbuf[0] = TS102_OP_CTL_BITPORT; 815 req.cmdbuf[1] = ~TS102_BITPORT_TFTPWR; 816 req.cmdlen = 3; 817 req.rsplen = 2; 818 819 if ((sc->sc_tft_on && !enabled) || (!sc->sc_tft_on && enabled)) { 820 sc->sc_tft_on = enabled; 821 if (sc->sc_ext_status & TS102_EXT_STATUS_LID_DOWN) { 822 splx(s); 823 return; 824 } 825 tadpole_request(&req, 1); 826 sc->sc_bitport = 827 (req.rspbuf[0] & req.cmdbuf[1]) ^ req.cmdbuf[2]; 828 } 829 splx(s); 830} 831 832static void 833tctrl_write_data(sc, v) 834 struct tctrl_softc *sc; 835 u_int8_t v; 836{ 837 unsigned int i; 838 839 for (i = 0; i < 100; i++) { 840 if (TS102_UCTRL_STS_TXNF_STA & tctrl_read(sc, TS102_REG_UCTRL_STS)) 841 break; 842 } 843 tctrl_write(sc, TS102_REG_UCTRL_DATA, v); 844} 845 846static u_int8_t 847tctrl_read_data(sc) 848 struct tctrl_softc *sc; 849{ 850 unsigned int i, v; 851 852 for (i = 0; i < 100000; i++) { 853 if (TS102_UCTRL_STS_RXNE_STA & tctrl_read(sc, TS102_REG_UCTRL_STS)) 854 break; 855 DELAY(1); 856 } 857 858 v = tctrl_read(sc, TS102_REG_UCTRL_DATA); 859 tctrl_write(sc, TS102_REG_UCTRL_STS, TS102_UCTRL_STS_RXNE_STA); 860 return v; 861} 862 863static u_int8_t 864tctrl_read(sc, off) 865 struct tctrl_softc *sc; 866 bus_size_t off; 867{ 868 869 sc->sc_junk = bus_space_read_1(sc->sc_memt, sc->sc_memh, off); 870 return sc->sc_junk; 871} 872 873static void 874tctrl_write(sc, off, v) 875 struct tctrl_softc *sc; 876 bus_size_t off; 877 u_int8_t v; 878{ 879 880 sc->sc_junk = v; 881 bus_space_write_1(sc->sc_memt, sc->sc_memh, off, v); 882} 883 884int 885tctrlopen(dev, flags, mode, p) 886 dev_t dev; 887 int flags, mode; 888 struct proc *p; 889{ 890 int unit = (minor(dev)&0xf0); 891 int ctl = (minor(dev)&0x0f); 892 struct tctrl_softc *sc; 893 894 if (unit >= tctrl_cd.cd_ndevs) 895 return(ENXIO); 896 sc = tctrl_cd.cd_devs[TCTRL_STD_DEV]; 897 if (!sc) 898 return(ENXIO); 899 900 switch (ctl) { 901 case TCTRL_STD_DEV: 902 break; 903 case TCTRL_APMCTL_DEV: 904 if (!(flags & FWRITE)) 905 return(EINVAL); 906 if (sc->sc_flags & TCTRL_APM_CTLOPEN) 907 return(EBUSY); 908 sc->sc_flags |= TCTRL_APM_CTLOPEN; 909 break; 910 default: 911 return(ENXIO); 912 break; 913 } 914 915 return(0); 916} 917 918int 919tctrlclose(dev, flags, mode, p) 920 dev_t dev; 921 int flags, mode; 922 struct proc *p; 923{ 924 int ctl = (minor(dev)&0x0f); 925 struct tctrl_softc *sc; 926 927 sc = tctrl_cd.cd_devs[TCTRL_STD_DEV]; 928 if (!sc) 929 return(ENXIO); 930 931 switch (ctl) { 932 case TCTRL_STD_DEV: 933 break; 934 case TCTRL_APMCTL_DEV: 935 sc->sc_flags &= ~TCTRL_APM_CTLOPEN; 936 break; 937 } 938 return(0); 939} 940 941int 942tctrlioctl(dev, cmd, data, flags, p) 943 dev_t dev; 944 u_long cmd; 945 caddr_t data; 946 int flags; 947 struct proc *p; 948{ 949 struct tctrl_req req, *reqn; 950 envsys_range_t *envrange; 951 envsys_temp_data_t *envdata; 952 envsys_temp_info_t *envinfo; 953 struct apm_power_info *powerp; 954 struct apm_event_info *evp; 955 struct tctrl_softc *sc; 956 int i; 957 u_int j; 958 u_int16_t a; 959 u_int8_t c; 960 961 if (tctrl_cd.cd_devs == NULL 962 || tctrl_cd.cd_ndevs == 0 963 || tctrl_cd.cd_devs[TCTRL_STD_DEV] == NULL) { 964 return ENXIO; 965 } 966 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 967 switch (cmd) { 968 969 case APM_IOC_STANDBY: 970 return(EOPNOTSUPP); /* for now */ 971 972 case APM_IOC_SUSPEND: 973 return(EOPNOTSUPP); /* for now */ 974 975 case APM_IOC_GETPOWER: 976 powerp = (struct apm_power_info *)data; 977 req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_RATE; 978 req.cmdlen = 1; 979 req.rsplen = 2; 980 req.p = p; 981 tadpole_request(&req, 0); 982 if (req.rspbuf[0] > 0x00) 983 powerp->battery_state = APM_BATT_CHARGING; 984 req.cmdbuf[0] = TS102_OP_RD_INT_CHARGE_LEVEL; 985 req.cmdlen = 1; 986 req.rsplen = 3; 987 req.p = p; 988 tadpole_request(&req, 0); 989 c = req.rspbuf[0]; 990 powerp->battery_life = c; 991 if (c > 0x70) /* the tadpole sometimes dips below zero, and */ 992 c = 0; /* into the 255 range. */ 993 powerp->minutes_left = (45 * c) / 100; /* XXX based on 45 min */ 994 if (powerp->battery_state != APM_BATT_CHARGING) { 995 if (c < 0x20) 996 powerp->battery_state = APM_BATT_CRITICAL; 997 else if (c < 0x40) 998 powerp->battery_state = APM_BATT_LOW; 999 else if (c < 0x66) 1000 powerp->battery_state = APM_BATT_HIGH; 1001 else 1002 powerp->battery_state = APM_BATT_UNKNOWN; 1003 } 1004 req.cmdbuf[0] = TS102_OP_RD_EXT_STATUS; 1005 req.cmdlen = 1; 1006 req.rsplen = 3; 1007 req.p = p; 1008 tadpole_request(&req, 0); 1009 a = req.rspbuf[0] * 256 + req.rspbuf[1]; 1010 if (a & TS102_EXT_STATUS_MAIN_POWER_AVAILABLE) 1011 powerp->ac_state = APM_AC_ON; 1012 else 1013 powerp->ac_state = APM_AC_OFF; 1014 break; 1015 1016 case APM_IOC_NEXTEVENT: 1017 if (!sc->sc_event_count) 1018 return EAGAIN; 1019 1020 evp = (struct apm_event_info *)data; 1021 i = sc->sc_event_ptr + APM_NEVENTS - sc->sc_event_count; 1022 i %= APM_NEVENTS; 1023 *evp = sc->sc_event_list[i]; 1024 sc->sc_event_count--; 1025 return(0); 1026 1027 /* this ioctl assumes the caller knows exactly what he is doing */ 1028 case TCTRL_CMD_REQ: 1029 reqn = (struct tctrl_req *)data; 1030 if ((i = suser(p->p_ucred, &p->p_acflag)) != 0 && 1031 (reqn->cmdbuf[0] == TS102_OP_CTL_BITPORT || 1032 (reqn->cmdbuf[0] >= TS102_OP_CTL_WATCHDOG && 1033 reqn->cmdbuf[0] <= TS102_OP_CTL_SECURITY_KEY) || 1034 reqn->cmdbuf[0] == TS102_OP_CTL_TIMEZONE || 1035 reqn->cmdbuf[0] == TS102_OP_CTL_DIAGNOSTIC_MODE || 1036 reqn->cmdbuf[0] == TS102_OP_CMD_SOFTWARE_RESET || 1037 (reqn->cmdbuf[0] >= TS102_OP_CMD_SET_RTC && 1038 reqn->cmdbuf[0] < TS102_OP_RD_INT_CHARGE_LEVEL) || 1039 reqn->cmdbuf[0] > TS102_OP_RD_EXT_CHARGE_LEVEL)) 1040 return(i); 1041 reqn->p = p; 1042 tadpole_request(reqn, 0); 1043 break; 1044 1045 case ENVSYS_VERSION: 1046 *(int32_t *)data = 1000; 1047 break; 1048 1049 case ENVSYS_GRANGE: 1050 envrange = (envsys_range_t *)data; 1051 i = 0; 1052 envrange->high = envrange->low = 0; 1053 for (j=0; j < ENVSYS_NUMSENSORS; j++) { 1054 if (!i && envrange->units == sc->sc_esensors[j].units) { 1055 envrange->low = j; 1056 i++; 1057 } 1058 if (i && envrange->units == sc->sc_esensors[j].units) 1059 envrange->high = j; 1060 } 1061 if (!i) { 1062 envrange->high = 0; 1063 envrange->low = 1; 1064 } 1065 break; 1066 1067 case ENVSYS_GTREDATA: 1068 envdata = (envsys_temp_data_t *)data; 1069 if (envdata->sensor >= ENVSYS_NUMSENSORS) { 1070 envdata->validflags = 0; 1071 break; 1072 } 1073 envdata->warnflags = ENVSYS_WARN_OK; 1074 if (envdata->sensor == 0) { 1075 envdata->validflags |= ENVSYS_FVALID; 1076 req.cmdbuf[0] = TS102_OP_RD_CURRENT_TEMP; 1077 req.cmdlen = 1; 1078 req.rsplen = 2; 1079 req.p = p; 1080 tadpole_request(&req, 0); 1081 envdata->cur.data_us = /* 273160? */ 1082 (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000); 1083 envdata->validflags |= ENVSYS_FCURVALID; 1084 req.cmdbuf[0] = TS102_OP_RD_MAX_TEMP; 1085 req.cmdlen = 1; 1086 req.rsplen = 2; 1087 req.p = p; 1088 tadpole_request(&req, 0); 1089 envdata->max.data_us = 1090 (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000); 1091 envdata->validflags |= ENVSYS_FMAXVALID; 1092 req.cmdbuf[0] = TS102_OP_RD_MIN_TEMP; 1093 req.cmdlen = 1; 1094 req.rsplen = 2; 1095 req.p = p; 1096 tadpole_request(&req, 0); 1097 envdata->min.data_us = 1098 (u_int32_t)((int)((int)req.rspbuf[0]-32)*5000000/9+273150000); 1099 envdata->validflags |= ENVSYS_FMINVALID; 1100 envdata->units = sc->sc_esensors[envdata->sensor].units; 1101 break; 1102 } else if (envdata->sensor == 1 || envdata->sensor == 2) { 1103 envdata->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID; 1104 envdata->units = sc->sc_esensors[envdata->sensor].units; 1105 if (envdata->sensor == 1) 1106 req.cmdbuf[0] = TS102_OP_RD_INT_BATT_VLT; 1107 else 1108 req.cmdbuf[0] = TS102_OP_RD_DC_IN_VLT; 1109 req.cmdlen = 1; 1110 req.rsplen = 2; 1111 req.p = p; 1112 tadpole_request(&req, 0); 1113 envdata->cur.data_s = (int32_t)req.rspbuf[0]*1000000/11; 1114 break; 1115 } 1116 break; 1117 1118 case ENVSYS_GTREINFO: 1119 envinfo = (envsys_temp_info_t *)data; 1120 if (envinfo->sensor >= ENVSYS_NUMSENSORS) { 1121 envinfo->validflags = 0; 1122 break; 1123 } 1124 envinfo->units = sc->sc_esensors[envinfo->sensor].units; 1125 memcpy(envinfo->desc, sc->sc_esensors[envinfo->sensor].desc, 1126 sizeof(sc->sc_esensors[envinfo->sensor].desc) > 1127 sizeof(envinfo->desc) ? sizeof(envinfo->desc) : 1128 sizeof(sc->sc_esensors[envinfo->sensor].desc)); 1129 if (envinfo->units == ENVSYS_STEMP) { 1130 envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID| 1131 ENVSYS_FMINVALID|ENVSYS_FMAXVALID; 1132 } else if (envinfo->units == ENVSYS_SVOLTS_DC) { 1133 envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID; 1134 } else 1135 envinfo->validflags = 0; 1136 break; 1137 1138 case ENVSYS_STREINFO: 1139 envinfo = (envsys_temp_info_t *)data; 1140 if (envinfo->sensor >= ENVSYS_NUMSENSORS) { 1141 envinfo->validflags = 0; 1142 break; 1143 } 1144 if (envinfo->units == sc->sc_esensors[envinfo->sensor].units) 1145 memcpy(sc->sc_esensors[envinfo->sensor].desc, 1146 envinfo->desc, 1147 sizeof(envinfo->desc) > sizeof(char)*32 ? 1148 sizeof(char)*32 : sizeof(envinfo->desc) ); 1149 if (envinfo->units == ENVSYS_STEMP) { 1150 envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID| 1151 ENVSYS_FMINVALID|ENVSYS_FMAXVALID; 1152 } else if (envinfo->units == ENVSYS_SVOLTS_DC) { 1153 envinfo->validflags = ENVSYS_FVALID|ENVSYS_FCURVALID; 1154 } else 1155 envinfo->validflags = 0; 1156 break; 1157 1158 1159 default: 1160 return (ENOTTY); 1161 } 1162 return (0); 1163} 1164 1165int 1166tctrlpoll(dev, events, p) 1167 dev_t dev; 1168 int events; 1169 struct proc *p; 1170{ 1171 struct tctrl_softc *sc = tctrl_cd.cd_devs[TCTRL_STD_DEV]; 1172 int revents = 0; 1173 1174 if (events & (POLLIN | POLLRDNORM)) { 1175 if (sc->sc_event_count) 1176 revents |= events & (POLLIN | POLLRDNORM); 1177 else 1178 selrecord(p, &sc->sc_rsel); 1179 } 1180 1181 return (revents); 1182} 1183/* DO NOT SET THIS OPTION */ 1184#ifdef TADPOLE_BLINK 1185void 1186cpu_disk_unbusy(busy) 1187 int busy; 1188{ 1189 static struct timeval tctrl_ds_timestamp; 1190 struct timeval dv_time, diff_time; 1191 struct tctrl_softc *sc; 1192 1193 sc = (struct tctrl_softc *) tctrl_cd.cd_devs[TCTRL_STD_DEV]; 1194 1195 /* quickly bail */ 1196 if (!(sc->sc_lcdstate & TS102_LCD_DISK_ACTIVE) || busy > 0) 1197 return; 1198 1199 /* we aren't terribly concerned with precision here */ 1200 dv_time = mono_time; 1201 timersub(&dv_time, &tctrl_ds_timestamp, &diff_time); 1202 1203 if (diff_time.tv_sec > 0) { 1204 tctrl_set_lcd(0, TS102_LCD_DISK_ACTIVE); 1205 tctrl_ds_timestamp = mono_time; 1206 } 1207} 1208#endif 1209