user space configuration tool

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

up to now we have pushed the issue of activating autosuspend to user space.
Presumably to be solved with udev and large white and black lists. Doing this
in practice has turned out very tedious. Doing it for device classes with
exceptions in the form of black lists has turned out outright difficult because
a device may have interfaces belonging to several classes, which may be treated
differently.
So in my opinion this needs a solution in user space with a little more sophistication.
I therefore have written this tool which includes class and id based solutions.

It defines four categories with respect to autosuspend into which classes
can be divided.

a - always autosuspend
b - autosuspend preferred
c - neutral
d - never autosuspend

For a device to be autosuspended it must have at least one category a
or category b interface and must not have a category d interface. What happens
if a meets d I haven't yet decided.

Solutions based on IDs always override the decision based on classes.

And because we've never solved the configuration issue, this tools also allows
setting configurations based on IDs.
The configuration files are based on glib, so they are relatively easy to manipulate
with tools and are nicely readable to humans.

It is currently useful to me but I'd still like to add some features.

- symbolic class names
- decisions on autosuspend based on subclasses and maybe protocols
Do you consider it useful?

	Regards
		Oliver
-- 
- - - 
SUSE LINUX Products GmbH, GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer, HRB 16746 (AG Nürnberg) 
Maxfeldstraße 5                         
90409 Nürnberg 
Germany 
- - - 
#define CONF		"/etc/usbconfigurator/configurations"
#define CLASSES		"/etc/usbconfigurator/classes"

#define POWER	"/power/control"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <libusb-1.0/libusb.h>
#include <libudev.h>
#include <glib.h>

#include "configurator.h"

libusb_device *device;
libusb_device_handle *handle;
struct udev *magic;
struct udev_device *ud;
struct libusb_device_descriptor dd;

char *always;
char *never;
char *good;

char always_classes[256];
char never_classes[256];
char good_classes[256];

GKeyFile *configurations;
GKeyFile *classes;

static void init_libs()
{
	int err;

	err = libusb_init(NULL);
	if (err < 0)
		exit(1);

	magic = udev_new();
	ud = udev_device_new_from_environment(magic);
}

static int simple_parse(gchar **field, const char *section, int length)
{
	int i, r;
	gchar *value;
	GError *err;

	for (i = 0; i < length; i++) {
		gchar *current = field[i];
		value = g_key_file_get_value(classes, section, current, &err);
		if (err) {
			continue;
		}
		r = atoi(current);
		if (r < 0 || r > 255) {
			continue;
		}

		if (!strcmp(value, "always")) {
			always_classes[r] = 1;
		} else if (!strcmp(value, "never")) {
			never_classes[r] = 1;
		} else if (!strcmp(value, "optional")) {
			good_classes[r] = 1;
		}
	}
	return 0;
}

static int parse_files()
{
	gchar **entries;

	gchar *value;
	GError *err;
	gsize length;

	entries = g_key_file_get_keys(classes, "class", &length, &err);
	simple_parse(entries, "class", length);
	return 0;
}

static int open_files()
{
	int fdconf, fdalways, fdnegative, fdgood;
	int err;
	GKeyFileFlags flags;
	GError *error = NULL;

	configurations = g_key_file_new();
	classes = g_key_file_new();
	flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;

	if (!g_key_file_load_from_file(configurations, CONF, flags, &error))
		return -1;
	if (!g_key_file_load_from_file(classes, CLASSES, flags, &error))
		return -1;

	err = parse_files();
	return err;
}

static int open_device()
{
	char *_busnum, *_devaddr;
	uint8_t busnum, devaddr;
	int cnt, err;
	ssize_t i;
	libusb_device **devs;
	libusb_device *dev;

	_busnum = udev_device_get_sysattr_value(ud, "busnum");
	_devaddr = udev_device_get_sysattr_value(ud, "devnum");

	/* if this fails we are called for an interface */
	if (! _busnum || ! _devaddr)
		return -1;

	busnum = atoi(_busnum);
	devaddr = atoi(_devaddr);

	cnt = libusb_get_device_list(NULL, &devs);
	for (i = 0; i < cnt; i++) {
		dev = devs[i];
		if (libusb_get_bus_number(dev) != busnum)
			continue;
		if (libusb_get_device_address(dev) != devaddr)
			continue;
		device = libusb_ref_device(dev);

		err = libusb_get_device_descriptor(device, &dd);
		if (err < 0) {
			return -1;
		}
break;
	}

	libusb_free_device_list(devs, 1);
	cnt = libusb_open(device, &handle);

	return cnt;
}

