diff options
Diffstat (limited to 'cras/src/server/cras_udev.c')
-rw-r--r-- | cras/src/server/cras_udev.c | 426 |
1 files changed, 0 insertions, 426 deletions
diff --git a/cras/src/server/cras_udev.c b/cras/src/server/cras_udev.c deleted file mode 100644 index 16f0e3b2..00000000 --- a/cras/src/server/cras_udev.c +++ /dev/null @@ -1,426 +0,0 @@ -/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -#include <assert.h> -#include <libudev.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <regex.h> -#include <syslog.h> - -#include "cras_system_state.h" -#include "cras_types.h" -#include "cras_util.h" -#include "cras_checksum.h" - -struct udev_callback_data { - struct udev_monitor *mon; - struct udev *udev; - int fd; -}; - -static unsigned is_action(const char *desired, const char *actual) - __attribute__((nonnull(1))); - -/* Matches Alsa sound device entries generated by udev. For - * example: - * - * /devices/pci0000:00/0000:00:1b.0/sound/card1/pcmC1D0p - * - * We want to be able to extract: - * - * o The card number - * o The device number - * o If it's 'playback' (p) or 'capture' (c). (It may not be both.) - * - * Given the example above, the following matches should occur: - * - * - * | A | - * BBCCCD - * /devices/pci0000:00/0000:00:1b.0/sound/card1/pcmC1D10p - * - * A: The whole regex will be matched. - * B: The card. - * C: The device. - * D: 'p' (playback) or 'c' (capture) - * - * The order of the offsets in the 'pmatch' buffer does not appear - * to match with the documentation: - * - * Each rm_so element that is not -1 indicates the start - * offset of the next largest substring match within the - * string. - * - * But are, instead, filled in the same order presented in the - * string. To alleviate possible issudes, the 'C' (card) and 'D' - * (device) identifying characters are included in the result. - */ -static const char pcm_regex_string[] = "^.*pcm(C[0-9]+)(D[0-9]+)([pc])"; -static regex_t pcm_regex; - -/* Card regex is similar to above, but only has one field -- the card. The - * format is the same with the exception of the leaf node being of the form: - * - * /devices/...../card0 - * - * Where 0 is the card number and the only thing we care about in - * this case. - */ - -static const char card_regex_string[] = "^.*/card([0-9]+)"; -static regex_t card_regex; - -static char const *const subsystem = "sound"; -static const unsigned int MAX_DESC_NAME_LEN = 256; - -static unsigned is_action(const char *desired, const char *actual) -{ - return actual != NULL && strcmp(desired, actual) == 0; -} - -static unsigned is_action_change(const char *action) -{ - return is_action("change", action); -} - -static unsigned is_action_remove(const char *action) -{ - return is_action("remove", action); -} - -static unsigned is_internal_bus(const char *bus) -{ - return (bus != NULL && - (strcmp(bus, "pci") == 0 || strcmp(bus, "platform") == 0)); -} - -static unsigned is_external_bus(const char *bus) -{ - return (bus != NULL && (strcmp(bus, "usb") == 0)); -} - -static unsigned is_internal_device(struct udev_device *dev) -{ - struct udev_device *parent = udev_device_get_parent(dev); - while (parent != NULL) { - const char *name = udev_device_get_subsystem(parent); - - if (name != NULL) { - if (is_external_bus(name)) - return 0; - else if (is_internal_bus(name)) - return 1; - } - parent = udev_device_get_parent(parent); - } - return 0; -} - -static unsigned is_card_device(struct udev_device *dev, unsigned *internal, - unsigned *card_number, const char **sysname) -{ - regmatch_t m[2]; - const char *devpath = udev_device_get_devpath(dev); - - if (devpath != NULL && - regexec(&card_regex, devpath, ARRAY_SIZE(m), m, 0) == 0) { - *sysname = udev_device_get_sysname(dev); - *internal = is_internal_device(dev); - *card_number = (unsigned)atoi(&devpath[m[1].rm_so]); - return 1; - } - - return 0; -} - -static void set_factory_default(unsigned card_number) -{ - static const char alsactl[] = "/usr/sbin/alsactl"; - static const char asound_state[] = "/etc/asound.state"; - char cmd_buf[128]; - struct stat stat_buf; - int r; - - if (stat(asound_state, &stat_buf) == 0) { - syslog(LOG_INFO, "%s: init card '%u' to factory default", - __FUNCTION__, card_number); - r = snprintf(cmd_buf, ARRAY_SIZE(cmd_buf), - "%s --file %s restore %u", alsactl, asound_state, - card_number); - cmd_buf[ARRAY_SIZE(cmd_buf) - 1] = '\0'; - r = system(cmd_buf); - if (r != 0) - syslog(LOG_ERR, - "%s: failed to init card '%d' " - "to factory default. Failure: %d. Command: %s", - __FUNCTION__, card_number, r, cmd_buf); - } -} - -static inline void udev_delay_for_alsa() -{ - /* Provide a small delay so that the udev message can - * propogate throughout the whole system, and Alsa can set up - * the new device. Without a small delay, an error of the - * form: - * - * Fail opening control hw:? - * - * will be produced by cras_alsa_card_create(). - */ - usleep(125000); /* 0.125 second */ -} - -/* Reads the "descriptors" file of the usb device and returns the - * checksum of the contents. Returns 0 if the file can not be read */ -static uint32_t calculate_desc_checksum(struct udev_device *dev) -{ - char path[MAX_DESC_NAME_LEN]; - struct stat stat_buf; - int fd; - unsigned char *buf = NULL; - int buf_size = 0; - int read_size; - ssize_t n; - uint32_t result; - - if (snprintf(path, sizeof(path), "%s/descriptors", - udev_device_get_syspath(dev)) >= sizeof(path)) { - syslog(LOG_ERR, "failed to build path"); - return 0; - } - - if (stat(path, &stat_buf) < 0) { - syslog(LOG_ERR, "failed to stat file %s: %s", path, - strerror(errno)); - return 0; - } - - fd = open(path, O_RDONLY); - if (fd < 0) { - syslog(LOG_ERR, "failed to open file %s: %s", path, - strerror(errno)); - return 0; - } - - read_size = 0; - while (read_size < stat_buf.st_size) { - if (read_size == buf_size) { - if (buf_size == 0) - buf_size = 256; - else - buf_size *= 2; - uint8_t *new_buf = realloc(buf, buf_size); - if (new_buf == NULL) { - syslog(LOG_ERR, "no memory to read file %s", - path); - goto bail; - } - buf = new_buf; - } - n = read(fd, buf + read_size, buf_size - read_size); - if (n == 0) - break; - if (n < 0) { - syslog(LOG_ERR, "failed to read file %s", path); - goto bail; - } - read_size += n; - } - - close(fd); - result = crc32_checksum(buf, read_size); - free(buf); - return result; -bail: - close(fd); - free(buf); - return 0; -} - -static void fill_usb_card_info(struct cras_alsa_card_info *card_info, - struct udev_device *dev) -{ - const char *sysattr; - struct udev_device *parent_dev = - udev_device_get_parent_with_subsystem_devtype(dev, "usb", - "usb_device"); - if (!parent_dev) - return; - - sysattr = udev_device_get_sysattr_value(parent_dev, "idVendor"); - if (sysattr) - card_info->usb_vendor_id = strtol(sysattr, NULL, 16); - sysattr = udev_device_get_sysattr_value(parent_dev, "idProduct"); - if (sysattr) - card_info->usb_product_id = strtol(sysattr, NULL, 16); - sysattr = udev_device_get_sysattr_value(parent_dev, "serial"); - if (sysattr) { - strncpy(card_info->usb_serial_number, sysattr, - USB_SERIAL_NUMBER_BUFFER_SIZE - 1); - card_info->usb_serial_number[USB_SERIAL_NUMBER_BUFFER_SIZE - 1] = - '\0'; - } - - card_info->usb_desc_checksum = calculate_desc_checksum(parent_dev); - - syslog(LOG_INFO, - "USB card: vendor:%04x, product:%04x, serial num:%s, " - "checksum:%08x", - card_info->usb_vendor_id, card_info->usb_product_id, - card_info->usb_serial_number, card_info->usb_desc_checksum); -} - -static void device_add_alsa(struct udev_device *dev, const char *sysname, - unsigned card, unsigned internal) -{ - struct cras_alsa_card_info card_info; - memset(&card_info, 0, sizeof(card_info)); - - udev_delay_for_alsa(); - card_info.card_index = card; - if (internal) { - card_info.card_type = ALSA_CARD_TYPE_INTERNAL; - } else { - card_info.card_type = ALSA_CARD_TYPE_USB; - fill_usb_card_info(&card_info, dev); - } - - cras_system_add_alsa_card(&card_info); -} - -void device_remove_alsa(const char *sysname, unsigned card) -{ - udev_delay_for_alsa(); - cras_system_remove_alsa_card(card); -} - -static int udev_sound_initialized(struct udev_device *dev) -{ - /* udev will set SOUND_INITALIZED=1 for the main card node when the - * system has already been initialized, i.e. when cras is restarted - * on an already running system. - */ - const char *s; - - s = udev_device_get_property_value(dev, "SOUND_INITIALIZED"); - if (s) - return 1; - - return 0; -} - -static void change_udev_device_if_alsa_device(struct udev_device *dev) -{ - /* If the device, 'dev' is an alsa device, add it to the set of - * devices available for I/O. Mark it as the active device. - */ - unsigned internal; - unsigned card_number; - const char *sysname; - - if (is_card_device(dev, &internal, &card_number, &sysname) && - udev_sound_initialized(dev) && - !cras_system_alsa_card_exists(card_number)) { - if (internal) - set_factory_default(card_number); - device_add_alsa(dev, sysname, card_number, internal); - } -} - -static void remove_device_if_card(struct udev_device *dev) -{ - unsigned internal; - unsigned card_number; - const char *sysname; - - if (is_card_device(dev, &internal, &card_number, &sysname)) - device_remove_alsa(sysname, card_number); -} - -static void enumerate_devices(struct udev_callback_data *data) -{ - struct udev_enumerate *enumerate = udev_enumerate_new(data->udev); - struct udev_list_entry *dl; - struct udev_list_entry *dev_list_entry; - - udev_enumerate_add_match_subsystem(enumerate, subsystem); - udev_enumerate_scan_devices(enumerate); - dl = udev_enumerate_get_list_entry(enumerate); - - udev_list_entry_foreach(dev_list_entry, dl) - { - const char *path = udev_list_entry_get_name(dev_list_entry); - struct udev_device *dev = - udev_device_new_from_syspath(data->udev, path); - - change_udev_device_if_alsa_device(dev); - udev_device_unref(dev); - } - udev_enumerate_unref(enumerate); -} - -static void udev_sound_subsystem_callback(void *arg, int revents) -{ - struct udev_callback_data *data = (struct udev_callback_data *)arg; - struct udev_device *dev; - - dev = udev_monitor_receive_device(data->mon); - if (dev) { - const char *action = udev_device_get_action(dev); - - if (is_action_change(action)) - change_udev_device_if_alsa_device(dev); - else if (is_action_remove(action)) - remove_device_if_card(dev); - udev_device_unref(dev); - } else - syslog(LOG_WARNING, - "%s (internal error): " - "No device obtained", - __FUNCTION__); -} - -static void compile_regex(regex_t *regex, const char *str) -{ - int r = regcomp(regex, str, REG_EXTENDED); - assert(r == 0); -} - -static struct udev_callback_data udev_data; -void cras_udev_start_sound_subsystem_monitor() -{ - int r; - - udev_data.udev = udev_new(); - assert(udev_data.udev != NULL); - udev_data.mon = udev_monitor_new_from_netlink(udev_data.udev, "udev"); - - udev_monitor_filter_add_match_subsystem_devtype(udev_data.mon, - subsystem, NULL); - udev_monitor_enable_receiving(udev_data.mon); - udev_data.fd = udev_monitor_get_fd(udev_data.mon); - - r = cras_system_add_select_fd(udev_data.fd, - udev_sound_subsystem_callback, &udev_data, - POLLIN); - assert(r == 0); - compile_regex(&pcm_regex, pcm_regex_string); - compile_regex(&card_regex, card_regex_string); - - enumerate_devices(&udev_data); -} - -void cras_udev_stop_sound_subsystem_monitor() -{ - udev_unref(udev_data.udev); - regfree(&pcm_regex); - regfree(&card_regex); -} |