xref: /openbsd-src/sys/arch/macppc/dev/apm.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: apm.c,v 1.17 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 
36 #if NAPM > 1
37 #error only one APM emulation device may be configured
38 #endif
39 
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/kernel.h>
43 #include <sys/proc.h>
44 #include <sys/device.h>
45 #include <sys/fcntl.h>
46 #include <sys/ioctl.h>
47 #include <sys/event.h>
48 
49 #include <machine/conf.h>
50 #include <machine/cpu.h>
51 #include <machine/apmvar.h>
52 #include <machine/autoconf.h>
53 
54 #include <macppc/dev/pm_direct.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 
88 struct filterops apmread_filtops =
89 	{ 1, NULL, filt_apmrdetach, filt_apmread};
90 
91 /*
92  * Flags to control kernel display
93  *	SCFLAG_NOPRINT:		do not output APM power messages due to
94  *				a power change event.
95  *
96  *	SCFLAG_PCTPRINT:	do not output APM power messages due to
97  *				to a power change event unless the battery
98  *				percentage changes.
99  */
100 
101 #define SCFLAG_NOPRINT	0x0008000
102 #define SCFLAG_PCTPRINT	0x0004000
103 #define SCFLAG_PRINT	(SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
104 
105 #define	SCFLAG_OREAD 	(1 << 0)
106 #define	SCFLAG_OWRITE	(1 << 1)
107 #define	SCFLAG_OPEN	(SCFLAG_OREAD|SCFLAG_OWRITE)
108 
109 
110 int
111 apmmatch(struct device *parent, void *match, void *aux)
112 {
113 	struct confargs *ca = aux;
114 
115 	if (strcmp(ca->ca_name, "apm") != 0)
116 		return (0);
117 
118 	return (1);
119 }
120 
121 void
122 apmattach(struct device *parent, struct device *self, void *aux)
123 {
124 	struct pmu_battery_info info;
125 
126 	pm_battery_info(0, &info);
127 
128 	printf(": battery flags 0x%X, ", info.flags);
129 	printf("%d%% charged\n", ((info.cur_charge * 100) / info.max_charge));
130 }
131 
132 int
133 apmopen(dev_t dev, int flag, int mode, struct proc *p)
134 {
135 	struct apm_softc *sc;
136 	int error = 0;
137 
138 	/* apm0 only */
139 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
140 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
141 		return ENXIO;
142 
143 	DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
144 	    APMDEV(dev), p->p_pid, flag, mode));
145 
146 	switch (APMDEV(dev)) {
147 	case APMDEV_CTL:
148 		if (!(flag & FWRITE)) {
149 			error = EINVAL;
150 			break;
151 		}
152 		if (sc->sc_flags & SCFLAG_OWRITE) {
153 			error = EBUSY;
154 			break;
155 		}
156 		sc->sc_flags |= SCFLAG_OWRITE;
157 		break;
158 	case APMDEV_NORMAL:
159 		if (!(flag & FREAD) || (flag & FWRITE)) {
160 			error = EINVAL;
161 			break;
162 		}
163 		sc->sc_flags |= SCFLAG_OREAD;
164 		break;
165 	default:
166 		error = ENXIO;
167 		break;
168 	}
169 	return error;
170 }
171 
172 int
173 apmclose(dev_t dev, int flag, int mode, struct proc *p)
174 {
175 	struct apm_softc *sc;
176 
177 	/* apm0 only */
178 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
179 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
180 		return ENXIO;
181 
182 	DPRINTF(("apmclose: pid %d flag %x mode %x\n", p->p_pid, flag, mode));
183 
184 	switch (APMDEV(dev)) {
185 	case APMDEV_CTL:
186 		sc->sc_flags &= ~SCFLAG_OWRITE;
187 		break;
188 	case APMDEV_NORMAL:
189 		sc->sc_flags &= ~SCFLAG_OREAD;
190 		break;
191 	}
192 	return 0;
193 }
194 
195 int
196 apmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
197 {
198 	struct apm_softc *sc;
199 	struct pmu_battery_info batt;
200 	struct apm_power_info *power;
201 	int error = 0;
202 
203 	/* apm0 only */
204 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
205 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
206 		return ENXIO;
207 
208 	switch (cmd) {
209 		/* some ioctl names from linux */
210 	case APM_IOC_STANDBY:
211 		if ((flag & FWRITE) == 0)
212 			error = EBADF;
213 		break;
214 	case APM_IOC_SUSPEND:
215 		if ((flag & FWRITE) == 0)
216 			error = EBADF;
217 		break;
218 	case APM_IOC_PRN_CTL:
219 		if ((flag & FWRITE) == 0)
220 			error = EBADF;
221 		else {
222 			int flag = *(int *)data;
223 			DPRINTF(( "APM_IOC_PRN_CTL: %d\n", flag ));
224 			switch (flag) {
225 			case APM_PRINT_ON:	/* enable printing */
226 				sc->sc_flags &= ~SCFLAG_PRINT;
227 				break;
228 			case APM_PRINT_OFF: /* disable printing */
229 				sc->sc_flags &= ~SCFLAG_PRINT;
230 				sc->sc_flags |= SCFLAG_NOPRINT;
231 				break;
232 			case APM_PRINT_PCT: /* disable some printing */
233 				sc->sc_flags &= ~SCFLAG_PRINT;
234 				sc->sc_flags |= SCFLAG_PCTPRINT;
235 				break;
236 			default:
237 				error = EINVAL;
238 				break;
239 			}
240 		}
241 		break;
242 	case APM_IOC_DEV_CTL:
243 		if ((flag & FWRITE) == 0)
244 			error = EBADF;
245 		break;
246 	case APM_IOC_GETPOWER:
247 	        power = (struct apm_power_info *)data;
248 
249 		pm_battery_info(0, &batt);
250 
251 		power->ac_state = ((batt.flags & PMU_PWR_AC_PRESENT) ?
252 		    APM_AC_ON : APM_AC_OFF);
253 		power->battery_life =
254 		    ((batt.cur_charge * 100) / batt.max_charge);
255 
256 		/*
257 		 * If the battery is charging, return the minutes left until
258 		 * charging is complete. apmd knows this.
259 		 */
260 
261 		if (!(batt.flags & PMU_PWR_BATT_PRESENT)) {
262 			power->battery_state = APM_BATT_UNKNOWN;
263 			power->minutes_left = 0;
264 			power->battery_life = 0;
265 		} else if ((power->ac_state == APM_AC_ON) &&
266 			   (batt.draw > 0)) {
267 			power->minutes_left =
268 			    (((batt.max_charge - batt.cur_charge) * 3600) /
269 			    batt.draw) / 60;
270 			power->battery_state = APM_BATT_CHARGING;
271 		} else {
272 			power->minutes_left =
273 			    ((batt.cur_charge * 3600) / (-batt.draw)) / 60;
274 
275 			/* XXX - Arbitrary */
276 			if (power->battery_life > 60)
277 				power->battery_state = APM_BATT_HIGH;
278 			else if (power->battery_life < 10)
279 				power->battery_state = APM_BATT_CRITICAL;
280 			else
281 				power->battery_state = APM_BATT_LOW;
282 		}
283 		break;
284 	case APM_IOC_STANDBY_REQ:
285 		if ((flag & FWRITE) == 0)
286 			error = EBADF;
287 		break;
288 	case APM_IOC_SUSPEND_REQ:
289 		if ((flag & FWRITE) == 0)
290 			error = EBADF;
291 		break;
292 	default:
293 		error = ENOTTY;
294 	}
295 
296 	return error;
297 }
298 
299 void
300 filt_apmrdetach(struct knote *kn)
301 {
302 	struct apm_softc *sc = (struct apm_softc *)kn->kn_hook;
303 
304 	SLIST_REMOVE(&sc->sc_note, kn, knote, kn_selnext);
305 }
306 
307 int
308 filt_apmread(struct knote *kn, long hint)
309 {
310 	/* XXX weird kqueue_scan() semantics */
311 	if (hint && !kn->kn_data)
312 		kn->kn_data = (int)hint;
313 
314 	return (1);
315 }
316 
317 int
318 apmkqfilter(dev_t dev, struct knote *kn)
319 {
320 	struct apm_softc *sc;
321 
322 	/* apm0 only */
323 	if (!apm_cd.cd_ndevs || APMUNIT(dev) != 0 ||
324 	    !(sc = apm_cd.cd_devs[APMUNIT(dev)]))
325 		return ENXIO;
326 
327 	switch (kn->kn_filter) {
328 	case EVFILT_READ:
329 		kn->kn_fop = &apmread_filtops;
330 		break;
331 	default:
332 		return (EINVAL);
333 	}
334 
335 	kn->kn_hook = (caddr_t)sc;
336 	SLIST_INSERT_HEAD(&sc->sc_note, kn, kn_selnext);
337 
338 	return (0);
339 }
340