ia64/linux-2.6.18-xen.hg

diff drivers/macintosh/apm_emu.c @ 0:831230e53067

Import 2.6.18 from kernel.org tarball.
author Ian Campbell <ian.campbell@xensource.com>
date Wed Apr 11 14:15:44 2007 +0100 (2007-04-11)
parents
children
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/drivers/macintosh/apm_emu.c	Wed Apr 11 14:15:44 2007 +0100
     1.3 @@ -0,0 +1,556 @@
     1.4 +/* APM emulation layer for PowerMac
     1.5 + * 
     1.6 + * Copyright 2001 Benjamin Herrenschmidt (benh@kernel.crashing.org)
     1.7 + *
     1.8 + * Lots of code inherited from apm.c, see appropriate notice in
     1.9 + *  arch/i386/kernel/apm.c
    1.10 + *
    1.11 + * This program is free software; you can redistribute it and/or modify it
    1.12 + * under the terms of the GNU General Public License as published by the
    1.13 + * Free Software Foundation; either version 2, or (at your option) any
    1.14 + * later version.
    1.15 + *
    1.16 + * This program is distributed in the hope that it will be useful, but
    1.17 + * WITHOUT ANY WARRANTY; without even the implied warranty of
    1.18 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    1.19 + * General Public License for more details.
    1.20 + *
    1.21 + *
    1.22 + */
    1.23 +
    1.24 +#include <linux/module.h>
    1.25 +
    1.26 +#include <linux/poll.h>
    1.27 +#include <linux/types.h>
    1.28 +#include <linux/stddef.h>
    1.29 +#include <linux/timer.h>
    1.30 +#include <linux/fcntl.h>
    1.31 +#include <linux/slab.h>
    1.32 +#include <linux/stat.h>
    1.33 +#include <linux/proc_fs.h>
    1.34 +#include <linux/miscdevice.h>
    1.35 +#include <linux/apm_bios.h>
    1.36 +#include <linux/init.h>
    1.37 +#include <linux/sched.h>
    1.38 +#include <linux/pm.h>
    1.39 +#include <linux/kernel.h>
    1.40 +#include <linux/smp_lock.h>
    1.41 +
    1.42 +#include <linux/adb.h>
    1.43 +#include <linux/pmu.h>
    1.44 +
    1.45 +#include <asm/system.h>
    1.46 +#include <asm/uaccess.h>
    1.47 +#include <asm/machdep.h>
    1.48 +
    1.49 +#undef DEBUG
    1.50 +
    1.51 +#ifdef DEBUG
    1.52 +#define DBG(args...) printk(KERN_DEBUG args)
    1.53 +//#define DBG(args...) xmon_printf(args)
    1.54 +#else
    1.55 +#define DBG(args...) do { } while (0)
    1.56 +#endif
    1.57 +
    1.58 +/*
    1.59 + * The apm_bios device is one of the misc char devices.
    1.60 + * This is its minor number.
    1.61 + */
    1.62 +#define	APM_MINOR_DEV	134
    1.63 +
    1.64 +/*
    1.65 + * Maximum number of events stored
    1.66 + */
    1.67 +#define APM_MAX_EVENTS		20
    1.68 +
    1.69 +#define FAKE_APM_BIOS_VERSION	0x0101
    1.70 +
    1.71 +#define APM_USER_NOTIFY_TIMEOUT	(5*HZ)
    1.72 +
    1.73 +/*
    1.74 + * The per-file APM data
    1.75 + */
    1.76 +struct apm_user {
    1.77 +	int		magic;
    1.78 +	struct apm_user *	next;
    1.79 +	int		suser: 1;
    1.80 +	int		suspend_waiting: 1;
    1.81 +	int		suspends_pending;
    1.82 +	int		suspends_read;
    1.83 +	int		event_head;
    1.84 +	int		event_tail;
    1.85 +	apm_event_t	events[APM_MAX_EVENTS];
    1.86 +};
    1.87 +
    1.88 +/*
    1.89 + * The magic number in apm_user
    1.90 + */
    1.91 +#define APM_BIOS_MAGIC		0x4101
    1.92 +
    1.93 +/*
    1.94 + * Local variables
    1.95 + */
    1.96 +static int			suspends_pending;
    1.97 +
    1.98 +static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
    1.99 +static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
   1.100 +static struct apm_user *	user_list;
   1.101 +
   1.102 +static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when);
   1.103 +static struct pmu_sleep_notifier apm_sleep_notifier = {
   1.104 +	apm_notify_sleep,
   1.105 +	SLEEP_LEVEL_USERLAND,
   1.106 +};
   1.107 +
   1.108 +static char			driver_version[] = "0.5";	/* no spaces */
   1.109 +
   1.110 +#ifdef DEBUG
   1.111 +static char *	apm_event_name[] = {
   1.112 +	"system standby",
   1.113 +	"system suspend",
   1.114 +	"normal resume",
   1.115 +	"critical resume",
   1.116 +	"low battery",
   1.117 +	"power status change",
   1.118 +	"update time",
   1.119 +	"critical suspend",
   1.120 +	"user standby",
   1.121 +	"user suspend",
   1.122 +	"system standby resume",
   1.123 +	"capabilities change"
   1.124 +};
   1.125 +#define NR_APM_EVENT_NAME	\
   1.126 +		(sizeof(apm_event_name) / sizeof(apm_event_name[0]))
   1.127 +
   1.128 +#endif
   1.129 +
   1.130 +static int queue_empty(struct apm_user *as)
   1.131 +{
   1.132 +	return as->event_head == as->event_tail;
   1.133 +}
   1.134 +
   1.135 +static apm_event_t get_queued_event(struct apm_user *as)
   1.136 +{
   1.137 +	as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
   1.138 +	return as->events[as->event_tail];
   1.139 +}
   1.140 +
   1.141 +static void queue_event(apm_event_t event, struct apm_user *sender)
   1.142 +{
   1.143 +	struct apm_user *	as;
   1.144 +
   1.145 +	DBG("apm_emu: queue_event(%s)\n", apm_event_name[event-1]);
   1.146 +	if (user_list == NULL)
   1.147 +		return;
   1.148 +	for (as = user_list; as != NULL; as = as->next) {
   1.149 +		if (as == sender)
   1.150 +			continue;
   1.151 +		as->event_head = (as->event_head + 1) % APM_MAX_EVENTS;
   1.152 +		if (as->event_head == as->event_tail) {
   1.153 +			static int notified;
   1.154 +
   1.155 +			if (notified++ == 0)
   1.156 +			    printk(KERN_ERR "apm_emu: an event queue overflowed\n");
   1.157 +			as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS;
   1.158 +		}
   1.159 +		as->events[as->event_head] = event;
   1.160 +		if (!as->suser)
   1.161 +			continue;
   1.162 +		switch (event) {
   1.163 +		case APM_SYS_SUSPEND:
   1.164 +		case APM_USER_SUSPEND:
   1.165 +			as->suspends_pending++;
   1.166 +			suspends_pending++;
   1.167 +			break;
   1.168 +		case APM_NORMAL_RESUME:
   1.169 +			as->suspend_waiting = 0;
   1.170 +			break;
   1.171 +		}
   1.172 +	}
   1.173 +	wake_up_interruptible(&apm_waitqueue);
   1.174 +}
   1.175 +
   1.176 +static int check_apm_user(struct apm_user *as, const char *func)
   1.177 +{
   1.178 +	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {
   1.179 +		printk(KERN_ERR "apm_emu: %s passed bad filp\n", func);
   1.180 +		return 1;
   1.181 +	}
   1.182 +	return 0;
   1.183 +}
   1.184 +
   1.185 +static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos)
   1.186 +{
   1.187 +	struct apm_user *	as;
   1.188 +	size_t			i;
   1.189 +	apm_event_t		event;
   1.190 +	DECLARE_WAITQUEUE(wait, current);
   1.191 +
   1.192 +	as = fp->private_data;
   1.193 +	if (check_apm_user(as, "read"))
   1.194 +		return -EIO;
   1.195 +	if (count < sizeof(apm_event_t))
   1.196 +		return -EINVAL;
   1.197 +	if (queue_empty(as)) {
   1.198 +		if (fp->f_flags & O_NONBLOCK)
   1.199 +			return -EAGAIN;
   1.200 +		add_wait_queue(&apm_waitqueue, &wait);
   1.201 +repeat:
   1.202 +		set_current_state(TASK_INTERRUPTIBLE);
   1.203 +		if (queue_empty(as) && !signal_pending(current)) {
   1.204 +			schedule();
   1.205 +			goto repeat;
   1.206 +		}
   1.207 +		set_current_state(TASK_RUNNING);
   1.208 +		remove_wait_queue(&apm_waitqueue, &wait);
   1.209 +	}
   1.210 +	i = count;
   1.211 +	while ((i >= sizeof(event)) && !queue_empty(as)) {
   1.212 +		event = get_queued_event(as);
   1.213 +		DBG("apm_emu: do_read, returning: %s\n", apm_event_name[event-1]);
   1.214 +		if (copy_to_user(buf, &event, sizeof(event))) {
   1.215 +			if (i < count)
   1.216 +				break;
   1.217 +			return -EFAULT;
   1.218 +		}
   1.219 +		switch (event) {
   1.220 +		case APM_SYS_SUSPEND:
   1.221 +		case APM_USER_SUSPEND:
   1.222 +			as->suspends_read++;
   1.223 +			break;
   1.224 +		}
   1.225 +		buf += sizeof(event);
   1.226 +		i -= sizeof(event);
   1.227 +	}
   1.228 +	if (i < count)
   1.229 +		return count - i;
   1.230 +	if (signal_pending(current))
   1.231 +		return -ERESTARTSYS;
   1.232 +	return 0;
   1.233 +}
   1.234 +
   1.235 +static unsigned int do_poll(struct file *fp, poll_table * wait)
   1.236 +{
   1.237 +	struct apm_user * as;
   1.238 +
   1.239 +	as = fp->private_data;
   1.240 +	if (check_apm_user(as, "poll"))
   1.241 +		return 0;
   1.242 +	poll_wait(fp, &apm_waitqueue, wait);
   1.243 +	if (!queue_empty(as))
   1.244 +		return POLLIN | POLLRDNORM;
   1.245 +	return 0;
   1.246 +}
   1.247 +
   1.248 +static int do_ioctl(struct inode * inode, struct file *filp,
   1.249 +		    u_int cmd, u_long arg)
   1.250 +{
   1.251 +	struct apm_user *	as;
   1.252 +	DECLARE_WAITQUEUE(wait, current);
   1.253 +
   1.254 +	as = filp->private_data;
   1.255 +	if (check_apm_user(as, "ioctl"))
   1.256 +		return -EIO;
   1.257 +	if (!as->suser)
   1.258 +		return -EPERM;
   1.259 +	switch (cmd) {
   1.260 +	case APM_IOC_SUSPEND:
   1.261 +		/* If a suspend message was sent to userland, we
   1.262 +		 * consider this as a confirmation message
   1.263 +		 */
   1.264 +		if (as->suspends_read > 0) {
   1.265 +			as->suspends_read--;
   1.266 +			as->suspends_pending--;
   1.267 +			suspends_pending--;
   1.268 +		} else {
   1.269 +			// Route to PMU suspend ?
   1.270 +			break;
   1.271 +		}
   1.272 +		as->suspend_waiting = 1;
   1.273 +		add_wait_queue(&apm_waitqueue, &wait);
   1.274 +		DBG("apm_emu: ioctl waking up sleep waiter !\n");
   1.275 +		wake_up(&apm_suspend_waitqueue);
   1.276 +		mb();
   1.277 +		while(as->suspend_waiting && !signal_pending(current)) {
   1.278 +			set_current_state(TASK_INTERRUPTIBLE);
   1.279 +			schedule();
   1.280 +		}
   1.281 +		set_current_state(TASK_RUNNING);
   1.282 +		remove_wait_queue(&apm_waitqueue, &wait);
   1.283 +		break;
   1.284 +	default:
   1.285 +		return -EINVAL;
   1.286 +	}
   1.287 +	return 0;
   1.288 +}
   1.289 +
   1.290 +static int do_release(struct inode * inode, struct file * filp)
   1.291 +{
   1.292 +	struct apm_user *	as;
   1.293 +
   1.294 +	as = filp->private_data;
   1.295 +	if (check_apm_user(as, "release"))
   1.296 +		return 0;
   1.297 +	filp->private_data = NULL;
   1.298 +	lock_kernel();
   1.299 +	if (as->suspends_pending > 0) {
   1.300 +		suspends_pending -= as->suspends_pending;
   1.301 +		if (suspends_pending <= 0)
   1.302 +			wake_up(&apm_suspend_waitqueue);
   1.303 +	}
   1.304 +	if (user_list == as)
   1.305 +		user_list = as->next;
   1.306 +	else {
   1.307 +		struct apm_user *	as1;
   1.308 +
   1.309 +		for (as1 = user_list;
   1.310 +		     (as1 != NULL) && (as1->next != as);
   1.311 +		     as1 = as1->next)
   1.312 +			;
   1.313 +		if (as1 == NULL)
   1.314 +			printk(KERN_ERR "apm: filp not in user list\n");
   1.315 +		else
   1.316 +			as1->next = as->next;
   1.317 +	}
   1.318 +	unlock_kernel();
   1.319 +	kfree(as);
   1.320 +	return 0;
   1.321 +}
   1.322 +
   1.323 +static int do_open(struct inode * inode, struct file * filp)
   1.324 +{
   1.325 +	struct apm_user *	as;
   1.326 +
   1.327 +	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);
   1.328 +	if (as == NULL) {
   1.329 +		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",
   1.330 +		       sizeof(*as));
   1.331 +		return -ENOMEM;
   1.332 +	}
   1.333 +	as->magic = APM_BIOS_MAGIC;
   1.334 +	as->event_tail = as->event_head = 0;
   1.335 +	as->suspends_pending = 0;
   1.336 +	as->suspends_read = 0;
   1.337 +	/*
   1.338 +	 * XXX - this is a tiny bit broken, when we consider BSD
   1.339 +         * process accounting. If the device is opened by root, we
   1.340 +	 * instantly flag that we used superuser privs. Who knows,
   1.341 +	 * we might close the device immediately without doing a
   1.342 +	 * privileged operation -- cevans
   1.343 +	 */
   1.344 +	as->suser = capable(CAP_SYS_ADMIN);
   1.345 +	as->next = user_list;
   1.346 +	user_list = as;
   1.347 +	filp->private_data = as;
   1.348 +
   1.349 +	DBG("apm_emu: opened by %s, suser: %d\n", current->comm, (int)as->suser);
   1.350 +
   1.351 +	return 0;
   1.352 +}
   1.353 +
   1.354 +/* Wait for all clients to ack the suspend request. APM API
   1.355 + * doesn't provide a way to NAK, but this could be added
   1.356 + * here.
   1.357 + */
   1.358 +static int wait_all_suspend(void)
   1.359 +{
   1.360 +	DECLARE_WAITQUEUE(wait, current);
   1.361 +
   1.362 +	add_wait_queue(&apm_suspend_waitqueue, &wait);
   1.363 +	DBG("apm_emu: wait_all_suspend(), suspends_pending: %d\n", suspends_pending);
   1.364 +	while(suspends_pending > 0) {
   1.365 +		set_current_state(TASK_UNINTERRUPTIBLE);
   1.366 +		schedule();
   1.367 +	}
   1.368 +	set_current_state(TASK_RUNNING);
   1.369 +	remove_wait_queue(&apm_suspend_waitqueue, &wait);
   1.370 +
   1.371 +	DBG("apm_emu: wait_all_suspend() - complete !\n");
   1.372 +	
   1.373 +	return 1;
   1.374 +}
   1.375 +
   1.376 +static int apm_notify_sleep(struct pmu_sleep_notifier *self, int when)
   1.377 +{
   1.378 +	switch(when) {
   1.379 +		case PBOOK_SLEEP_REQUEST:
   1.380 +			queue_event(APM_SYS_SUSPEND, NULL);
   1.381 +			if (!wait_all_suspend())
   1.382 +				return PBOOK_SLEEP_REFUSE;
   1.383 +			break;
   1.384 +		case PBOOK_SLEEP_REJECT:
   1.385 +		case PBOOK_WAKE:
   1.386 +			queue_event(APM_NORMAL_RESUME, NULL);
   1.387 +			break;
   1.388 +	}
   1.389 +	return PBOOK_SLEEP_OK;
   1.390 +}
   1.391 +
   1.392 +#define APM_CRITICAL		10
   1.393 +#define APM_LOW			30
   1.394 +
   1.395 +static int apm_emu_get_info(char *buf, char **start, off_t fpos, int length)
   1.396 +{
   1.397 +	/* Arguments, with symbols from linux/apm_bios.h.  Information is
   1.398 +	   from the Get Power Status (0x0a) call unless otherwise noted.
   1.399 +
   1.400 +	   0) Linux driver version (this will change if format changes)
   1.401 +	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.
   1.402 +	   2) APM flags from APM Installation Check (0x00):
   1.403 +	      bit 0: APM_16_BIT_SUPPORT
   1.404 +	      bit 1: APM_32_BIT_SUPPORT
   1.405 +	      bit 2: APM_IDLE_SLOWS_CLOCK
   1.406 +	      bit 3: APM_BIOS_DISABLED
   1.407 +	      bit 4: APM_BIOS_DISENGAGED
   1.408 +	   3) AC line status
   1.409 +	      0x00: Off-line
   1.410 +	      0x01: On-line
   1.411 +	      0x02: On backup power (BIOS >= 1.1 only)
   1.412 +	      0xff: Unknown
   1.413 +	   4) Battery status
   1.414 +	      0x00: High
   1.415 +	      0x01: Low
   1.416 +	      0x02: Critical
   1.417 +	      0x03: Charging
   1.418 +	      0x04: Selected battery not present (BIOS >= 1.2 only)
   1.419 +	      0xff: Unknown
   1.420 +	   5) Battery flag
   1.421 +	      bit 0: High
   1.422 +	      bit 1: Low
   1.423 +	      bit 2: Critical
   1.424 +	      bit 3: Charging
   1.425 +	      bit 7: No system battery
   1.426 +	      0xff: Unknown
   1.427 +	   6) Remaining battery life (percentage of charge):
   1.428 +	      0-100: valid
   1.429 +	      -1: Unknown
   1.430 +	   7) Remaining battery life (time units):
   1.431 +	      Number of remaining minutes or seconds
   1.432 +	      -1: Unknown
   1.433 +	   8) min = minutes; sec = seconds */
   1.434 +
   1.435 +	unsigned short  ac_line_status;
   1.436 +	unsigned short  battery_status = 0;
   1.437 +	unsigned short  battery_flag   = 0xff;
   1.438 +	int		percentage     = -1;
   1.439 +	int             time_units     = -1;
   1.440 +	int		real_count     = 0;
   1.441 +	int		i;
   1.442 +	char *		p = buf;
   1.443 +	char		charging       = 0;
   1.444 +	long		charge	       = -1;
   1.445 +	long		amperage       = 0;
   1.446 +	unsigned long	btype          = 0;
   1.447 +
   1.448 +	ac_line_status = ((pmu_power_flags & PMU_PWR_AC_PRESENT) != 0);
   1.449 +	for (i=0; i<pmu_battery_count; i++) {
   1.450 +		if (pmu_batteries[i].flags & PMU_BATT_PRESENT) {
   1.451 +			battery_status++;
   1.452 +			if (percentage < 0)
   1.453 +				percentage = 0;
   1.454 +			if (charge < 0)
   1.455 +				charge = 0;
   1.456 +			percentage += (pmu_batteries[i].charge * 100) /
   1.457 +				pmu_batteries[i].max_charge;
   1.458 +			charge += pmu_batteries[i].charge;
   1.459 +			amperage += pmu_batteries[i].amperage;
   1.460 +			if (btype == 0)
   1.461 +				btype = (pmu_batteries[i].flags & PMU_BATT_TYPE_MASK);
   1.462 +			real_count++;
   1.463 +			if ((pmu_batteries[i].flags & PMU_BATT_CHARGING))
   1.464 +				charging++;
   1.465 +		}
   1.466 +	}
   1.467 +	if (0 == battery_status)
   1.468 +		ac_line_status = 1;
   1.469 +	battery_status = 0xff;
   1.470 +	if (real_count) {
   1.471 +		if (amperage < 0) {
   1.472 +			if (btype == PMU_BATT_TYPE_SMART)
   1.473 +				time_units = (charge * 59) / (amperage * -1);
   1.474 +			else
   1.475 +				time_units = (charge * 16440) / (amperage * -60);
   1.476 +		}
   1.477 +		percentage /= real_count;
   1.478 +		if (charging > 0) {
   1.479 +			battery_status = 0x03;
   1.480 +			battery_flag = 0x08;
   1.481 +		} else if (percentage <= APM_CRITICAL) {
   1.482 +			battery_status = 0x02;
   1.483 +			battery_flag = 0x04;
   1.484 +		} else if (percentage <= APM_LOW) {
   1.485 +			battery_status = 0x01;
   1.486 +			battery_flag = 0x02;
   1.487 +		} else {
   1.488 +			battery_status = 0x00;
   1.489 +			battery_flag = 0x01;
   1.490 +		}
   1.491 +	}
   1.492 +	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
   1.493 +		     driver_version,
   1.494 +		     (FAKE_APM_BIOS_VERSION >> 8) & 0xff,
   1.495 +		     FAKE_APM_BIOS_VERSION & 0xff,
   1.496 +		     0,
   1.497 +		     ac_line_status,
   1.498 +		     battery_status,
   1.499 +		     battery_flag,
   1.500 +		     percentage,
   1.501 +		     time_units,
   1.502 +		     "min");
   1.503 +
   1.504 +	return p - buf;
   1.505 +}
   1.506 +
   1.507 +static struct file_operations apm_bios_fops = {
   1.508 +	.owner		= THIS_MODULE,
   1.509 +	.read		= do_read,
   1.510 +	.poll		= do_poll,
   1.511 +	.ioctl		= do_ioctl,
   1.512 +	.open		= do_open,
   1.513 +	.release	= do_release,
   1.514 +};
   1.515 +
   1.516 +static struct miscdevice apm_device = {
   1.517 +	APM_MINOR_DEV,
   1.518 +	"apm_bios",
   1.519 +	&apm_bios_fops
   1.520 +};
   1.521 +
   1.522 +static int __init apm_emu_init(void)
   1.523 +{
   1.524 +	struct proc_dir_entry *apm_proc;
   1.525 +
   1.526 +	if (sys_ctrler != SYS_CTRLER_PMU) {
   1.527 +		printk(KERN_INFO "apm_emu: Requires a machine with a PMU.\n");
   1.528 +		return -ENODEV;
   1.529 +	}
   1.530 +		
   1.531 +	apm_proc = create_proc_info_entry("apm", 0, NULL, apm_emu_get_info);
   1.532 +	if (apm_proc)
   1.533 +		apm_proc->owner = THIS_MODULE;
   1.534 +
   1.535 +	misc_register(&apm_device);
   1.536 +
   1.537 +	pmu_register_sleep_notifier(&apm_sleep_notifier);
   1.538 +
   1.539 +	printk(KERN_INFO "apm_emu: APM Emulation %s initialized.\n", driver_version);
   1.540 +
   1.541 +	return 0;
   1.542 +}
   1.543 +
   1.544 +static void __exit apm_emu_exit(void)
   1.545 +{
   1.546 +	pmu_unregister_sleep_notifier(&apm_sleep_notifier);
   1.547 +	misc_deregister(&apm_device);
   1.548 +	remove_proc_entry("apm", NULL);
   1.549 +
   1.550 +	printk(KERN_INFO "apm_emu: APM Emulation removed.\n");
   1.551 +}
   1.552 +
   1.553 +module_init(apm_emu_init);
   1.554 +module_exit(apm_emu_exit);
   1.555 +
   1.556 +MODULE_AUTHOR("Benjamin Herrenschmidt");
   1.557 +MODULE_DESCRIPTION("APM emulation layer for PowerMac");
   1.558 +MODULE_LICENSE("GPL");
   1.559 +