diff --git a/configs/rpt/res_usbradio.conf b/configs/rpt/res_usbradio.conf new file mode 100644 index 0000000..d4d8030 --- /dev/null +++ b/configs/rpt/res_usbradio.conf @@ -0,0 +1,7 @@ +[general] +;usb_devices = 1209:7388 ;comma delimited list of usb + ;descriptors to allow. + ;format vvvv:pppp in hexadecimal + ;vvvv=vendor id, pppp=product id + ; + ;1209:7388 = AIOC (all in one cable) diff --git a/configs/samples/res_usbradio.conf.sample b/configs/samples/res_usbradio.conf.sample new file mode 100644 index 0000000..d4d8030 --- /dev/null +++ b/configs/samples/res_usbradio.conf.sample @@ -0,0 +1,7 @@ +[general] +;usb_devices = 1209:7388 ;comma delimited list of usb + ;descriptors to allow. + ;format vvvv:pppp in hexadecimal + ;vvvv=vendor id, pppp=product id + ; + ;1209:7388 = AIOC (all in one cable) diff --git a/res/res_usbradio.c b/res/res_usbradio.c index c7f9175..ea49fac 100644 --- a/res/res_usbradio.c +++ b/res/res_usbradio.c @@ -67,6 +67,9 @@ #include "asterisk/module.h" #include "asterisk/cli.h" #include "asterisk/poll-compat.h" /* Used for polling */ +#include "asterisk/config.h" + +#define CONFIG_FILE "res_usbradio.conf" AST_MUTEX_DEFINE_STATIC(usb_list_lock); @@ -78,6 +81,33 @@ AST_MUTEX_DEFINE_STATIC(usb_list_lock); static char *usb_device_list = NULL; static int usb_device_list_size = 0; +/*! + * \brief Structure for defined usb devices. + */ +struct usb_device_entry { + unsigned short idVendor; + unsigned short idProduct; + unsigned short idMask; + AST_LIST_ENTRY(usb_device_entry) entry; +}; + +/*! + * \brief Array of known compatible usb devices. + */ +const struct usb_device_entry known_devices[] = { + { C108_VENDOR_ID, C108_PRODUCT_ID, 0xfffc, {NULL} }, + { C108_VENDOR_ID, C108B_PRODUCT_ID, 0xffff, {NULL} }, + { C108_VENDOR_ID, C108AH_PRODUCT_ID, 0xffff, {NULL} }, + { C108_VENDOR_ID, C119A_PRODUCT_ID, 0xffff, {NULL} }, + { C108_VENDOR_ID, C119B_PRODUCT_ID, 0xffff, {NULL} }, + { C108_VENDOR_ID, N1KDO_PRODUCT_ID, 0xff00, {NULL} }, + { C108_VENDOR_ID, C119_PRODUCT_ID, 0xffff, {NULL} }, +}; + +/*! + * \brief Linked list of user defined usb devices. + */ +static AST_RWLIST_HEAD_STATIC(user_devices, usb_device_entry); long ast_radio_lround(double x) { @@ -295,6 +325,52 @@ void ast_radio_put_eeprom(struct usb_dev_handle *handle, unsigned short *buf) write_eeprom(handle, i, buf[EEPROM_USER_CS_ADDR]); } +/*! + * \brief See if the passed device matches one of our known devices. + * + * \param dev usb device + * \return 0 does not matches + * \return 1 matches + */ +static int is_known_device(struct usb_device *dev) +{ + int index; + int matched_entry = 0; + + for (index = 0; index < ARRAY_LEN(known_devices); index++) { + if (dev->descriptor.idVendor == known_devices[index].idVendor && + dev->descriptor.idProduct == (known_devices[index].idProduct & known_devices[index].idMask)) { + matched_entry = 1; + break; + }; + } + + return matched_entry; +} + +/*! + * \brief See if the passed device matches one of our user defined devices. + * + * \param dev usb device + * \return 0 does not matches + * \return 1 matches + */ +static int is_user_device(struct usb_device *dev) +{ + struct usb_device_entry *device; + + AST_RWLIST_RDLOCK(&user_devices); + AST_LIST_TRAVERSE(&user_devices, device, entry) { + if (dev->descriptor.idVendor == device->idVendor && + dev->descriptor.idProduct == device->idProduct) { + break; + }; + } + AST_RWLIST_UNLOCK(&user_devices); + + return device ? 1 : 0; +} + int ast_radio_hid_device_mklist(void) { struct usb_bus *usb_bus; @@ -319,15 +395,7 @@ int ast_radio_hid_device_mklist(void) usb_find_devices(); for (usb_bus = usb_busses; usb_bus; usb_bus = usb_bus->next) { for (dev = usb_bus->devices; dev; dev = dev->next) { - if ((dev->descriptor.idVendor - == C108_VENDOR_ID) && - (((dev->descriptor.idProduct & 0xfffc) == C108_PRODUCT_ID) || - (dev->descriptor.idProduct == C108B_PRODUCT_ID) || - (dev->descriptor.idProduct == C108AH_PRODUCT_ID) || - (dev->descriptor.idProduct == C119A_PRODUCT_ID) || - (dev->descriptor.idProduct == C119B_PRODUCT_ID) || - ((dev->descriptor.idProduct & 0xff00) == N1KDO_PRODUCT_ID) || - (dev->descriptor.idProduct == C119_PRODUCT_ID))) { + if (is_known_device(dev) || is_user_device(dev)) { sprintf(devstr, "%s/%s", usb_bus->dirname, dev->filename); for (i = 0; i < 32; i++) { sprintf(str, "/proc/asound/card%d/usbbus", i); @@ -421,15 +489,7 @@ struct usb_device *ast_radio_hid_device_init(const char *desired_device) usb_find_devices(); for (usb_bus = usb_busses; usb_bus; usb_bus = usb_bus->next) { for (dev = usb_bus->devices; dev; dev = dev->next) { - if ((dev->descriptor.idVendor - == C108_VENDOR_ID) && - (((dev->descriptor.idProduct & 0xfffc) == C108_PRODUCT_ID) || - (dev->descriptor.idProduct == C108B_PRODUCT_ID) || - (dev->descriptor.idProduct == C108AH_PRODUCT_ID) || - (dev->descriptor.idProduct == C119A_PRODUCT_ID) || - (dev->descriptor.idProduct == C119B_PRODUCT_ID) || - ((dev->descriptor.idProduct & 0xff00) == N1KDO_PRODUCT_ID) || - (dev->descriptor.idProduct == C119_PRODUCT_ID))) { + if (is_known_device(dev) || is_user_device(dev)) { sprintf(devstr, "%s/%s", usb_bus->dirname, dev->filename); for (i = 0; i < 32; i++) { sprintf(str, "/proc/asound/card%d/usbbus", i); @@ -820,13 +880,100 @@ void ast_radio_print_rx_audio_stats(int fd, struct rxaudiostatistics *o) } } +/*! + * \brief Remove and free up all user devices. + */ +static void cleanup_user_devices(void) +{ + struct usb_device_entry *device; + /* Remove all existing devices */ + AST_RWLIST_WRLOCK(&user_devices); + while ((device = AST_LIST_REMOVE_HEAD(&user_devices, entry))) { + ast_free(device); + } + AST_RWLIST_UNLOCK(&user_devices); +} + +/* Load our configuration */ +static int load_config(int reload) +{ + struct ast_config *cfg; + struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 }; + const char *varval; + + if (!(cfg = ast_config_load(CONFIG_FILE, config_flags))) { + ast_log(LOG_WARNING, "Config file %s not found\n", CONFIG_FILE); + return 0; + } else if (cfg == CONFIG_STATUS_FILEUNCHANGED) { + ast_debug(1, "Config file %s unchanged, skipping\n", CONFIG_FILE); + return 0; + } else if (cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Config file %s is in an invalid format. Aborting.\n", CONFIG_FILE); + return -1; + } + + if (reload) { + cleanup_user_devices(); + } + + /* general section + * usb_devices format vvvv:pppp,vvvv:pppp where vvvv is usb vendor id and pppp is usb product id + */ + if ((varval = ast_variable_retrieve(cfg, "general", "usb_devices")) && !ast_strlen_zero(varval)) { + struct usb_device_entry *device; + char *item; + char *value; + int idVendor; + int idProduct; + + value = ast_strdupa(varval); + + /* process the delimited list */ + while ((item = strsep(&value, ","))) { + + if (sscanf(item, "%04x:%04x", &idVendor, &idProduct) == 2) { + /* allocate space for our device */ + if(!(device = ast_calloc(1, sizeof(*device)))) { + break; + } + device->idVendor = idVendor; + device->idProduct = idProduct; + device->idMask = 0xffff; + /* Add it to our list */ + AST_RWLIST_WRLOCK(&user_devices); + AST_LIST_INSERT_HEAD(&user_devices, device, entry); + AST_RWLIST_UNLOCK(&user_devices); + + ast_debug(1, "Loaded user defined usb device %s", item); + } else { + ast_log(LOG_WARNING,"USB Device descriptor '%s' is in the wrong format", item); + } + } + } + + ast_config_destroy(cfg); + + return 0; +} + +static int reload_module(void) +{ + return load_config(1); +} + static int load_module(void) { + if (load_config(0)) { + return AST_MODULE_LOAD_DECLINE; + } + return 0; } static int unload_module(void) { + cleanup_user_devices(); + return 0; } @@ -834,5 +981,6 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, + .reload = reload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND - 5, );