1 /* $NetBSD: apm.c,v 1.22 2009/03/14 15:36:09 dsl Exp $ */
2 /* $OpenBSD: apm.c,v 1.5 2002/06/07 07:13:59 miod Exp $ */
5 * Copyright (c) 2001 Alexander Guy. All rights reserved.
6 * Copyright (c) 1998-2001 Michael Shalayeff. All rights reserved.
7 * Copyright (c) 1995 John T. Kohl. All rights reserved.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the names of the authors nor the names of contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF MIND, USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: apm.c,v 1.22 2009/03/14 15:36:09 dsl Exp $");
41 #error only one APM emulation device may be configured
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/kernel.h>
48 #include <sys/device.h>
49 #include <sys/fcntl.h>
50 #include <sys/ioctl.h>
51 #include <sys/mutex.h>
53 #include <sys/event.h>
56 #include <sys/select.h>
62 #include <machine/conf.h>
64 #include <machine/cpu.h>
65 #include <machine/apmvar.h>
67 #include <macppc/dev/adbvar.h>
68 #include <macppc/dev/pm_direct.h>
71 #define DPRINTF(x) printf x
73 #define DPRINTF(x) /**/
76 #define APM_NEVENTS 16
80 struct selinfo sc_rsel
;
88 struct apm_event_info event_list
[APM_NEVENTS
];
92 * A brief note on the locking protocol: it's very simple; we
93 * assert an exclusive lock any time thread context enters the
94 * APM module. This is both the APM thread itself, as well as
98 #define APM_LOCK(apmsc) mutex_enter(&(apmsc)->sc_lock)
99 #define APM_UNLOCK(apmsc) mutex_exit(&(apmsc)->sc_lock)
101 #define APM_LOCK(apmsc)
102 #define APM_UNLOCK(apmsc)
105 int apmmatch(struct device
*, struct cfdata
*, void *);
106 void apmattach(struct device
*, struct device
*, void *);
110 static int apm_record_event(struct apm_softc
*, u_int
);
114 CFATTACH_DECL(apm
, sizeof(struct apm_softc
),
115 apmmatch
, apmattach
, NULL
, NULL
);
118 struct cfdriver apm_cd
= {
122 extern struct cfdriver apm_cd
;
124 dev_type_open(apmopen
);
125 dev_type_close(apmclose
);
126 dev_type_ioctl(apmioctl
);
127 dev_type_poll(apmpoll
);
128 dev_type_kqfilter(apmkqfilter
);
130 const struct cdevsw apm_cdevsw
= {
131 apmopen
, apmclose
, noread
, nowrite
, apmioctl
,
132 nostop
, notty
, apmpoll
, nommap
, apmkqfilter
,
138 #define APMUNIT(dev) (minor(dev)&0xf0)
139 #define APMDEV(dev) (minor(dev)&0x0f)
140 #define APMDEV_NORMAL 0
144 * Flags to control kernel display
145 * SCFLAG_NOPRINT: do not output APM power messages due to
146 * a power change event.
148 * SCFLAG_PCTPRINT: do not output APM power messages due to
149 * to a power change event unless the battery
150 * percentage changes.
153 #define SCFLAG_NOPRINT 0x0008000
154 #define SCFLAG_PCTPRINT 0x0004000
155 #define SCFLAG_PRINT (SCFLAG_NOPRINT|SCFLAG_PCTPRINT)
157 #define SCFLAG_OREAD (1 << 0)
158 #define SCFLAG_OWRITE (1 << 1)
159 #define SCFLAG_OPEN (SCFLAG_OREAD|SCFLAG_OWRITE)
163 apmmatch(struct device
*parent
, struct cfdata
*match
, void *aux
)
165 struct adb_attach_args
*aa
= (void *)aux
;
166 if (aa
->origaddr
!= ADBADDR_APM
||
167 aa
->handler_id
!= ADBADDR_APM
||
168 aa
->adbaddr
!= ADBADDR_APM
)
171 if (adbHardware
!= ADB_HW_PMU
)
178 apmattach(struct device
*parent
, struct device
*self
, void *aux
)
180 struct apm_softc
*sc
= (struct apm_softc
*) self
;
181 struct pmu_battery_info info
;
183 pm_battery_info(0, &info
);
185 printf(": battery flags 0x%X, ", info
.flags
);
186 printf("%d%% charged\n", ((info
.cur_charge
* 100) / info
.max_charge
));
191 mutex_init(&sc
->sc_lock
, MUTEX_DEFAULT
, IPL_NONE
);
192 selinit(&sc
->sc_rsel
);
196 apmopen(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
198 struct apm_softc
*sc
;
202 sc
= device_lookup_private(&apm_cd
, APMUNIT(dev
));
206 DPRINTF(("apmopen: dev %d pid %d flag %x mode %x\n",
207 APMDEV(dev
), l
->l_proc
->p_pid
, flag
, mode
));
210 switch (APMDEV(dev
)) {
212 if (!(flag
& FWRITE
)) {
216 if (sc
->sc_flags
& SCFLAG_OWRITE
) {
220 sc
->sc_flags
|= SCFLAG_OWRITE
;
223 if (!(flag
& FREAD
) || (flag
& FWRITE
)) {
227 sc
->sc_flags
|= SCFLAG_OREAD
;
238 apmclose(dev_t dev
, int flag
, int mode
, struct lwp
*l
)
240 struct apm_softc
*sc
;
243 sc
= device_lookup_private(&apm_cd
, APMUNIT(dev
));
247 DPRINTF(("apmclose: pid %d flag %x mode %x\n", l
->l_proc
->p_pid
, flag
, mode
));
250 switch (APMDEV(dev
)) {
252 sc
->sc_flags
&= ~SCFLAG_OWRITE
;
255 sc
->sc_flags
&= ~SCFLAG_OREAD
;
263 apmioctl(dev_t dev
, u_long cmd
, void *data
, int flag
, struct lwp
*l
)
265 struct apm_softc
*sc
;
266 struct pmu_battery_info batt
;
267 struct apm_power_info
*power
;
271 sc
= device_lookup_private(&apm_cd
, APMUNIT(dev
));
277 /* some ioctl names from linux */
278 case APM_IOC_STANDBY
:
279 if ((flag
& FWRITE
) == 0)
281 case APM_IOC_SUSPEND
:
282 if ((flag
& FWRITE
) == 0)
285 case APM_IOC_PRN_CTL
:
286 if ((flag
& FWRITE
) == 0)
289 int op
= *(int *)data
;
290 DPRINTF(( "APM_IOC_PRN_CTL: %d\n", op
));
292 case APM_PRINT_ON
: /* enable printing */
293 sc
->sc_flags
&= ~SCFLAG_PRINT
;
295 case APM_PRINT_OFF
: /* disable printing */
296 sc
->sc_flags
&= ~SCFLAG_PRINT
;
297 sc
->sc_flags
|= SCFLAG_NOPRINT
;
299 case APM_PRINT_PCT
: /* disable some printing */
300 sc
->sc_flags
&= ~SCFLAG_PRINT
;
301 sc
->sc_flags
|= SCFLAG_PCTPRINT
;
309 case APM_IOC_DEV_CTL
:
310 if ((flag
& FWRITE
) == 0)
313 case APM_IOC_GETPOWER
:
314 power
= (struct apm_power_info
*)data
;
316 pm_battery_info(0, &batt
);
318 power
->ac_state
= ((batt
.flags
& PMU_PWR_AC_PRESENT
) ?
319 APM_AC_ON
: APM_AC_OFF
);
320 power
->battery_life
=
321 ((batt
.cur_charge
* 100) / batt
.max_charge
);
324 * If the battery is charging, return the minutes left until
325 * charging is complete. apmd knows this.
328 if (!(batt
.flags
& PMU_PWR_BATT_PRESENT
)) {
329 power
->battery_state
= APM_BATT_UNKNOWN
;
330 power
->minutes_left
= 0;
331 power
->battery_life
= 0;
332 } else if ((power
->ac_state
== APM_AC_ON
) &&
334 power
->minutes_left
= batt
.secs_remaining
/ 60;
335 power
->battery_state
= APM_BATT_CHARGING
;
337 power
->minutes_left
= batt
.secs_remaining
/ 60;
339 /* XXX - Arbitrary */
340 if (power
->battery_life
> 60) {
341 power
->battery_state
= APM_BATT_HIGH
;
342 } else if (power
->battery_life
< 10) {
343 power
->battery_state
= APM_BATT_CRITICAL
;
345 power
->battery_state
= APM_BATT_LOW
;
362 * return 0 if the user will notice and handle the event,
363 * return 1 if the kernel driver should do so.
366 apm_record_event(struct apm_softc
*sc
, u_int event_type
)
368 struct apm_event_info
*evp
;
370 if ((sc
->sc_flags
& SCFLAG_OPEN
) == 0)
371 return 1; /* no user waiting */
372 if (sc
->event_count
== APM_NEVENTS
) {
373 DPRINTF(("apm_record_event: queue full!\n"));
374 return 1; /* overflow */
376 evp
= &sc
->event_list
[sc
->event_ptr
];
379 sc
->event_ptr
%= APM_NEVENTS
;
380 evp
->type
= event_type
;
381 evp
->index
= ++apm_evindex
;
382 selnotify(&sc
->sc_rsel
, 0, 0);
383 return (sc
->sc_flags
& SCFLAG_OWRITE
) ? 0 : 1; /* user may handle */
388 apmpoll(dev_t dev
, int events
, struct lwp
*l
)
390 struct apm_softc
*sc
= device_lookup_private(&apm_cd
,APMUNIT(dev
));
394 if (events
& (POLLIN
| POLLRDNORM
)) {
396 revents
|= events
& (POLLIN
| POLLRDNORM
);
398 selrecord(l
, &sc
->sc_rsel
);
407 filt_apmrdetach(struct knote
*kn
)
409 struct apm_softc
*sc
= (struct apm_softc
*)kn
->kn_hook
;
412 SLIST_REMOVE(&sc
->sc_rsel
.sel_klist
, kn
, knote
, kn_selnext
);
417 filt_apmread(struct knote
*kn
, long hint
)
419 struct apm_softc
*sc
= kn
->kn_hook
;
421 kn
->kn_data
= sc
->event_count
;
422 return (kn
->kn_data
> 0);
425 static struct filterops apmread_filtops
=
426 { 1, NULL
, filt_apmrdetach
, filt_apmread
};
429 apmkqfilter(dev_t dev
, struct knote
*kn
)
431 struct apm_softc
*sc
= device_lookup_private(&apm_cd
,APMUNIT(dev
));
434 switch (kn
->kn_filter
) {
436 klist
= &sc
->sc_rsel
.sel_klist
;
437 kn
->kn_fop
= &apmread_filtops
;
446 SLIST_INSERT_HEAD(klist
, kn
, kn_selnext
);