Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0210b66d authored by Stefan Richter's avatar Stefan Richter
Browse files

firewire: core: add sysfs attribute for easier udev rules



This adds the attribute /sys/bus/firewire/devices/fw[0-9]+/units.  It
can be used in udev rules like the following ones:

# IIDC devices: industrial cameras and some webcams
SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x00010?*", GROUP="video"

# AV/C devices: camcorders, set-top boxes, TV sets, audio devices, ...
SUBSYSTEM=="firewire", ATTR{units}=="*0x00a02d:0x010001*", GROUP="video"

Background:

firewire-core manages two device types:
  - fw_device is a FireWire node.  A character device file is associated
    with it.
  - fw_unit is a unit directory on a node.  Each fw_device may have 0..n
    children of type fw_unit.  The units tell us what kinds of protocols
    a node implements.

We want to set ownership or ACLs or permissions of the character device
file of an fw_device, or/and create symlinks to it, based on available
protocols.  Until now udev rules had to look at the fw_unit devices and
then modify their parent's character device file accordingly.  This is
problematic for two reasons:  1) It happens sometime after the creation
of the fw_device, 2) an access policy may require that information from
all children is evaluated before a decision about the parent is made.

Problem 1) can ultimately not be avoided since this is the nature of
FireWire nodes:  They may add or remove unit directories at any point in
time.

However, we can still help userland a lot by providing the protocol type
information of all units in a summary sysfs attribute directly at the
fw_device.  This way,
   - the information is immediately available at the affected device
     when userspace goes about to handle an ADD or CHANGE event of the
     fw_device,
   - with most policies, it won't be necessary anymore to dig through
     child attributes.

The new attribute is called "units".  It contains space-separated tuples
of specifier_id and version of each present unit.  The delimiter within
tuples is a colon.  Specifier_id and version are printed as 0x%06x.

Here is an example of a node which implements an IPv4 unit and an IPv6
unit:  $ cat /sys/bus/firewire/devices/fw2/units
0x00005e:0x000001 0x00005e:0x000002

Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent e5333db9
Loading
Loading
Loading
Loading
+50 −0
Original line number Original line Diff line number Diff line
@@ -355,9 +355,56 @@ static ssize_t guid_show(struct device *dev,
	return ret;
	return ret;
}
}


static int units_sprintf(char *buf, u32 *directory)
{
	struct fw_csr_iterator ci;
	int key, value;
	int specifier_id = 0;
	int version = 0;

	fw_csr_iterator_init(&ci, directory);
	while (fw_csr_iterator_next(&ci, &key, &value)) {
		switch (key) {
		case CSR_SPECIFIER_ID:
			specifier_id = value;
			break;
		case CSR_VERSION:
			version = value;
			break;
		}
	}

	return sprintf(buf, "0x%06x:0x%06x ", specifier_id, version);
}

static ssize_t units_show(struct device *dev,
			  struct device_attribute *attr, char *buf)
{
	struct fw_device *device = fw_device(dev);
	struct fw_csr_iterator ci;
	int key, value, i = 0;

	down_read(&fw_device_rwsem);
	fw_csr_iterator_init(&ci, &device->config_rom[5]);
	while (fw_csr_iterator_next(&ci, &key, &value)) {
		if (key != (CSR_UNIT | CSR_DIRECTORY))
			continue;
		i += units_sprintf(&buf[i], ci.p + value - 1);
		if (i >= PAGE_SIZE - (8 + 1 + 8 + 1))
			break;
	}
	up_read(&fw_device_rwsem);

	if (i)
		buf[i - 1] = '\n';

	return i;
}

static struct device_attribute fw_device_attributes[] = {
static struct device_attribute fw_device_attributes[] = {
	__ATTR_RO(config_rom),
	__ATTR_RO(config_rom),
	__ATTR_RO(guid),
	__ATTR_RO(guid),
	__ATTR_RO(units),
	__ATTR_NULL,
	__ATTR_NULL,
};
};


@@ -1000,6 +1047,9 @@ static void fw_device_refresh(struct work_struct *work)


	create_units(device);
	create_units(device);


	/* Userspace may want to re-read attributes. */
	kobject_uevent(&device->device.kobj, KOBJ_CHANGE);

	if (atomic_cmpxchg(&device->state,
	if (atomic_cmpxchg(&device->state,
			   FW_DEVICE_INITIALIZING,
			   FW_DEVICE_INITIALIZING,
			   FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
			   FW_DEVICE_RUNNING) == FW_DEVICE_GONE)
+1 −1
Original line number Original line Diff line number Diff line
@@ -42,7 +42,7 @@ enum fw_device_state {
struct fw_attribute_group {
struct fw_attribute_group {
	struct attribute_group *groups[2];
	struct attribute_group *groups[2];
	struct attribute_group group;
	struct attribute_group group;
	struct attribute *attrs[11];
	struct attribute *attrs[12];
};
};


struct fw_node;
struct fw_node;