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
65 cdev_decl(tctrl);
66
67 extern struct cfdriver tctrl_cd;
68
69 static 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
83 struct 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
121 static int tctrl_match __P((struct device *parent, struct cfdata *cf,
122 void *aux));
123 static void tctrl_attach __P((struct device *parent, struct device *self,
124 void *aux));
125 static void tctrl_write __P((struct tctrl_softc *sc, bus_size_t off,
126 u_int8_t v));
127 static u_int8_t tctrl_read __P((struct tctrl_softc *sc, bus_size_t off));
128 static void tctrl_write_data __P((struct tctrl_softc *sc, u_int8_t v));
129 static u_int8_t tctrl_read_data __P((struct tctrl_softc *sc));
130 static int tctrl_intr __P((void *arg));
131 static void tctrl_setup_bitport __P((void));
132 static void tctrl_setup_bitport_nop __P((void));
133 static void tctrl_read_ext_status __P((void));
134 static void tctrl_read_event_status __P((void *arg));
135 static int tctrl_apm_record_event __P((struct tctrl_softc *sc,
136 u_int event_type));
137 static void tctrl_init_lcd __P((void));
138
139 struct cfattach tctrl_ca = {
140 sizeof(struct tctrl_softc), tctrl_match, tctrl_attach
141 };
142
143 extern struct cfdriver tctrl_cd;
144 /* XXX wtf is this? see i386/apm.c */
145 int tctrl_apm_evindex;
146
147 static int
148 tctrl_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
167 static void
168 tctrl_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
257 static int
258 tctrl_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
386 static void
387 tctrl_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
406 static void
407 tctrl_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
445 static void
446 tctrl_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
570 void
571 tctrl_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
617 static void
618 tctrl_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 */
645 static int
646 tctrl_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
666 static void
667 tctrl_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
720 void
721 tadpole_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
783 void
784 tadpole_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
795 void
796 tadpole_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
832 static void
833 tctrl_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
846 static u_int8_t
847 tctrl_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
863 static u_int8_t
864 tctrl_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
873 static void
874 tctrl_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
884 int
885 tctrlopen(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
918 int
919 tctrlclose(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
941 int
942 tctrlioctl(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
1165 int
1166 tctrlpoll(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
1185 void
1186 cpu_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