static void close_libs()
{
	libusb_unref_device(device);
	libusb_close(handle);
	libusb_exit(NULL);
}

static void close_files()
{
}

static int find_configuration()
{
	char numeric_name[10];
	int err, rv;
	int *provided_conf;
	gsize length;

	sprintf(numeric_name, "%04x:%04x", (unsigned int)dd.idVendor, (unsigned int)dd.idProduct);

	provided_conf = g_key_file_get_integer_list(configurations, "configuration", numeric_name, &length, NULL);
	rv = provided_conf ? *provided_conf : -1;
	if (rv < -1 || rv > 255)
		rv = -1;
	return rv;
}

static int set_configuration(int conf)
{
	return libusb_set_configuration(handle, conf);
}

static char always_pm(int class)
{
	return always_classes[class];
}

static char never_pm(int class)
{
	return never_classes[class];
}

static char good_pm(int class)
{
	return good_classes[class];
}

static void activate_runtime_pm()
{
	char powerpath[256];
	char *syspath;
	char *p;
	int fd;

	syspath = udev_device_get_syspath(ud);
	if (!syspath)
		return;

	strcpy(powerpath, syspath);
	for (p = powerpath; *p; p++);
	strcpy(p, POWER);

	fd = open(powerpath, O_WRONLY);
	if (fd < 0)
		return;

	write(fd, "auto\n", 5);
	close(fd);
}

static int pm_by_id()
{
	gboolean provided_pm;
	GError *err = NULL;
	char numeric_name[10];

	sprintf(numeric_name, "%04x:%04x", (unsigned int)dd.idVendor, (unsigned int)dd.idProduct);

	provided_pm = g_key_file_get_boolean(configurations, "autosuspend", numeric_name, &err);
	if (provided_pm == FALSE && err)
		return 0;
	return provided_pm == TRUE ? 1 : 2;
}

main(int argc, char **argv)
{
	int i, j, err, class;
	int intf, maxintf;
	struct libusb_config_descriptor *current_conf;
	const struct libusb_interface *interfaces;
	const struct libusb_interface_descriptor *setting;
	struct libusb_interface *iface;
	unsigned char pm = 0, by_id;

	init_libs();
	err = open_files();
	if (err < 0)
		goto out;
	err = open_device();
	if (err < 0)
		goto skip_err;

	i = find_configuration();
	if (i == -2)
		goto skip;
	if (i >= 0) {
		err = set_configuration(i);
		if (err < 0)
			goto skip_err;
	}

	err = libusb_get_active_config_descriptor(device, &current_conf); 
	if (err < 0)
		goto skip_err;
	interfaces = current_conf->interface;
	maxintf = current_conf->bNumInterfaces;

	if ((by_id = pm_by_id()) > 0) {
		if (by_id == 1)
			pm = 1;
		else
			pm = 0;
		goto finish;
	}

	if (dd.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) {
		if (always_pm(dd.bDeviceClass) || good_pm(dd.bDeviceClass))
			pm = 1;
		else
			pm = 0;
		goto finish;
	}
	for (intf = 0; intf < maxintf; intf++) {
		iface = interfaces + intf;
		
		for (j = 0; j < iface->num_altsetting; j++) {
			setting = iface->altsetting + j;
			class = setting->bInterfaceClass;

			if (always_pm(class)) {
				pm = 1;
				goto finish;
			}

			if (never_pm(class)) {
				pm = 0;
				goto finish;
			}

			if (good_pm(class)) {
				pm = 1;
			}
		}
	}
finish:
	libusb_free_config_descriptor(current_conf);
	if (pm)
		activate_runtime_pm();

skip_err:
skip:
	close_files();
out:
	close_libs();
}
PROGRAM = usbconfigurator
PROGRAM_FILES = configurator.c

cc = gcc
CFLAGS	+= -g $(shell pkg-config --cflags glib-2.0 libusb-1.0)
LDFLAGS	+= -g
LIBS 	+= $(shell pkg-config --libs glib-2.0 libusb-1.0)

all: $(PROGRAM)

$(PROGRAM): configurator.c configurator.h
	$(cc) $(PROGRAM_FILES) $(CFLAGS) $(LDFLAGS) -o $(PROGRAM) $(LIBS) -ludev 

clean:
	@rm -rf $(PROGRAM)

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux