summaryrefslogtreecommitdiff
path: root/cras/src/server/cras_udev.c
diff options
context:
space:
mode:
Diffstat (limited to 'cras/src/server/cras_udev.c')
-rw-r--r--cras/src/server/cras_udev.c426
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);
-}