Skip to contentSkip to conversation

ACPI devices _HID export through sysfs

PATCH In review [email protected] 2 participants 2 messages Jul 8, 2006 – Jul 10, 2006

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 ACPI devices _HID export through sysfs

Hi

I recently thought about using coldplug (/etc/hotplug/*.rc) scripts to automagically load modules responsible for ACPI-controlled devices such as button, battery, laptop extras. To accomplish this I wrote a trivial kernel patch which exports the ACPI device _HID and _CID attributes in the corresponding sysfs node. I also wrote a script for /etc/hotplug which scans the /sys tree and loads modules according to HIDs exported.

Patch is an attachment because my mail client has a maximum line length limit. The patch is against 2.6.17.

Signature · 3 lines
|   ******* Maciek Grela RV409b *********
|        JID: [email protected]
| Linux user #319794 ++ There is no spoon ...
drivers/acpi/scan.c +40 -0
diff -rup --exclude-from=patch.exclude linux-2.6.17/drivers/acpi/scan.c linux-2.6.17-th/drivers/acpi/scan.c
@@ -351,11 +351,19 @@ static int acpi_bus_get_wakeup_device_fl
 static ssize_t acpi_eject_store(struct acpi_device *device,
 				const char *buf, size_t count);
 
+static ssize_t acpi_hid_show(struct acpi_device *device,
+				char *buf);
+
+static ssize_t acpi_cid_show(struct acpi_device *device,
+				char *buf);
+
 #define ACPI_DEVICE_ATTR(_name,_mode,_show,_store) \
 static struct acpi_device_attribute acpi_device_attr_##_name = \
 		__ATTR(_name, _mode, _show, _store)
 
 ACPI_DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store);
+ACPI_DEVICE_ATTR(hid, 0400, acpi_hid_show, NULL);
+ACPI_DEVICE_ATTR(cid, 0400, acpi_cid_show, NULL);
 
 /**
  * setup_sys_fs_device_files - sets up the device files under device namespace
@@ -376,6 +384,15 @@ setup_sys_fs_device_files(struct acpi_de
 	status = acpi_get_handle(dev->handle, "_EJ0", &temp);
 	if (ACPI_SUCCESS(status))
 		(*(func)) (&dev->kobj, &acpi_device_attr_eject.attr);
+
+	if ( dev->pnp.cid_list ) {
+		(*(func)) (&dev->kobj, &acpi_device_attr_cid.attr);
+	}
+
+	if ( dev->pnp.hardware_id[0] != 0 ) {
+		(*(func)) (&dev->kobj, &acpi_device_attr_hid.attr);
+	}
+
 }
 
 static int acpi_eject_operation(acpi_handle handle, int lockable)
@@ -456,6 +473,29 @@ acpi_eject_store(struct acpi_device *dev
 	return ret;
 }
 
+static ssize_t acpi_hid_show(struct acpi_device *device,
+				char *buf)
+{
+	return snprintf(buf, PAGE_SIZE, "%s\n",device->pnp.hardware_id);
+}
+
+static ssize_t acpi_cid_show(struct acpi_device *device,
+				char *buf)
+{
+	int ret = 0;
+	int buflen = PAGE_SIZE;
+	int i,w;
+	
+	for (i = 0; i < device->pnp.cid_list->count; i++, ret+=w, buflen-=w, buf+=w ) {
+		w = snprintf(buf, buflen, "%s ", device->pnp.cid_list->id[i].value);
+	}
+
+	ret += snprintf(buf, buflen, "\n");
+	
+	return ret;
+}
+
+
 /* --------------------------------------------------------------------------
                               Performance Management
    -------------------------------------------------------------------------- */
#1 lore raw
Quoted 6 lines · On Saturday 08 July 2006 16:24, [email protected] wrote:

I recently thought about using coldplug (/etc/hotplug/*.rc) scripts to automagically load modules responsible for ACPI-controlled devices such as button, battery, laptop extras. To accomplish this I wrote a trivial kernel patch which exports the ACPI device _HID and _CID attributes in the corresponding sysfs node. I also wrote a script for /etc/hotplug which scans the /sys tree and loads modules according to HIDs exported.

Funny, I was just thinking about this, too :-) I think it's a good idea to be able to automatically load the correct drivers for ACPI devices.

But I think it'd be better for the kernel to expose ACPI devices to user-space as PNP devices, and to use uevents for PNP devices just like we do for PCI devices. I don't like the idea of having separate user-space stuff for PNPBIOS vs PNPACPI devices.

I don't know very much about udev. Would the following patch, in combination with CONFIG_PNPACPI, be enough to do what you want?

drivers/pnp/driver.c +47 -0
Index: work-mm7/drivers/pnp/driver.c
===================================================================
@@ -191,9 +191,56 @@
 	return 0;
 }
 
+static int pnp_uevent(struct device *dev, char **envp, int num_envp,
+		      char *buffer, int buffer_size)
+{
+	struct pnp_dev *pdev;
+	struct pnp_id *pos;
+	int i = 0;
+	int length = 0;
+	int ids = 0;
+	char *id_buffer, *p;
+
+	if (!dev)
+		return -ENODEV;
+
+	pdev = to_pnp_dev(dev);
+	if (!pdev)
+		return -ENODEV;
+
+	for (pos = pdev->id; pos; pos = pos->next)
+		ids++;
+
+	id_buffer = kmalloc(ids * 9, GFP_KERNEL);
+	if (!id_buffer)
+		return -ENOMEM;
+
+	p = id_buffer;
+	for (pos = pdev->id; pos; pos = pos->next)
+		p += scnprintf(p, 9, "%s%c", pos->id, pos->next ? ':' : '\0');
+
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "PNP_ID=%s", id_buffer)) {
+		kfree(id_buffer);
+		return -ENOMEM;
+	}
+
+	kfree(id_buffer);
+
+	if (add_uevent_var(envp, num_envp, &i,
+			   buffer, buffer_size, &length,
+			   "PNP_DEVICE_NODE=%s", dev->bus_id))
+		return -ENOMEM;
+
+	envp[i] = NULL;
+	return 0;
+}
+
 struct bus_type pnp_bus_type = {
 	.name	= "pnp",
 	.match	= pnp_bus_match,
+	.uevent = pnp_uevent,
 	.probe	= pnp_device_probe,
 	.remove	= pnp_device_remove,
 	.suspend = pnp_bus_suspend,