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

📄 aml_nfw.c

📁 linux内核源码
💻 C
字号:
/* * OpRegion handler to allow AML to call native firmware * * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. *	Bjorn Helgaas <bjorn.helgaas@hp.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This driver implements HP Open Source Review Board proposal 1842, * which was approved on 9/20/2006. * * For technical documentation, see the HP SPPA Firmware EAS, Appendix F. * * ACPI does not define a mechanism for AML methods to call native firmware * interfaces such as PAL or SAL.  This OpRegion handler adds such a mechanism. * After the handler is installed, an AML method can call native firmware by * storing the arguments and firmware entry point to specific offsets in the * OpRegion.  When AML reads the "return value" offset from the OpRegion, this * handler loads up the arguments, makes the firmware call, and returns the * result. */#include <linux/module.h>#include <acpi/acpi_bus.h>#include <acpi/acpi_drivers.h>#include <asm/sal.h>MODULE_AUTHOR("Bjorn Helgaas <bjorn.helgaas@hp.com>");MODULE_LICENSE("GPL");MODULE_DESCRIPTION("ACPI opregion handler for native firmware calls");static int force_register;module_param_named(force, force_register, bool, 0);MODULE_PARM_DESC(force, "Install opregion handler even without HPQ5001 device");#define AML_NFW_SPACE		0xA1struct ia64_pdesc {	void *ip;	void *gp;};/* * N.B.  The layout of this structure is defined in the HP SPPA FW EAS, and *	 the member offsets are embedded in AML methods. */struct ia64_nfw_context {	u64 arg[8];	struct ia64_sal_retval ret;	u64 ip;	u64 gp;	u64 pad[2];};static void *virt_map(u64 address){	if (address & (1UL << 63))		return (void *) (__IA64_UNCACHED_OFFSET | address);	return __va(address);}static void aml_nfw_execute(struct ia64_nfw_context *c){	struct ia64_pdesc virt_entry;	ia64_sal_handler entry;	virt_entry.ip = virt_map(c->ip);	virt_entry.gp = virt_map(c->gp);	entry = (ia64_sal_handler) &virt_entry;	IA64_FW_CALL(entry, c->ret,		     c->arg[0], c->arg[1], c->arg[2], c->arg[3],		     c->arg[4], c->arg[5], c->arg[6], c->arg[7]);}static void aml_nfw_read_arg(u8 *offset, u32 bit_width, acpi_integer *value){	switch (bit_width) {	case 8:		*value = *(u8 *)offset;		break;	case 16:		*value = *(u16 *)offset;		break;	case 32:		*value = *(u32 *)offset;		break;	case 64:		*value = *(u64 *)offset;		break;	}}static void aml_nfw_write_arg(u8 *offset, u32 bit_width, acpi_integer *value){	switch (bit_width) {	case 8:		*(u8 *) offset = *value;		break;	case 16:		*(u16 *) offset = *value;		break;	case 32:		*(u32 *) offset = *value;		break;	case 64:		*(u64 *) offset = *value;		break;	}}static acpi_status aml_nfw_handler(u32 function, acpi_physical_address address,	u32 bit_width, acpi_integer *value, void *handler_context,	void *region_context){	struct ia64_nfw_context *context = handler_context;	u8 *offset = (u8 *) context + address;	if (bit_width !=  8 && bit_width != 16 &&	    bit_width != 32 && bit_width != 64)		return AE_BAD_PARAMETER;	if (address + (bit_width >> 3) > sizeof(struct ia64_nfw_context))		return AE_BAD_PARAMETER;	switch (function) {	case ACPI_READ:		if (address == offsetof(struct ia64_nfw_context, ret))			aml_nfw_execute(context);		aml_nfw_read_arg(offset, bit_width, value);		break;	case ACPI_WRITE:		aml_nfw_write_arg(offset, bit_width, value);		break;	}	return AE_OK;}static struct ia64_nfw_context global_context;static int global_handler_registered;static int aml_nfw_add_global_handler(void){	acpi_status status;	if (global_handler_registered)		return 0;	status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,		AML_NFW_SPACE, aml_nfw_handler, NULL, &global_context);	if (ACPI_FAILURE(status))		return -ENODEV;	global_handler_registered = 1;	printk(KERN_INFO "Global 0x%02X opregion handler registered\n",		AML_NFW_SPACE);	return 0;}static int aml_nfw_remove_global_handler(void){	acpi_status status;	if (!global_handler_registered)		return 0;	status = acpi_remove_address_space_handler(ACPI_ROOT_OBJECT,		AML_NFW_SPACE, aml_nfw_handler);	if (ACPI_FAILURE(status))		return -ENODEV;	global_handler_registered = 0;	printk(KERN_INFO "Global 0x%02X opregion handler removed\n",		AML_NFW_SPACE);	return 0;}static int aml_nfw_add(struct acpi_device *device){	/*	 * We would normally allocate a new context structure and install	 * the address space handler for the specific device we found.	 * But the HP-UX implementation shares a single global context	 * and always puts the handler at the root, so we'll do the same.	 */	return aml_nfw_add_global_handler();}static int aml_nfw_remove(struct acpi_device *device, int type){	return aml_nfw_remove_global_handler();}static const struct acpi_device_id aml_nfw_ids[] = {	{"HPQ5001", 0},	{"", 0}};static struct acpi_driver acpi_aml_nfw_driver = {	.name = "native firmware",	.ids = aml_nfw_ids,	.ops = {		.add = aml_nfw_add,		.remove = aml_nfw_remove,		},};static int __init aml_nfw_init(void){	int result;	if (force_register)		aml_nfw_add_global_handler();	result = acpi_bus_register_driver(&acpi_aml_nfw_driver);	if (result < 0) {		aml_nfw_remove_global_handler();		return result;	}	return 0;}static void __exit aml_nfw_exit(void){	acpi_bus_unregister_driver(&acpi_aml_nfw_driver);	aml_nfw_remove_global_handler();}module_init(aml_nfw_init);module_exit(aml_nfw_exit);

⌨️ 快捷键说明

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