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