scan.c

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

C
1,500
字号
/* * scan.c - support for transforming the ACPI namespace into individual objects */#include <linux/module.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/acpi.h>#include <acpi/acpi_drivers.h>#include <acpi/acinterp.h>	/* for acpi_ex_eisa_id_to_string() */#define _COMPONENT		ACPI_BUS_COMPONENTACPI_MODULE_NAME("scan");#define STRUCT_TO_INT(s)	(*((int*)&s))extern struct acpi_device *acpi_root;#define ACPI_BUS_CLASS			"system_bus"#define ACPI_BUS_HID			"LNXSYBUS"#define ACPI_BUS_DEVICE_NAME		"System Bus"static LIST_HEAD(acpi_device_list);static LIST_HEAD(acpi_bus_id_list);DEFINE_SPINLOCK(acpi_device_lock);LIST_HEAD(acpi_wakeup_device_list);struct acpi_device_bus_id{	char bus_id[15];	unsigned int instance_no;	struct list_head node;};/* * Creates hid/cid(s) string needed for modalias and uevent * e.g. on a device with hid:IBM0001 and cid:ACPI0001 you get: * char *modalias: "acpi:IBM0001:ACPI0001"*/static int create_modalias(struct acpi_device *acpi_dev, char *modalias,			   int size){	int len;	if (!acpi_dev->flags.hardware_id)		return -ENODEV;	len = snprintf(modalias, size, "acpi:%s:",		       acpi_dev->pnp.hardware_id);	if (len < 0 || len >= size)		return -EINVAL;	size -= len;	if (acpi_dev->flags.compatible_ids) {		struct acpi_compatible_id_list *cid_list;		int i;		int count;		cid_list = acpi_dev->pnp.cid_list;		for (i = 0; i < cid_list->count; i++) {			count = snprintf(&modalias[len], size, "%s:",					 cid_list->id[i].value);			if (count < 0 || count >= size) {				printk(KERN_ERR "acpi: %s cid[%i] exceeds event buffer size",				       acpi_dev->pnp.device_name, i);				break;			}			len += count;			size -= count;		}	}	modalias[len] = '\0';	return len;}static ssize_tacpi_device_modalias_show(struct device *dev, struct device_attribute *attr, char *buf) {	struct acpi_device *acpi_dev = to_acpi_device(dev);	int len;	/* Device has no HID and no CID or string is >1024 */	len = create_modalias(acpi_dev, buf, 1024);	if (len <= 0)		return 0;	buf[len++] = '\n';	return len;}static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL);static int acpi_eject_operation(acpi_handle handle, int lockable){	struct acpi_object_list arg_list;	union acpi_object arg;	acpi_status status = AE_OK;	/*	 * TBD: evaluate _PS3?	 */	if (lockable) {		arg_list.count = 1;		arg_list.pointer = &arg;		arg.type = ACPI_TYPE_INTEGER;		arg.integer.value = 0;		acpi_evaluate_object(handle, "_LCK", &arg_list, NULL);	}	arg_list.count = 1;	arg_list.pointer = &arg;	arg.type = ACPI_TYPE_INTEGER;	arg.integer.value = 1;	/*	 * TBD: _EJD support.	 */	status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);	if (ACPI_FAILURE(status)) {		return (-ENODEV);	}	return (0);}static ssize_tacpi_eject_store(struct device *d, struct device_attribute *attr,		const char *buf, size_t count){	int result;	int ret = count;	int islockable;	acpi_status status;	acpi_handle handle;	acpi_object_type type = 0;	struct acpi_device *acpi_device = to_acpi_device(d);	if ((!count) || (buf[0] != '1')) {		return -EINVAL;	}#ifndef FORCE_EJECT	if (acpi_device->driver == NULL) {		ret = -ENODEV;		goto err;	}#endif	status = acpi_get_type(acpi_device->handle, &type);	if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) {		ret = -ENODEV;		goto err;	}	islockable = acpi_device->flags.lockable;	handle = acpi_device->handle;	result = acpi_bus_trim(acpi_device, 1);	if (!result)		result = acpi_eject_operation(handle, islockable);	if (result) {		ret = -EBUSY;	}      err:	return ret;}static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);static ssize_tacpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) {	struct acpi_device *acpi_dev = to_acpi_device(dev);	return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id);}static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL);static ssize_tacpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) {	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};	int result;	result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path);	if(result)		goto end;	result = sprintf(buf, "%s\n", (char*)path.pointer);	kfree(path.pointer);  end:	return result;}static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL);static int acpi_device_setup_files(struct acpi_device *dev){	acpi_status status;	acpi_handle temp;	int result = 0;	/*	 * Devices gotten from FADT don't have a "path" attribute	 */	if(dev->handle) {		result = device_create_file(&dev->dev, &dev_attr_path);		if(result)			goto end;	}	if(dev->flags.hardware_id) {		result = device_create_file(&dev->dev, &dev_attr_hid);		if(result)			goto end;	}	if (dev->flags.hardware_id || dev->flags.compatible_ids){		result = device_create_file(&dev->dev, &dev_attr_modalias);		if(result)			goto end;	}        /*         * If device has _EJ0, 'eject' file is created that is used to trigger         * hot-removal function from userland.         */	status = acpi_get_handle(dev->handle, "_EJ0", &temp);	if (ACPI_SUCCESS(status))		result = device_create_file(&dev->dev, &dev_attr_eject);  end:	return result;}static void acpi_device_remove_files(struct acpi_device *dev){	acpi_status status;	acpi_handle temp;	/*	 * If device has _EJ0, 'eject' file is created that is used to trigger	 * hot-removal function from userland.	 */	status = acpi_get_handle(dev->handle, "_EJ0", &temp);	if (ACPI_SUCCESS(status))		device_remove_file(&dev->dev, &dev_attr_eject);	if (dev->flags.hardware_id || dev->flags.compatible_ids)		device_remove_file(&dev->dev, &dev_attr_modalias);	if(dev->flags.hardware_id)		device_remove_file(&dev->dev, &dev_attr_hid);	if(dev->handle)		device_remove_file(&dev->dev, &dev_attr_path);}/* --------------------------------------------------------------------------			ACPI Bus operations   -------------------------------------------------------------------------- */int acpi_match_device_ids(struct acpi_device *device,			  const struct acpi_device_id *ids){	const struct acpi_device_id *id;	if (device->flags.hardware_id) {		for (id = ids; id->id[0]; id++) {			if (!strcmp((char*)id->id, device->pnp.hardware_id))				return 0;		}	}	if (device->flags.compatible_ids) {		struct acpi_compatible_id_list *cid_list = device->pnp.cid_list;		int i;		for (id = ids; id->id[0]; id++) {			/* compare multiple _CID entries against driver ids */			for (i = 0; i < cid_list->count; i++) {				if (!strcmp((char*)id->id,					    cid_list->id[i].value))					return 0;			}		}	}	return -ENOENT;}EXPORT_SYMBOL(acpi_match_device_ids);static void acpi_device_release(struct device *dev){	struct acpi_device *acpi_dev = to_acpi_device(dev);	kfree(acpi_dev->pnp.cid_list);	kfree(acpi_dev);}static int acpi_device_suspend(struct device *dev, pm_message_t state){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = acpi_dev->driver;	if (acpi_drv && acpi_drv->ops.suspend)		return acpi_drv->ops.suspend(acpi_dev, state);	return 0;}static int acpi_device_resume(struct device *dev){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = acpi_dev->driver;	if (acpi_drv && acpi_drv->ops.resume)		return acpi_drv->ops.resume(acpi_dev);	return 0;}static int acpi_bus_match(struct device *dev, struct device_driver *drv){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = to_acpi_driver(drv);	return !acpi_match_device_ids(acpi_dev, acpi_drv->ids);}static int acpi_device_uevent(struct device *dev, struct kobj_uevent_env *env){	struct acpi_device *acpi_dev = to_acpi_device(dev);	int len;	if (add_uevent_var(env, "MODALIAS="))		return -ENOMEM;	len = create_modalias(acpi_dev, &env->buf[env->buflen - 1],			      sizeof(env->buf) - env->buflen);	if (len >= (sizeof(env->buf) - env->buflen))		return -ENOMEM;	env->buflen += len;	return 0;}static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *);static int acpi_start_single_object(struct acpi_device *);static int acpi_device_probe(struct device * dev){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver);	int ret;	ret = acpi_bus_driver_init(acpi_dev, acpi_drv);	if (!ret) {		if (acpi_dev->bus_ops.acpi_op_start)			acpi_start_single_object(acpi_dev);		ACPI_DEBUG_PRINT((ACPI_DB_INFO,			"Found driver [%s] for device [%s]\n",			acpi_drv->name, acpi_dev->pnp.bus_id));		get_device(dev);	}	return ret;}static int acpi_device_remove(struct device * dev){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = acpi_dev->driver;	if (acpi_drv) {		if (acpi_drv->ops.stop)			acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type);		if (acpi_drv->ops.remove)			acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type);	}	acpi_dev->driver = NULL;	acpi_driver_data(dev) = NULL;	put_device(dev);	return 0;}static void acpi_device_shutdown(struct device *dev){	struct acpi_device *acpi_dev = to_acpi_device(dev);	struct acpi_driver *acpi_drv = acpi_dev->driver;	if (acpi_drv && acpi_drv->ops.shutdown)		acpi_drv->ops.shutdown(acpi_dev);	return ;}struct bus_type acpi_bus_type = {	.name		= "acpi",	.suspend	= acpi_device_suspend,	.resume		= acpi_device_resume,	.shutdown	= acpi_device_shutdown,	.match		= acpi_bus_match,	.probe		= acpi_device_probe,	.remove		= acpi_device_remove,	.uevent		= acpi_device_uevent,};static int acpi_device_register(struct acpi_device *device,				 struct acpi_device *parent){	int result;	struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id;	int found = 0;	/*	 * Linkage	 * -------	 * Link this device to its parent and siblings.	 */	INIT_LIST_HEAD(&device->children);	INIT_LIST_HEAD(&device->node);	INIT_LIST_HEAD(&device->g_list);	INIT_LIST_HEAD(&device->wakeup_list);	new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL);	if (!new_bus_id) {		printk(KERN_ERR PREFIX "Memory allocation error\n");		return -ENOMEM;	}	spin_lock(&acpi_device_lock);	/*	 * Find suitable bus_id and instance number in acpi_bus_id_list	 * If failed, create one and link it into acpi_bus_id_list	 */	list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) {		if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "device")) {			acpi_device_bus_id->instance_no ++;			found = 1;			kfree(new_bus_id);			break;		}	}	if(!found) {		acpi_device_bus_id = new_bus_id;		strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device");		acpi_device_bus_id->instance_no = 0;		list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list);	}	sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no);	if (device->parent) {		list_add_tail(&device->node, &device->parent->children);		list_add_tail(&device->g_list, &device->parent->g_list);	} else		list_add_tail(&device->g_list, &acpi_device_list);	if (device->wakeup.flags.valid)		list_add_tail(&device->wakeup_list, &acpi_wakeup_device_list);	spin_unlock(&acpi_device_lock);	if (device->parent)		device->dev.parent = &parent->dev;	device->dev.bus = &acpi_bus_type;	device_initialize(&device->dev);	device->dev.release = &acpi_device_release;	result = device_add(&device->dev);	if(result) {		printk("Error adding device %s", device->dev.bus_id);		goto end;	}	result = acpi_device_setup_files(device);	if(result)		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error creating sysfs interface for device %s\n", device->dev.bus_id));	device->removal_type = ACPI_BUS_REMOVAL_NORMAL;	return 0;  end:	spin_lock(&acpi_device_lock);	if (device->parent) {		list_del(&device->node);		list_del(&device->g_list);	} else		list_del(&device->g_list);	list_del(&device->wakeup_list);	spin_unlock(&acpi_device_lock);	return result;}static void acpi_device_unregister(struct acpi_device *device, int type){	spin_lock(&acpi_device_lock);	if (device->parent) {		list_del(&device->node);		list_del(&device->g_list);	} else		list_del(&device->g_list);	list_del(&device->wakeup_list);	spin_unlock(&acpi_device_lock);	acpi_detach_data(device->handle, acpi_bus_data_handler);	acpi_device_remove_files(device);	device_unregister(&device->dev);}/* --------------------------------------------------------------------------                                 Driver Management   -------------------------------------------------------------------------- *//** * acpi_bus_driver_init - add a device to a driver

⌨️ 快捷键说明

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