thinkpad_acpi.c

来自「linux 内核源代码」· C语言 代码 · 共 2,636 行 · 第 1/5 页

C
2,636
字号
		if (tp_features.hotkey_mask) {			if (!acpi_evalf(hkey_handle, &hotkey_all_mask,					"MHKA", "qd")) {				printk(IBM_ERR				       "missing MHKA handler, "				       "please report this to %s\n",				       IBM_MAIL);				hotkey_all_mask = 0x080cU; /* FN+F12, FN+F4, FN+F3 */			}		}		res = hotkey_get(&hotkey_orig_status, &hotkey_orig_mask);		if (!res && tp_features.hotkey_mask) {			res = add_many_to_attr_set(hotkey_dev_attributes,				hotkey_mask_attributes,				ARRAY_SIZE(hotkey_mask_attributes));		}		/* Not all thinkpads have a hardware radio switch */		if (!res && acpi_evalf(hkey_handle, &status, "WLSW", "qd")) {			tp_features.hotkey_wlsw = 1;			printk(IBM_INFO				"radio switch found; radios are %s\n",				enabled(status, 0));			res = add_to_attr_set(hotkey_dev_attributes,					&dev_attr_hotkey_radio_sw.attr);		}		if (!res)			res = register_attr_set_with_sysfs(					hotkey_dev_attributes,					&tpacpi_pdev->dev.kobj);		if (res)			return res;		/* Set up key map */		hotkey_keycode_map = kmalloc(TPACPI_HOTKEY_MAP_SIZE,						GFP_KERNEL);		if (!hotkey_keycode_map) {			printk(IBM_ERR "failed to allocate memory for key map\n");			return -ENOMEM;		}		if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) {			dbg_printk(TPACPI_DBG_INIT,				   "using Lenovo default hot key map\n");			memcpy(hotkey_keycode_map, &lenovo_keycode_map,				TPACPI_HOTKEY_MAP_SIZE);		} else {			dbg_printk(TPACPI_DBG_INIT,				   "using IBM default hot key map\n");			memcpy(hotkey_keycode_map, &ibm_keycode_map,				TPACPI_HOTKEY_MAP_SIZE);		}		set_bit(EV_KEY, tpacpi_inputdev->evbit);		set_bit(EV_MSC, tpacpi_inputdev->evbit);		set_bit(MSC_SCAN, tpacpi_inputdev->mscbit);		tpacpi_inputdev->keycodesize = TPACPI_HOTKEY_MAP_TYPESIZE;		tpacpi_inputdev->keycodemax = TPACPI_HOTKEY_MAP_LEN;		tpacpi_inputdev->keycode = hotkey_keycode_map;		for (i = 0; i < TPACPI_HOTKEY_MAP_LEN; i++) {			if (hotkey_keycode_map[i] != KEY_RESERVED) {				set_bit(hotkey_keycode_map[i],					tpacpi_inputdev->keybit);			} else {				if (i < sizeof(hotkey_reserved_mask)*8)					hotkey_reserved_mask |= 1 << i;			}		}		if (tp_features.hotkey_wlsw) {			set_bit(EV_SW, tpacpi_inputdev->evbit);			set_bit(SW_RADIO, tpacpi_inputdev->swbit);		}		dbg_printk(TPACPI_DBG_INIT,				"enabling hot key handling\n");		res = hotkey_set(1, (hotkey_all_mask & ~hotkey_reserved_mask)					| hotkey_orig_mask);		if (res)			return res;		dbg_printk(TPACPI_DBG_INIT,				"legacy hot key reporting over procfs %s\n",				(hotkey_report_mode < 2) ?					"enabled" : "disabled");	}	return (tp_features.hotkey)? 0 : 1;}static void hotkey_exit(void){	int res;	if (tp_features.hotkey) {		dbg_printk(TPACPI_DBG_EXIT, "restoring original hotkey mask\n");		res = hotkey_set(hotkey_orig_status, hotkey_orig_mask);		if (res)			printk(IBM_ERR "failed to restore hotkey to BIOS defaults\n");	}	if (hotkey_dev_attributes) {		delete_attr_set(hotkey_dev_attributes, &tpacpi_pdev->dev.kobj);		hotkey_dev_attributes = NULL;	}}static void tpacpi_input_send_key(unsigned int scancode,				  unsigned int keycode){	if (keycode != KEY_RESERVED) {		mutex_lock(&tpacpi_inputdev_send_mutex);		input_report_key(tpacpi_inputdev, keycode, 1);		if (keycode == KEY_UNKNOWN)			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,				    scancode);		input_sync(tpacpi_inputdev);		input_report_key(tpacpi_inputdev, keycode, 0);		if (keycode == KEY_UNKNOWN)			input_event(tpacpi_inputdev, EV_MSC, MSC_SCAN,				    scancode);		input_sync(tpacpi_inputdev);		mutex_unlock(&tpacpi_inputdev_send_mutex);	}}static void tpacpi_input_send_radiosw(void){	int wlsw;	mutex_lock(&tpacpi_inputdev_send_mutex);	if (tp_features.hotkey_wlsw && !hotkey_get_wlsw(&wlsw)) {		input_report_switch(tpacpi_inputdev,				    SW_RADIO, !!wlsw);		input_sync(tpacpi_inputdev);	}	mutex_unlock(&tpacpi_inputdev_send_mutex);}static void hotkey_notify(struct ibm_struct *ibm, u32 event){	u32 hkey;	unsigned int keycode, scancode;	int send_acpi_ev;	int ignore_acpi_ev;	if (event != 0x80) {		printk(IBM_ERR "unknown HKEY notification event %d\n", event);		/* forward it to userspace, maybe it knows how to handle it */		acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,						ibm->acpi->device->dev.bus_id,						event, 0);		return;	}	while (1) {		if (!acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) {			printk(IBM_ERR "failed to retrieve HKEY event\n");			return;		}		if (hkey == 0) {			/* queue empty */			return;		}		send_acpi_ev = 0;		ignore_acpi_ev = 0;		switch (hkey >> 12) {		case 1:			/* 0x1000-0x1FFF: key presses */			scancode = hkey & 0xfff;			if (scancode > 0 && scancode < 0x21) {				scancode--;				keycode = hotkey_keycode_map[scancode];				tpacpi_input_send_key(scancode, keycode);			} else {				printk(IBM_ERR				       "hotkey 0x%04x out of range for keyboard map\n",				       hkey);				send_acpi_ev = 1;			}			break;		case 5:			/* 0x5000-0x5FFF: LID */			/* we don't handle it through this path, just			 * eat up known LID events */			if (hkey != 0x5001 && hkey != 0x5002) {				printk(IBM_ERR				       "unknown LID-related HKEY event: 0x%04x\n",				       hkey);				send_acpi_ev = 1;			} else {				ignore_acpi_ev = 1;			}			break;		case 7:			/* 0x7000-0x7FFF: misc */			if (tp_features.hotkey_wlsw && hkey == 0x7000) {				tpacpi_input_send_radiosw();				break;			}			/* fallthrough to default */		default:			/* case 2: dock-related */			/*	0x2305 - T43 waking up due to bay lever eject while aslept */			/* case 3: ultra-bay related. maybe bay in dock? */			/*	0x3003 - T43 after wake up by bay lever eject (0x2305) */			printk(IBM_NOTICE "unhandled HKEY event 0x%04x\n", hkey);			send_acpi_ev = 1;		}		/* Legacy events */		if (!ignore_acpi_ev && (send_acpi_ev || hotkey_report_mode < 2)) {			acpi_bus_generate_proc_event(ibm->acpi->device, event, hkey);		}		/* netlink events */		if (!ignore_acpi_ev && send_acpi_ev) {			acpi_bus_generate_netlink_event(ibm->acpi->device->pnp.device_class,							ibm->acpi->device->dev.bus_id,							event, hkey);		}	}}static void hotkey_resume(void){	tpacpi_input_send_radiosw();}/* * Call with hotkey_mutex held */static int hotkey_get(int *status, u32 *mask){	if (!acpi_evalf(hkey_handle, status, "DHKC", "d"))		return -EIO;	if (tp_features.hotkey_mask)		if (!acpi_evalf(hkey_handle, mask, "DHKN", "d"))			return -EIO;	return 0;}/* * Call with hotkey_mutex held */static int hotkey_set(int status, u32 mask){	int i;	if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status))		return -EIO;	if (tp_features.hotkey_mask)		for (i = 0; i < 32; i++) {			int bit = ((1 << i) & mask) != 0;			if (!acpi_evalf(hkey_handle,					NULL, "MHKM", "vdd", i + 1, bit))				return -EIO;		}	return 0;}/* procfs -------------------------------------------------------------- */static int hotkey_read(char *p){	int res, status;	u32 mask;	int len = 0;	if (!tp_features.hotkey) {		len += sprintf(p + len, "status:\t\tnot supported\n");		return len;	}	if (mutex_lock_interruptible(&hotkey_mutex))		return -ERESTARTSYS;	res = hotkey_get(&status, &mask);	mutex_unlock(&hotkey_mutex);	if (res)		return res;	len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0));	if (tp_features.hotkey_mask) {		len += sprintf(p + len, "mask:\t\t0x%08x\n", mask);		len += sprintf(p + len,			       "commands:\tenable, disable, reset, <mask>\n");	} else {		len += sprintf(p + len, "mask:\t\tnot supported\n");		len += sprintf(p + len, "commands:\tenable, disable, reset\n");	}	return len;}static int hotkey_write(char *buf){	int res, status;	u32 mask;	char *cmd;	int do_cmd = 0;	if (!tp_features.hotkey)		return -ENODEV;	if (mutex_lock_interruptible(&hotkey_mutex))		return -ERESTARTSYS;	res = hotkey_get(&status, &mask);	if (res)		goto errexit;	res = 0;	while ((cmd = next_cmd(&buf))) {		if (strlencmp(cmd, "enable") == 0) {			status = 1;		} else if (strlencmp(cmd, "disable") == 0) {			status = 0;		} else if (strlencmp(cmd, "reset") == 0) {			status = hotkey_orig_status;			mask = hotkey_orig_mask;		} else if (sscanf(cmd, "0x%x", &mask) == 1) {			/* mask set */		} else if (sscanf(cmd, "%x", &mask) == 1) {			/* mask set */		} else {			res = -EINVAL;			goto errexit;		}		do_cmd = 1;	}	if (do_cmd)		res = hotkey_set(status, mask);errexit:	mutex_unlock(&hotkey_mutex);	return res;}static const struct acpi_device_id ibm_htk_device_ids[] = {	{IBM_HKEY_HID, 0},	{"", 0},};static struct tp_acpi_drv_struct ibm_hotkey_acpidriver = {	.hid = ibm_htk_device_ids,	.notify = hotkey_notify,	.handle = &hkey_handle,	.type = ACPI_DEVICE_NOTIFY,};static struct ibm_struct hotkey_driver_data = {	.name = "hotkey",	.read = hotkey_read,	.write = hotkey_write,	.exit = hotkey_exit,	.resume = hotkey_resume,	.acpi = &ibm_hotkey_acpidriver,};/************************************************************************* * Bluetooth subdriver *//* sysfs bluetooth enable ---------------------------------------------- */static ssize_t bluetooth_enable_show(struct device *dev,			   struct device_attribute *attr,			   char *buf){	int status;	status = bluetooth_get_radiosw();	if (status < 0)		return status;	return snprintf(buf, PAGE_SIZE, "%d\n", status ? 1 : 0);}static ssize_t bluetooth_enable_store(struct device *dev,			    struct device_attribute *attr,			    const char *buf, size_t count){	unsigned long t;	int res;	if (parse_strtoul(buf, 1, &t))		return -EINVAL;	res = bluetooth_set_radiosw(t);	return (res) ? res : count;}static struct device_attribute dev_attr_bluetooth_enable =	__ATTR(bluetooth_enable, S_IWUSR | S_IRUGO,		bluetooth_enable_show, bluetooth_enable_store);/* --------------------------------------------------------------------- */static struct attribute *bluetooth_attributes[] = {	&dev_attr_bluetooth_enable.attr,	NULL};static const struct attribute_group bluetooth_attr_group = {	.attrs = bluetooth_attributes,};static int __init bluetooth_init(struct ibm_init_struct *iibm){	int res;	int status = 0;	vdbg_printk(TPACPI_DBG_INIT, "initializing bluetooth subdriver\n");	IBM_ACPIHANDLE_INIT(hkey);	/* bluetooth not supported on 570, 600e/x, 770e, 770x, A21e, A2xm/p,	   G4x, R30, R31, R40e, R50e, T20-22, X20-21 */	tp_features.bluetooth = hkey_handle &&	    acpi_evalf(hkey_handle, &status, "GBDC", "qd");	vdbg_printk(TPACPI_DBG_INIT, "bluetooth is %s, status 0x%02x\n",		str_supported(tp_features.bluetooth),		status);	if (tp_features.bluetooth) {		if (!(status & TP_ACPI_BLUETOOTH_HWPRESENT)) {			/* no bluetooth hardware present in system */			tp_features.bluetooth = 0;			dbg_printk(TPACPI_DBG_INIT,				   "bluetooth hardware not installed\n");		} else {			res = sysfs_create_group(&tpacpi_pdev->dev.kobj,					&bluetooth_attr_group);			if (res)				return res;		}	}	return (tp_features.bluetooth)? 0 : 1;}static void bluetooth_exit(void){	sysfs_remove_group(&tpacpi_pdev->dev.kobj,			&bluetooth_attr_group);}static int bluetooth_get_radiosw(void){	int status;	if (!tp_features.bluetooth)		return -ENODEV;	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))		return -EIO;	return ((status & TP_ACPI_BLUETOOTH_RADIOSSW) != 0);}static int bluetooth_set_radiosw(int radio_on){	int status;	if (!tp_features.bluetooth)		return -ENODEV;	if (!acpi_evalf(hkey_handle, &status, "GBDC", "d"))		return -EIO;	if (radio_on)		status |= TP_ACPI_BLUETOOTH_RADIOSSW;	else		status &= ~TP_ACPI_BLUETOOTH_RADIOSSW;	if (!acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status))		return -EIO;	return 0;}/* procfs -------------------------------------------------------------- */static int bluetooth_read(char *p){	int len = 0;	int status = bluetooth_get_radiosw();	if (!tp_features.bluetooth)		len += sprintf(p + len, "status:\t\tnot supported\n");	else {		len += sprintf(p + len, "status:\t\t%s\n",				(status)? "enabled" : "disabled");		len += sprintf(p + len, "commands:\tenable, disable\n");	}	return len;}static int bluetooth_write(char *buf){	char *cmd;	if (!tp_features.bluetooth)		return -ENODEV;	while ((cmd = next_cmd(&buf))) {		if (strlencmp(cmd, "enable") == 0) {			bluetooth_set_radiosw(1);		} else if (strlencmp(cmd, "disable") == 0) {			bluetooth_set_radiosw(0);		} else			return -EINVAL;	}

⌨️ 快捷键说明

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