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