⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 apm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
			/*			 * If we are already processing a SUSPEND,			 * then further SUSPEND events from the BIOS			 * will be ignored.  We also return here to			 * cope with the fact that the Thinkpads keep			 * sending a SUSPEND event until something else			 * happens!			 */			if (ignore_sys_suspend)				return;			ignore_sys_suspend = 1;			queue_event(event, NULL);			if (suspends_pending <= 0)				(void) suspend(1);			break;		case APM_NORMAL_RESUME:		case APM_CRITICAL_RESUME:		case APM_STANDBY_RESUME:			ignore_sys_suspend = 0;			last_resume = jiffies;			ignore_bounce = 1;			if ((event != APM_NORMAL_RESUME)			    || (ignore_normal_resume == 0)) {				write_seqlock_irq(&xtime_lock);				set_time();				write_sequnlock_irq(&xtime_lock);				device_resume();				pm_send_all(PM_RESUME, (void *)0);				queue_event(event, NULL);			}			ignore_normal_resume = 0;			break;		case APM_CAPABILITY_CHANGE:		case APM_LOW_BATTERY:		case APM_POWER_STATUS_CHANGE:			queue_event(event, NULL);			/* If needed, notify drivers here */			break;		case APM_UPDATE_TIME:			write_seqlock_irq(&xtime_lock);			set_time();			write_sequnlock_irq(&xtime_lock);			break;		case APM_CRITICAL_SUSPEND:			/*			 * We are not allowed to reject a critical suspend.			 */			(void) suspend(0);			break;		}	}}static void apm_event_handler(void){	static int	pending_count = 4;	int		err;	if ((standbys_pending > 0) || (suspends_pending > 0)) {		if ((apm_info.connection_version > 0x100) &&				(pending_count-- <= 0)) {			pending_count = 4;			if (debug)				printk(KERN_DEBUG "apm: setting state busy\n");			err = set_system_power_state(APM_STATE_BUSY);			if (err)				apm_error("busy", err);		}	} else		pending_count = 4;	check_events();}/* * This is the APM thread main loop. */static void apm_mainloop(void){	DECLARE_WAITQUEUE(wait, current);	add_wait_queue(&apm_waitqueue, &wait);	set_current_state(TASK_INTERRUPTIBLE);	for (;;) {		schedule_timeout(APM_CHECK_TIMEOUT);		if (exit_kapmd)			break;		/*		 * Ok, check all events, check for idle (and mark us sleeping		 * so as not to count towards the load average)..		 */		set_current_state(TASK_INTERRUPTIBLE);		apm_event_handler();	}	remove_wait_queue(&apm_waitqueue, &wait);}static int check_apm_user(struct apm_user *as, const char *func){	if ((as == NULL) || (as->magic != APM_BIOS_MAGIC)) {		printk(KERN_ERR "apm: %s passed bad filp\n", func);		return 1;	}	return 0;}static ssize_t do_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos){	struct apm_user *	as;	int			i;	apm_event_t		event;	as = fp->private_data;	if (check_apm_user(as, "read"))		return -EIO;	if ((int)count < sizeof(apm_event_t))		return -EINVAL;	if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))		return -EAGAIN;	wait_event_interruptible(apm_waitqueue, !queue_empty(as));	i = count;	while ((i >= sizeof(event)) && !queue_empty(as)) {		event = get_queued_event(as);		if (copy_to_user(buf, &event, sizeof(event))) {			if (i < count)				break;			return -EFAULT;		}		switch (event) {		case APM_SYS_SUSPEND:		case APM_USER_SUSPEND:			as->suspends_read++;			break;		case APM_SYS_STANDBY:		case APM_USER_STANDBY:			as->standbys_read++;			break;		}		buf += sizeof(event);		i -= sizeof(event);	}	if (i < count)		return count - i;	if (signal_pending(current))		return -ERESTARTSYS;	return 0;}static unsigned int do_poll(struct file *fp, poll_table * wait){	struct apm_user * as;	as = fp->private_data;	if (check_apm_user(as, "poll"))		return 0;	poll_wait(fp, &apm_waitqueue, wait);	if (!queue_empty(as))		return POLLIN | POLLRDNORM;	return 0;}static int do_ioctl(struct inode * inode, struct file *filp,		    u_int cmd, u_long arg){	struct apm_user *	as;	as = filp->private_data;	if (check_apm_user(as, "ioctl"))		return -EIO;	if ((!as->suser) || (!as->writer))		return -EPERM;	switch (cmd) {	case APM_IOC_STANDBY:		if (as->standbys_read > 0) {			as->standbys_read--;			as->standbys_pending--;			standbys_pending--;		} else			queue_event(APM_USER_STANDBY, as);		if (standbys_pending <= 0)			standby();		break;	case APM_IOC_SUSPEND:		if (as->suspends_read > 0) {			as->suspends_read--;			as->suspends_pending--;			suspends_pending--;		} else			queue_event(APM_USER_SUSPEND, as);		if (suspends_pending <= 0) {			return suspend(1);		} else {			as->suspend_wait = 1;			wait_event_interruptible(apm_suspend_waitqueue,					as->suspend_wait == 0);			return as->suspend_result;		}		break;	default:		return -EINVAL;	}	return 0;}static int do_release(struct inode * inode, struct file * filp){	struct apm_user *	as;	as = filp->private_data;	if (check_apm_user(as, "release"))		return 0;	filp->private_data = NULL;	if (as->standbys_pending > 0) {		standbys_pending -= as->standbys_pending;		if (standbys_pending <= 0)			standby();	}	if (as->suspends_pending > 0) {		suspends_pending -= as->suspends_pending;		if (suspends_pending <= 0)			(void) suspend(1);	}  	spin_lock(&user_list_lock);	if (user_list == as)		user_list = as->next;	else {		struct apm_user *	as1;		for (as1 = user_list;		     (as1 != NULL) && (as1->next != as);		     as1 = as1->next)			;		if (as1 == NULL)			printk(KERN_ERR "apm: filp not in user list\n");		else			as1->next = as->next;	}	spin_unlock(&user_list_lock);	kfree(as);	return 0;}static int do_open(struct inode * inode, struct file * filp){	struct apm_user *	as;	as = (struct apm_user *)kmalloc(sizeof(*as), GFP_KERNEL);	if (as == NULL) {		printk(KERN_ERR "apm: cannot allocate struct of size %d bytes\n",		       sizeof(*as));		return -ENOMEM;	}	as->magic = APM_BIOS_MAGIC;	as->event_tail = as->event_head = 0;	as->suspends_pending = as->standbys_pending = 0;	as->suspends_read = as->standbys_read = 0;	/*	 * XXX - this is a tiny bit broken, when we consider BSD         * process accounting. If the device is opened by root, we	 * instantly flag that we used superuser privs. Who knows,	 * we might close the device immediately without doing a	 * privileged operation -- cevans	 */	as->suser = capable(CAP_SYS_ADMIN);	as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;	as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;	spin_lock(&user_list_lock);	as->next = user_list;	user_list = as;	spin_unlock(&user_list_lock);	filp->private_data = as;	return 0;}static int apm_get_info(char *buf, char **start, off_t fpos, int length){	char *		p;	unsigned short	bx;	unsigned short	cx;	unsigned short	dx;	int		error;	unsigned short  ac_line_status = 0xff;	unsigned short  battery_status = 0xff;	unsigned short  battery_flag   = 0xff;	int		percentage     = -1;	int             time_units     = -1;	char            *units         = "?";	p = buf;	if ((num_online_cpus() == 1) &&	    !(error = apm_get_power_status(&bx, &cx, &dx))) {		ac_line_status = (bx >> 8) & 0xff;		battery_status = bx & 0xff;		if ((cx & 0xff) != 0xff)			percentage = cx & 0xff;		if (apm_info.connection_version > 0x100) {			battery_flag = (cx >> 8) & 0xff;			if (dx != 0xffff) {				units = (dx & 0x8000) ? "min" : "sec";				time_units = dx & 0x7fff;			}		}	}	/* Arguments, with symbols from linux/apm_bios.h.  Information is	   from the Get Power Status (0x0a) call unless otherwise noted.	   0) Linux driver version (this will change if format changes)	   1) APM BIOS Version.  Usually 1.0, 1.1 or 1.2.	   2) APM flags from APM Installation Check (0x00):	      bit 0: APM_16_BIT_SUPPORT	      bit 1: APM_32_BIT_SUPPORT	      bit 2: APM_IDLE_SLOWS_CLOCK	      bit 3: APM_BIOS_DISABLED	      bit 4: APM_BIOS_DISENGAGED	   3) AC line status	      0x00: Off-line	      0x01: On-line	      0x02: On backup power (BIOS >= 1.1 only)	      0xff: Unknown	   4) Battery status	      0x00: High	      0x01: Low	      0x02: Critical	      0x03: Charging	      0x04: Selected battery not present (BIOS >= 1.2 only)	      0xff: Unknown	   5) Battery flag	      bit 0: High	      bit 1: Low	      bit 2: Critical	      bit 3: Charging	      bit 7: No system battery	      0xff: Unknown	   6) Remaining battery life (percentage of charge):	      0-100: valid	      -1: Unknown	   7) Remaining battery life (time units):	      Number of remaining minutes or seconds	      -1: Unknown	   8) min = minutes; sec = seconds */	p += sprintf(p, "%s %d.%d 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",		     driver_version,		     (apm_info.bios.version >> 8) & 0xff,		     apm_info.bios.version & 0xff,		     apm_info.bios.flags,		     ac_line_status,		     battery_status,		     battery_flag,		     percentage,		     time_units,		     units);	return p - buf;}static int apm(void *unused){	unsigned short	bx;	unsigned short	cx;	unsigned short	dx;	int		error;	char *		power_stat;	char *		bat_stat;	kapmd_running = 1;	daemonize("kapmd");	current->flags |= PF_NOFREEZE;#ifdef CONFIG_SMP	/* 2002/08/01 - WT	 * This is to avoid random crashes at boot time during initialization	 * on SMP systems in case of "apm=power-off" mode. Seen on ASUS A7M266D.	 * Some bioses don't like being called from CPU != 0.	 * Method suggested by Ingo Molnar.	 */	set_cpus_allowed(current, cpumask_of_cpu(0));	BUG_ON(smp_processor_id() != 0);#endif	if (apm_info.connection_version == 0) {		apm_info.connection_version = apm_info.bios.version;		if (apm_info.connection_version > 0x100) {			/*			 * We only support BIOSs up to version 1.2			 */			if (apm_info.connection_version > 0x0102)				apm_info.connection_version = 0x0102;			error = apm_driver_version(&apm_info.connection_version);			if (error != APM_SUCCESS) {				apm_error("driver version", error);				/* Fall back to an APM 1.0 connection. */				apm_info.connection_version = 0x100;			}		}	}	if (debug)		printk(KERN_INFO "apm: Connection version %d.%d\n",			(apm_info.connection_version >> 8) & 0xff,			apm_info.connection_version & 0xff);#ifdef CONFIG_APM_DO_ENABLE	if (apm_info.bios.flags & APM_BIOS_DISABLED) {		/*		 * This call causes my NEC UltraLite Versa 33/C to hang if it		 * is booted with PM disabled but not in the docking station.		 * Unfortunate ...		 */		error = apm_enable_power_management(1);		if (error) {			apm_error("enable power management", error);			return -1;		}	}#endif	if ((apm_info.bios.flags & APM_BIOS_DISENGAGED)	    && (apm_info.connection_version > 0x0100)) {		error = apm_engage_power_management(APM_DEVICE_ALL, 1);		if (error) {			apm_error("engage power management", error);			return -1;		}	}	if (debug && (num_online_cpus() == 1 || smp )) {		error = apm_get_power_status(&bx, &cx, &dx);		if (error)			printk(KERN_INFO "apm: power status not available\n");		else {			switch ((bx >> 8) & 0xff) {			case 0: power_stat = "off line"; break;			case 1: power_stat = "on line"; break;			case 2: power_stat = "on backup power"; break;			default: power_stat = "unknown"; break;			}			switch (bx & 0xff) {			case 0: bat_stat = "high"; break;			case 1: bat_stat = "low"; break;			case 2: bat_stat = "critical"; break;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -