xref: /openbsd/sys/arch/loongson/dev/apm.c (revision 891d7ab6)
1 /*	$OpenBSD: apm.c,v 1.9 2011/07/02 22:20:07 nicm Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 Alexander Guy.  All rights reserved.
5  * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
6  * Copyright (c) 1995 John T. Kohl.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the names of the authors nor the names of contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  */
33 
34 #include "apm.h"
35 #include "wsdisplay.h"
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/proc.h>
41 #include <sys/device.h>
42 #include <sys/fcntl.h>
43 #include <sys/ioctl.h>
44 #include <sys/buf.h>
45 #include <sys/event.h>
46 
47 #include <machine/autoconf.h>
48 #include <machine/conf.h>
49 #include <machine/cpu.h>
50 #include <machine/apmvar.h>
51 
52 #include <dev/wscons/wsdisplayvar.h>
53 
54 #include <loongson/dev/kb3310var.h>
55 
56 #if defined(APMDEBUG)
57 #define DPRINTF(x)	printf x
58 #else
59 #define	DPRINTF(x)	/**/
60 #endif
61 
62 struct apm_softc {
63 	struct device sc_dev;
64 	struct klist sc_note;
65 	int    sc_flags;
66 };
67 
68 int apmmatch(struct device *, void *, void *);
69 void apmattach(struct device *, struct device *, void *);
70 
71 struct cfattach apm_ca = {
72 	sizeof(struct apm_softc), apmmatch, apmattach
73 };
74 
75 struct cfdriver apm_cd = {
76 	NULL, "apm", DV_DULL
77 };
78 
79 #define	APMUNIT(dev)	(minor(dev)&0xf0)
80 #define	APMDEV(dev)	(minor(dev)&0x0f)
81 #define APMDEV_NORMAL	0
82 #define APMDEV_CTL	8
83 
84 void filt_apmrdetach(struct knote *kn);
85 int filt_apmread(struct knote *kn, long hint);
86 int apmkqfilter(dev_t dev, struct knote *kn);
87 int apm_getdefaultinfo(struct apm_power_info *);
88 
89 int apm_suspend(void);
90 
91 struct filterops apmread_filtops =
92 	{ 1, NULL, filt_apmrdetach, filt_apmread};
93 
94 int (*get_apminfo)(struct apm_power_info *) = apm_getdefaultinfo;
95 
96 /*
97  * Flags to control kernel display
98  *	SCFLAG_NOPRINT:		do not output APM power messages due to
99  *				a power change event.
100  *
101  *	SCFLAG_PCTPRINT:	do not output APM power messages due to
102  *				to a power change event unless the battery
103  *				percentage changes.
104  */
105 
106 #define SCFLAG_NOPRINT	0x0008000
107 #define SCFLAG_PCTPRINT	0x0004000
108 #define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
109 
110 #define	SCFLAG_OREAD 	(1 << 0)
111 #define	SCFLAG_OWRITE	(1 << 1)
112 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
113 
114 
115 int
116 apmmatch(struct device *parent, void *match, void *aux)
117 {
118 	struct mainbus_attach_args *maa = aux;
119 
120 	/*
121 	 * It only makes sense to attach on a 2F system, since 2E do not
122 	 * feature speed throttling, and we do not support 2E-based
123 	 * notebooks yet (assuming there are any).
124 	 */
125 	if (strcmp(maa->maa_name, apm_cd.cd_name) == 0 && loongson_ver == 0x2f)
126 		return (1);
127 	return (0);
128 }
129 
130 void
131 apmattach(struct device *parent, struct device *self, void *aux)
132 {
133 	printf("\n");
134 }
135 
136 int
137 apmopen(dev_t dev, int flag, int mode, struct proc *p)
138 {
139 	struct apm_softc *sc;
140 	int error = 0;
141 
142 	/* apm0 only */
143 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
144 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
145 		return ENXIO;
146 
147 	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
148 	    APMDEV(dev), p->p_pid, flag, mode));
149 
150 	switch (APMDEV(dev)) {
151 	case APMDEV_CTL:
152 		if (!(flag & FWRITE)) {
153 			error = EINVAL;
154 			break;
155 		}
156 		if (sc->sc_flags & SCFLAG_OWRITE) {
157 			error = EBUSY;
158 			break;
159 		}
160 		sc->sc_flags |= SCFLAG_OWRITE;
161 		break;
162 	case APMDEV_NORMAL:
163 		if (!(flag & FREAD) || (flag & FWRITE)) {
164 			error = EINVAL;
165 			break;
166 		}
167 		sc->sc_flags |= SCFLAG_OREAD;
168 		break;
169 	default:
170 		error = ENXIO;
171 		break;
172 	}
173 	return error;
174 }
175 
176 int
177 apmclose(dev_t dev, int flag, int mode, struct proc *p)
178 {
179 	struct apm_softc *sc;
180 
181 	/* apm0 only */
182 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
183 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
184 		return ENXIO;
185 
186 	DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
187 
188 	switch (APMDEV(dev)) {
189 	case APMDEV_CTL:
190 		sc->sc_flags &= ~SCFLAG_OWRITE;
191 		break;
192 	case APMDEV_NORMAL:
193 		sc->sc_flags &= ~SCFLAG_OREAD;
194 		break;
195 	}
196 	return 0;
197 }
198 
199 int
200 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
201 {
202 	struct apm_softc *sc;
203 	struct apm_power_info *power;
204 	int error = 0;
205 
206 	/* apm0 only */
207 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
208 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
209 		return ENXIO;
210 
211 	switch (cmd) {
212 		/* some ioctl names from linux */
213 	case APM_IOC_STANDBY:
214 	case APM_IOC_STANDBY_REQ:
215 		if ((flag & FWRITE) == 0)
216 			error = EBADF;
217 		else
218 			error = EOPNOTSUPP; /* XXX */
219 		break;
220 	case APM_IOC_SUSPEND:
221 	case APM_IOC_SUSPEND_REQ:
222 		if ((flag & FWRITE) == 0)
223 			error = EBADF;
224 		else if (sys_platform->suspend == NULL ||
225 		    sys_platform->resume == NULL)
226 			error = EOPNOTSUPP;
227 		else
228 			error = apm_suspend();
229 		break;
230 	case APM_IOC_PRN_CTL:
231 		if ((flag & FWRITE) == 0)
232 			error = EBADF;
233 		else {
234 			int flag = *(int *)data;
235 			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
236 			switch (flag) {
237 			case APM_PRINT_ON:	/* enable printing */
238 				sc->sc_flags &= ~SCFLAG_PRINT;
239 				break;
240 			case APM_PRINT_OFF: /* disable printing */
241 				sc->sc_flags &= ~SCFLAG_PRINT;
242 				sc->sc_flags |= SCFLAG_NOPRINT;
243 				break;
244 			case APM_PRINT_PCT: /* disable some printing */
245 				sc->sc_flags &= ~SCFLAG_PRINT;
246 				sc->sc_flags |= SCFLAG_PCTPRINT;
247 				break;
248 			default:
249 				error = EINVAL;
250 				break;
251 			}
252 		}
253 		break;
254 	case APM_IOC_DEV_CTL:
255 		if ((flag & FWRITE) == 0)
256 			error = EBADF;
257 		else
258 			error = EOPNOTSUPP; /* XXX */
259 		break;
260 	case APM_IOC_GETPOWER:
261 	        power = (struct apm_power_info *)data;
262 		error = (*get_apminfo)(power);
263 		break;
264 	default:
265 		error = ENOTTY;
266 	}
267 
268 	return error;
269 }
270 
271 void
272 filt_apmrdetach(struct knote *kn)
273 {
274 	struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
275 
276 	SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
277 }
278 
279 int
280 filt_apmread(struct knote *kn, long hint)
281 {
282 	/* XXX weird kqueue_scan() semantics */
283 	if (hint && !kn->kn_data)
284 		kn->kn_data = (int)hint;
285 
286 	return (1);
287 }
288 
289 int
290 apmkqfilter(dev_t dev, struct knote *kn)
291 {
292 	struct apm_softc *sc;
293 
294 	/* apm0 only */
295 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
296 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
297 		return ENXIO;
298 
299 	switch (kn->kn_filter) {
300 	case EVFILT_READ:
301 		kn->kn_fop = &apmread_filtops;
302 		break;
303 	default:
304 		return (EINVAL);
305 	}
306 
307 	kn->kn_hook = (caddr_t)sc;
308 	SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
309 
310 	return (0);
311 }
312 
313 int
314 apm_getdefaultinfo(struct apm_power_info *info)
315 {
316 	info->battery_state = APM_BATT_UNKNOWN;
317 	info->ac_state = APM_AC_UNKNOWN;
318 	info->battery_life = 0;
319 	info->minutes_left = -1;
320 	return (0);
321 }
322 
323 void
324 apm_setinfohook(int (*hook)(struct apm_power_info *))
325 {
326 	get_apminfo = hook;
327 }
328 
329 int
330 apm_record_event(u_int event, const char *src, const char *msg)
331 {
332 	static int apm_evindex;
333 	struct apm_softc *sc;
334 
335 	/* apm0 only */
336 	if (apm_cd.cd_ndevs == 0 || (sc = apm_cd.cd_devs[0]) == NULL)
337 		return ENXIO;
338 
339 	if ((sc->sc_flags & SCFLAG_NOPRINT) == 0)
340 		printf("%s: %s %s\n", sc->sc_dev.dv_xname, src, msg);
341 
342 	/* skip if no user waiting */
343 	if ((sc->sc_flags & SCFLAG_OPEN) == 0)
344 		return (1);
345 
346 	apm_evindex++;
347 	KNOTE(&sc->sc_note, APM_EVENT_COMPOSE(event, apm_evindex));
348 
349 	return (0);
350 }
351 
352 int
353 apm_suspend()
354 {
355 	int rv;
356 	int s;
357 
358 #if NSWDISPLAY > 0
359 	wsdisplay_suspend();
360 #endif
361 	bufq_quiesce();
362 	config_suspend(TAILQ_FIRST(&alldevs), DVACT_QUIESCE);
363 
364 	s = splhigh();
365 	(void)disableintr();
366 	cold = 1;
367 
368 	rv = config_suspend(TAILQ_FIRST(&alldevs), DVACT_SUSPEND);
369 	if (rv == 0) {
370 		rv = sys_platform->suspend();
371 		if (rv == 0)
372 			rv = sys_platform->resume();
373 	}
374 	config_suspend(TAILQ_FIRST(&alldevs), DVACT_RESUME);
375 
376 	cold = 0;
377 	(void)enableintr();
378 	splx(s);
379 
380 	bufq_restart();
381 #if NWSDISPLAY > 0
382 	wsdisplay_resume();
383 #endif
384 
385 	return rv;
386 }
387