Open Sound System |
Do you have problems with sound/audio application development? Don't panic! Click here for help! |
This file is part of Open Sound System.
Copyright (C) 4Front Technologies 1996-2008.
This this source file is released under GPL v2 license (no other versions). See the COPYING file included in the main directory of this source distribution for the license terms and conditions.
#define KBUILD_MODNAME oss_usb #include "oss_config.h" #include "ossusb.h" static const udi_usb_devinfo known_devices[] = { {0x763, 0x2002, "M Audio USB AudioSport Duo", { {"OFF 24bit_hispeed 16bit_hispeed 24bit_lowspeed 16bit_lowspeed", 2, 0x0014}, {"OFF 24bit_hispeed 16bit_hispeed 24bit_lowspeed 16bit_lowspeed", 2, 0x0014} } }, {0x763, 0x2001, "M Audio USB AudioSport Quatro"}, {0x763, 0x2805, "M Audio Sonica"}, {0x763, 0x2007, "M Audio Sonica Theatre", { {"OFF 24bit_hispeed 16bit_hispeed 16bit_hispeed 24bit_lowspeed 24bit24bit 16bit 16bit OFF OFF", 3}, {"OFF 24bit_hispeed 16bit_hispeed 24bit 16bit_lowspeed", 2} } }, {0x763, 0x200d, "M Audio OmniStudio USB"}, {0x41e, 0x3000, "Creative Sound Blaster Extigy"}, {0x41e, 0x3010, "Creative Sound Blaster MP3+"}, {0x41e, 0x3020, "Creative Audigy2 NX USB", { {"OFF 16bit22kHz 16bit44kHz " /* 0-2 */ "24bit44kHz 16bit48kHz 24bit48kHz " /* 3-5 */ "16bit96kHz 24bit96kHz 16bit22kHz " /* 6-8 */ "16bit48kHz 24bit48kHz 16bit5.1ch22kHz " /* 9-11 */ "16bit5.1ch48kHz 24bit5.1ch48kHz 16bit7.1ch22kHz " /* 12-14 */ "16bit7.1ch48kHz" /* 15 */ , 4}, {"OFF 16bit32kHz 24bit32kHz " /* 0-2 */ "16bit44kHz 24bit44kHz 16bit48kHz " /* 3-5 */ "24bit48kHzin 24bit96kHz 24bit96kHz ", /* 6-8 */ 5} } }, {0x46d, 0x8b2, "Logitec Quickcam Pro 4000 (mic)"}, {0x46d, 0xa01, "Logitec USB Headset"}, {0x0471, 0x0311, "Philips ToUcam Pro (mic)"}, {0x672, 0x1041, "Labtec LCS1040 Speaker System"}, {0x6f8, 0xc000, "Hercules Gamesurround MUSE Pocket"}, {0x0d8c, 0x000c, "C-Media USB audio"}, {0x0d8c, 0x0102, "C-Media 2/4/6/8ch USB audio", { {"OFF 7.1 2.0 3.1 5.1 digital", 2}, {"OFF stereo"} } }, {0x0d8c, 0x0103, "C-Media USB audio"}, {-1, -1, "USB sound device"} }; typedef struct { unsigned int devid; special_driver_t driver; } special_table_t; extern int usb_mixerstyle; #define MAX_DEVC 10 static ossusb_devc *devc_list[MAX_DEVC]; static int ndevs = 0; int usb_quiet = 0; void ossusb_dump_desc (unsigned char *desc, int cfg_len) { int i; for (i = 0; i < cfg_len; i++) { if (!(i % 16)) cmn_err (CE_CONT, "\n%04x: ", i); cmn_err (CE_CONT, "%02x ", desc[i]); } cmn_err (CE_CONT, "\n"); } unsigned int ossusb_get_int (unsigned char *p, int nbytes) { unsigned int v = 0; int i; for (i = 0; i < nbytes; i++) { v |= (*p++) << (i * 8); } return v; }
Mixer stuff
static int decode_vol (usb_control_t * c, unsigned char *buf, int l) { int value, range; if (l == 1) value = (signed char) buf[0]; else value = (signed short) (buf[0] | (buf[1] << 8)); range = c->max - c->min; value -= c->min; value = (value * c->scale + range / 2) / range; if (value < 0) value = 0; if (value > 255) value = 255; return value; } static void encode_vol (usb_control_t * c, unsigned char *buf, int l, int value) { int range; range = c->max - c->min; value = (value * range + c->scale / 2) / c->scale; value += c->min; if (l == 1) buf[0] = value; else { buf[0] = value & 0xff; buf[1] = (value >> 8) & 0xff; } } static int read_feature_value (ossusb_devc * devc, usb_control_t * c, int rq, int *v) { int len, value = 0, l; int nch, ch; unsigned char buf[2];
Volume controls use two message bytes while the others use just one.
l = (c->index == 1) ? 2 : 1; ch = c->min_ch + 1; if (c->global) ch = 0; if (rq != GET_CUR) { buf[0] = buf[1] = 0; len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint rq, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((c->index + 1) << 8) | (ch), // value (c->unit->num << 8), // index buf, // buffer l, // buflen OSS_HZ * 2); if (len < 0) return 0; value = (len == 1) ? (signed char) buf[0] : (signed short) (buf[0] | (buf[1] << 8)); *v = value; return 1; } nch = c->max_ch - c->min_ch + 1; buf[0] = buf[1] = 0; len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((c->index + 1) << 8) | (ch), // value (c->unit->num << 8), // index buf, // buffer l, // buflen OSS_HZ * 2); if (len < 0) { // cmn_err(CE_CONT, "feature (%s/%d) read error %d\n", c->unit->name, c->index, len); *v = 0; return 0; } value = decode_vol (c, buf, len); if (!c->global) if (nch == 2) /* Read the right channel */ { len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((c->index + 1) << 8) | (ch + 1), // value (c->unit->num << 8), // index buf, // buffer l, // buflen OSS_HZ * 2); if (len < 0) value |= (value & 0xff) << 8; else value |= (decode_vol (c, buf, len)) << 8; } *v = value; return 1; } static int write_feature_value (ossusb_devc * devc, usb_control_t * c, int value) { int len, l; int left, right; int ch, nch; unsigned char buf[2]; ch = c->min_ch + 1; if (c->global) ch = 0; left = value & 0xff; right = (value >> 8) & 0xff; if (left > (c->max - c->min)) left = c->max - c->min; if (right > (c->max - c->min)) right = c->max - c->min; l = (c->index == 1) ? 2 : 1; nch = c->max_ch - c->min_ch + 1; buf[0] = buf[1] = 0; encode_vol (c, buf, l, left); len = udi_usb_snd_control_msg (devc->mixer_usbdev, 0, // endpoint SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((c->index + 1) << 8) | ch, // value (c->unit->num << 8), // index buf, // buffer l, // buflen OSS_HZ * 2); if (len < 0) { cmn_err (CE_CONT, "feature write error %d\n", len); return len; } if (nch == 2) /* Write the right channel */ { buf[0] = buf[1] = 0; encode_vol (c, buf, l, right); len = udi_usb_snd_control_msg (devc->mixer_usbdev, 0, // endpoint SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((c->index + 1) << 8) | (ch + 1), // value (c->unit->num << 8), // index buf, // buffer l, // buflen OSS_HZ * 2); } else right = left; value = left | (right << 8); return value; } static int read_mixer_value (ossusb_devc * devc, usb_control_t * c, int rq, int *v) { int len, value = 0; unsigned char buf[2]; if (rq != GET_CUR) { len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint rq, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype (c->index << 8) | c->min_ch, // value (c->unit->num << 8) | 1, // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len < 0) return 0; *v = (signed char) buf[1]; return 1; } len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint rq, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype (c->index << 8) | c->min_ch, // value (c->unit->num << 8) | 1, // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len < 0) { cmn_err (CE_CONT, "mixer read error %d\n", len); return 0; } value = buf[1] - c->min; *v = value; return 1; } static int write_mixer_value (ossusb_devc * devc, usb_control_t * c, int value) { int len; unsigned char buf[2]; value &= 0xff; buf[0] = 0; buf[1] = value + c->min; len = udi_usb_snd_control_msg (devc->mixer_usbdev, 0, // endpoint SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype (c->index << 8) | c->min_ch, // value (c->unit->num << 8) | 1, // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len < 0) { cmn_err (CE_CONT, "mixer write error %d\n", len); return 0; } return value; } static int read_unit (ossusb_devc * devc, usb_control_t * c, int rq, int *v) { int err, value; unsigned char buf[2]; *v = value = 0; switch (c->unit->typ) { case TY_FEAT: return read_feature_value (devc, c, rq, v); break; case TY_MIXER: return read_mixer_value (devc, c, rq, v); break; case TY_SELECT: err = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint rq, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype 0, // value (c->unit->num << 8), // index buf, // buffer 1, // buflen OSS_HZ * 2); if (err < 0) { cmn_err (CE_CONT, "USB mixer unit read error %d\n", err); return 0; } value = buf[0] - 1; break; } /* switch */ *v = value; return 1; } static int read_current_value (ossusb_devc * devc, usb_control_t * c) { int v; if (!read_unit (devc, c, GET_CUR, &v)) return 0; c->value = v; return 1; } static int write_current_value (ossusb_devc * devc, usb_control_t * c, int value) { unsigned char buf[2]; int err; switch (c->unit->typ) { case TY_SELECT: if (value > c->max) value = c->max; buf[0] = value + 1; err = udi_usb_snd_control_msg (devc->mixer_usbdev, 0, // endpoint SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype 0, // value (c->unit->num << 8), // index buf, // buffer 1, // buflen OSS_HZ * 2); if (err < 0) { cmn_err (CE_CONT, "Selector write error %d\n", err); return 0; } break; case TY_FEAT: value = write_feature_value (devc, c, value); break; case TY_MIXER: value = write_mixer_value (devc, c, value); break; default: return OSS_EIO; } return value; } static int new_ctl (ossusb_devc * devc, usb_audio_unit_t * un, int index, int exttype, int chmask) { int n, min, max; usb_control_t *c; int i, min_ch = 0, max_ch = 0; if (devc->ncontrols >= MAX_CONTROLS) { cmn_err (CE_CONT, "Too many mixer features.\n"); return OSS_EIO; } n = devc->ncontrols++; c = &devc->controls[n]; if (chmask) { min_ch = -1; for (i = 0; i < 32; i++) if (chmask & (1 << i)) { if (min_ch == -1) min_ch = i; max_ch = i; } } c->unit = un; c->index = index; c->exttype = exttype; c->min_ch = min_ch; c->global = (chmask == 0) ? 1 : 0; c->max_ch = max_ch; c->min = 0; c->max = 255; c->chmask = chmask; c->value = 0; if (index != 1) /* Not volume control */ { c->max = 1; } else { if (read_unit (devc, c, GET_MAX, &max)) { c->max = max; if (read_unit (devc, c, GET_MIN, &min)) { c->min = min; } } } if (c->max <= c->min) {
The device reported bad limits. Try to fix the situation.
if (c->min < 0) c->max = 0; else c->min = 0; if (c->max <= c->min) { c->min = 0; c->max = 255; } } c->scale = 255; if (c->max - c->min < 255) c->scale = c->max - c->min; read_current_value (devc, c); if (usb_trace) { cmn_err (CE_CONT, "Ctl %2d: %d/%s %d ", n, c->unit->num, c->unit->name, c->index); cmn_err (CE_CONT, "Min %d, max %d, scale %d\n", c->min, c->max, c->scale); cmn_err (CE_CONT, "ch=%x ", c->chmask); cmn_err (CE_CONT, "Value %04x, (%d - %d, %d)\n", c->value, c->min, c->max, c->scale); } return n; } static int mixer_func (int dev, int ctrl, unsigned int cmd, int value) { ossusb_devc *devc = mixer_devs[dev]->devc; usb_control_t *c; if (devc->disabled) return OSS_EPIPE; if (ctrl < 0 || ctrl >= devc->ncontrols) return OSS_EIO; c = &devc->controls[ctrl]; if (cmd == SNDCTL_MIX_READ) { return c->value; } if (cmd != SNDCTL_MIX_WRITE) return OSS_EINVAL; value = c->value = write_current_value (devc, c, value); return value; } static char * get_feature_name (int n) { switch (n) { case 0: return "mute"; case 1: return "vol"; case 2: return "bass"; case 3: return "mid"; case 4: return "treble"; case 5: return "eq"; case 6: return "agc"; case 7: return "delay"; case 8: return "boost"; case 9: return "loud"; default: return "misc"; } } static int get_feature_type (int n) { switch (n) { case 0: return MIXT_ONOFF; case 1: return MIXT_SLIDER; case 2: return MIXT_SLIDER; case 3: return MIXT_SLIDER; case 4: return MIXT_SLIDER; case 5: return MIXT_SLIDER; case 6: return MIXT_ONOFF; case 7: return MIXT_SLIDER; case 8: return MIXT_ONOFF; case 9: return MIXT_ONOFF; default: return MIXT_ONOFF; } } struct chmasks { int mask; char *name; char type; char channels; }; static const struct chmasks chmasks[] = { {0x0003, "front", MIXT_STEREOSLIDER, 2}, {0x0001, "L", MIXT_MONOSLIDER, 1}, {0x0002, "R", MIXT_MONOSLIDER, 1}, {0x0030, "surr", MIXT_STEREOSLIDER, 2}, {0x0010, "LS", MIXT_MONOSLIDER, 1}, {0x0020, "RS", MIXT_MONOSLIDER, 1}, {0x000c, "C/L", MIXT_STEREOSLIDER, 2}, {0x0004, "C", MIXT_MONOSLIDER, 1}, {0x0008, "LFE", MIXT_MONOSLIDER, 1}, {0x00c0, "center", MIXT_STEREOSLIDER, 2}, {0x0040, "LC", MIXT_MONOSLIDER, 1}, {0x0080, "RC", MIXT_MONOSLIDER, 1}, {0x0100, "surr", MIXT_STEREOSLIDER, 2}, {0x0600, "side", MIXT_STEREOSLIDER, 2}, {0x0200, "SL", MIXT_MONOSLIDER, 1}, {0x0400, "SR", MIXT_MONOSLIDER, 1}, {0x0800, "top", MIXT_MONOSLIDER, 1}, {0, NULL} }; static int count_source_controls (ossusb_devc * devc, int unit) { int n = 0, nn, i; usb_audio_unit_t *un; unsigned char *d; if (unit <= 0 || unit >= devc->nunits) return 0; un = &devc->units[unit]; d = un->desc; if (un == NULL) return 0; switch (un->typ) { case TY_MIXER: case TY_SELECT: nn = d[4]; d += 5; for (i = 0; i < nn; i++) { n += count_source_controls (devc, *d); d++; } break; case TY_PROC: case TY_EXT: nn = d[6]; d += 7; for (i = 0; i < nn; i++) { n += count_source_controls (devc, *d); d++; } break; default: if (un->source <= 0 && un->source < devc->nunits) { n = count_source_controls (devc, un->source); } } if (!un->ctl_avail) return n; return n + un->control_count; } static int count_target_controls (ossusb_devc * devc, int unit) { int n = 0; usb_audio_unit_t *un; if (unit <= 0 || unit >= devc->nunits) return 0; un = &devc->units[unit]; if (un == NULL) return 0; if (un->typ == TY_SELECT || un->typ == TY_MIXER) { n = 0; } else if (un->target <= 0 && un->target < devc->nunits) { n = count_target_controls (devc, un->target); } if (!un->ctl_avail) return n; return n + un->control_count; } static int follow_source_links (ossusb_devc * devc, usb_audio_unit_t * un) { while (un->source > 0 && un->source < devc->nunits) un = &devc->units[un->source]; return un->num; } static int follow_target_links (ossusb_devc * devc, usb_audio_unit_t * un) { while (un->target > 0 && un->target < devc->nunits) un = &devc->units[un->target]; return un->num; } static void add_controls_for_mixer (ossusb_devc * devc, usb_audio_unit_t * un, int group) { int i, n; unsigned char *d = un->desc; if (!un->ctl_avail) return; n = d[4]; d += 5; for (i = 0; i < n; i++) if (*d > 0 && *d < devc->nunits) { int ref = follow_source_links (devc, &devc->units[*d]); { int ctl = new_ctl (devc, un, i, MIXT_MONOSLIDER, un->chmask); UDB (cmn_err (CE_CONT, "Add mixer control %d/%s\n", un->num, devc->units[ref].name)); if (mixer_ext_create_control (devc->mixer_dev, group, ctl, mixer_func, MIXT_MONOSLIDER, devc->units[ref].name, 255, MIXF_READABLE | MIXF_WRITEABLE) < 0) return; } d++; } un->ctl_avail = 0; } /*ARGSUSED*/ static void add_controls_for_proc (ossusb_devc * devc, usb_audio_unit_t * un, int group) { int i, n; unsigned char *d = un->desc; if (!un->ctl_avail) return; n = d[6]; d += 7; for (i = 0; i < n; i++) if (*d > 0 && *d < devc->nunits) { d++; } un->ctl_avail = 0; } static void add_controls_for_selector (ossusb_devc * devc, usb_audio_unit_t * un, int group) { static oss_mixer_enuminfo ent; char *s; int i, n, err; unsigned char *d = un->desc; int ctl = new_ctl (devc, un, 0, MIXT_ENUM, 0); if (!un->ctl_avail) return; n = d[4]; d += 5; UDB (cmn_err (CE_CONT, "Add selector control %d/%s\n", un->num, un->name)); if ((err = mixer_ext_create_control (devc->mixer_dev, group, ctl, mixer_func, MIXT_ENUM, un->name, n, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return; memset (&ent, 0, sizeof (ent)); s = ent.strings; for (i = 0; i < n; i++) if (*d > 0 && *d < devc->nunits) { int ref = follow_source_links (devc, &devc->units[*d]); d++; if (s > ent.strings) *s++ = '|'; strcpy (s, devc->units[ref].name); s += strlen (s); } s = ent.strings; for (i = 0; i < strlen (s); i++) { if (s[i] == ' ') s[i] = '_'; if (s[i] == '|') s[i] = ' '; } ent.dev = devc->mixer_dev; ent.ctrl = err; ent.nvalues = 0; mixer_ext_set_enum (&ent); un->ctl_avail = 0; } /*ARGSUSED*/ static void add_multich_volumes (ossusb_devc * devc, usb_audio_unit_t * un, int group, int fea, int mask, unsigned short *feature_mask) { int i; for (i = 0; mask != 0 && chmasks[i].mask != 0; i++) { int m = chmasks[i].mask; int ctl; usb_control_t *c; if (!(mask & m)) continue; ctl = new_ctl (devc, un, fea, chmasks[i].type, m); c = &devc->controls[ctl]; UDB (cmn_err (CE_CONT, "Add multich feature control %d/%s\n", un->num, chmasks[i].name)); if (mixer_ext_create_control (devc->mixer_dev, group, ctl, mixer_func, chmasks[i].type, chmasks[i].name, c->scale, MIXF_READABLE | MIXF_WRITEABLE) < 0) return; mask &= ~m; } if (mask) cmn_err (CE_CONT, "Warning! Unsupported channels (%02x)\n", mask); } static void translate_feature_mask (usb_audio_unit_t * un, unsigned short *feature_mask) { int i, n, c; unsigned char *d = un->desc; n = d[5]; if (n < 1 || n > 2) { cmn_err (CE_CONT, "Bad feature mask size %d\n", n); return; } d = d + 6 + n; // Skip the global mask for (c = 0; c < un->channels; c++) { int mask = d[c * n]; if (n > 1) mask |= d[c * n + 1]; for (i = 0; i < n * 8; i++) if ((mask & (1 << i)) && (un->ctl_avail & (1 << i))) { feature_mask[i] |= 1 << c; } } } static void add_controls_for_feature (ossusb_devc * devc, usb_audio_unit_t * un, int group) { int i; unsigned short feature_mask[16], global_mask; if (!un->ctl_avail) return; // Handle the global controls first global_mask = un->desc[6]; if (un->desc[5] > 0) global_mask |= un->desc[7] << 8; for (i = 0; i < 11; i++) if (un->ctl_avail & (1 << i) && (global_mask & (1 << i))) { char *name = get_feature_name (i); int type = get_feature_type (i); int max; char tmp[16]; int ctl; ctl = new_ctl (devc, un, i, type, 0); strcpy (tmp, name); if (type == MIXT_SLIDER) max = devc->controls[ctl].max - devc->controls[ctl].min; else max = 1; if (max > 255) max = 255; UDB (cmn_err (CE_CONT, "Add (global) feature control %d:%s, max=%d\n", un->num, name, max)); if (mixer_ext_create_control (devc->mixer_dev, group, ctl, mixer_func, type, tmp, max, MIXF_READABLE | MIXF_WRITEABLE) < 0) return; //un->ctl_avail &= ~(1 << i); } // Handle the channelized controls if (un->ctl_avail == 0) /* Nothing left */ return; // Translate the channel/feature availability matrix memset (feature_mask, 0, sizeof (feature_mask)); translate_feature_mask (un, feature_mask); for (i = 0; i < 16; i++) if (feature_mask[i]) { char *name = get_feature_name (i); int type = get_feature_type (i); int max, instances = 0; int nc = 0, j; for (j = 0; j < 16; j++) if (feature_mask[i] & (1 << j)) nc = j + 1; if (type == MIXT_SLIDER) { if (nc == 1) { type = MIXT_MONOSLIDER; instances = 1; } else if (nc == 2) { type = MIXT_STEREOSLIDER; instances = 1; } else { type = MIXT_MONOSLIDER; if (i == 1) /* "volume" */ instances = nc; else { instances = 0; type = MIXT_MONOSLIDER; } } max = 255; } else max = 2; if (instances && (instances > 1 || feature_mask[i] > 0x0003)) { int g; char tmp[16]; strcpy (tmp, name); UDB (cmn_err (CE_CONT, "Add feature group %s\n", tmp)); if ((g = mixer_ext_create_group (devc->mixer_dev, group, tmp)) < 0) return; add_multich_volumes (devc, un, g, i, feature_mask[i], feature_mask); } else { char tmp[16]; int ctl = new_ctl (devc, un, i, type, un->chmask); strcpy (tmp, name); max = devc->controls[ctl].max - devc->controls[ctl].min; if (max > 255) max = 255; UDB (cmn_err (CE_CONT, "Add feature control %d:%s\n", un->num, name)); if (mixer_ext_create_control (devc->mixer_dev, group, ctl, mixer_func, type, tmp, max, MIXF_READABLE | MIXF_WRITEABLE) < 0) return; } } un->ctl_avail = 0; // All done (hope so) } static void traverse_source_controls (ossusb_devc * devc, usb_audio_unit_t * un, int group) { unsigned char *d; d = un->desc; if (un->typ == TY_MIXER) { add_controls_for_mixer (devc, un, group); return; } if (un->typ == TY_PROC) { add_controls_for_proc (devc, un, group); if (d[6] > 1) /* More than 1 sources */ return; } if (un->typ == TY_SELECT) { add_controls_for_selector (devc, un, group); return; } if (un->source > 0 && un->source < devc->nunits) { traverse_source_controls (devc, &devc->units[un->source], group); } if (un->typ == TY_FEAT) { add_controls_for_feature (devc, un, group); } } static void traverse_target_controls (ossusb_devc * devc, usb_audio_unit_t * un, int group) { unsigned char *d; d = un->desc; if (un->typ == TY_SELECT) { add_controls_for_selector (devc, un, group); } if (un->typ == TY_PROC || un->typ == TY_EXT) if (d[6] > 1) // More than 1 input pins return; if (un->typ == TY_MIXER) { #if 1 add_controls_for_mixer (devc, un, group); #endif } if (un->target > 0 && un->target < devc->nunits) traverse_target_controls (devc, &devc->units[un->target], group); if (un->typ == TY_FEAT) { add_controls_for_feature (devc, un, group); } } void ossusb_create_altset_control (int dev, int portc_num, int nalt, char *name) { int ctl; ossusb_devc *devc = mixer_devs[dev]->devc; char *as = NULL; int default_altsetting; unsigned int altsetting_mask; if (nalt < 3) /* Only one alternative setting (plus "OFF") available */ return; if ((as = udi_usbdev_get_altsetting_labels (devc->mixer_usbdev, portc_num, &default_altsetting, &altsetting_mask)) != NULL) { if (altsetting_mask != 0) {
Check if more than one of them are enabled.
int i, n = 0; for (i = 1; i < nalt; i++) if (altsetting_mask & (1 << i)) n++; if (n < 2) /* No */ return; } } if ((ctl = mixer_ext_create_control (dev, 0, portc_num, ossusb_change_altsetting, MIXT_ENUM, name, nalt, MIXF_READABLE | MIXF_WRITEABLE)) < 0) return; if (as != NULL) /* Use custom labels */ { mixer_ext_set_strings (dev, ctl, as, 0); if (altsetting_mask != 0) { oss_mixext *ext; int i; ext = mixer_find_ext (dev, ctl); memset (ext->enum_present, 0, sizeof (ext->enum_present)); for (i = 0; i < nalt; i++) if (altsetting_mask & (1 << i)) ext->enum_present[i / 8] |= (1 << (i % 8)); } if (default_altsetting > 0) ossusb_change_altsetting (dev, portc_num, SNDCTL_MIX_WRITE, default_altsetting); } else mixer_ext_set_strings (dev, ctl, "OFF 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19", 0); } static int usb_mix_init (int dev) { ossusb_devc *devc; int i, group; devc = mixer_devs[dev]->devc; for (i = 0; i < devc->nunits; i++) { usb_audio_unit_t *un = &devc->units[i]; if (un->typ == TY_OUTPUT) { if (count_source_controls (devc, un->source) == 0) { continue; } if ((group = mixer_ext_create_group (dev, 0, un->name)) < 0) return group; traverse_source_controls (devc, un, group); continue; } } for (i = 0; i < devc->nunits; i++) { usb_audio_unit_t *un = &devc->units[i]; if (un->typ == TY_INPUT) { if (count_target_controls (devc, un->target) == 0) { continue; } if ((group = mixer_ext_create_group (dev, 0, un->name)) < 0) return group; traverse_target_controls (devc, un, group); un->ctl_avail = 0; continue; } } return 0; } static char * check_feature (usb_audio_unit_t * un, unsigned char *mask, int n) { if (mask[n / 8] & (1 << (n % 8))) { un->control_count++; return get_feature_name (n); } return NULL; } /*ARGSUSED*/ static int usb_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg) { ossusb_devc *devc = mixer_devs[dev]->hw_devc; if (devc->disabled) return OSS_EPIPE; switch (cmd) { case SOUND_MIXER_READ_DEVMASK: return *arg = devc->devmask | devc->recmask; case SOUND_MIXER_READ_RECMASK: return *arg = devc->recmask; case SOUND_MIXER_READ_RECSRC: return *arg = devc->recsrc; case SOUND_MIXER_READ_STEREODEVS: return *arg = devc->stereodevs; } return *arg = 0; } static mixer_driver_t usb_mixer_driver = { usb_mixer_ioctl }; static int init_mixer (ossusb_devc * devc) { if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION, devc->osdev, devc->osdev, devc->dev_name, &usb_mixer_driver, sizeof (mixer_driver_t), devc)) >= 0) { char mxname[20]; sprintf (mxname, "USB_%d", ndevs); devc->levels = load_mixer_volumes (mxname, NULL, 1); mixer_devs[devc->mixer_dev]->hw_devc = devc; mixer_ext_set_init_fn (devc->mixer_dev, usb_mix_init, MAX_CONTROLS); } return 1; } static usb_audio_unit_t * setup_unit (ossusb_devc * devc, int unit, char *name, unsigned char *desc, int desclen, int typ) { usb_audio_unit_t *un; if (unit < 0 || unit >= MAX_UNIT) { cmn_err (CE_WARN, "Bad unit number %d\n", unit); return NULL; } if (typ >= sizeof (devc->unit_counters)) { cmn_err (CE_WARN, "Bad unit type %d\n", typ); return NULL; } devc->unit_counters[typ]++; if (unit >= devc->nunits) devc->nunits = unit + 1; un = &devc->units[unit]; un->num = unit; un->typ = typ; un->desc = desc; un->desclen = desclen; un->mixnum = -1; un->channels = 2; un->chmask = 0x03; strcpy (un->name, name); return un; } static char * get_terminal_id (int type, char *def, int typ, int *mn) { int dummy, *mixnum; if (mn != NULL) mixnum = mn; else mixnum = &dummy; switch (type) { case 0x0101: if (typ == TY_INPUT) { *mixnum = SOUND_MIXER_PCM; return "play"; } if (typ == TY_OUTPUT) { *mixnum = SOUND_MIXER_RECLEV; return "rec"; } break; case 0x0301: *mixnum = SOUND_MIXER_VOLUME; return "output"; case 0x0302: *mixnum = SOUND_MIXER_VOLUME; return "headph"; case 0x0307: return "LFE"; case 0x0401: return "handset"; case 0x0402: return "headset"; case 0x0403: case 0x0404: case 0x0405: case 0x0500: case 0x0501: case 0x0502: case 0x0504: return "phone"; break; case 0x0600: *mixnum = SOUND_MIXER_LINE1; return "aux"; case 0x0601: *mixnum = SOUND_MIXER_LINE2; return "aux"; case 0x0602: return "digital"; case 0x0603: *mixnum = SOUND_MIXER_LINE; return "line"; case 0x0604: *mixnum = SOUND_MIXER_SPEAKER; return "pc_in"; case 0x0605: *mixnum = SOUND_MIXER_DIGITAL1; if (typ == TY_INPUT) return "spdin"; else return "spdout"; case 0x0606: return "da_stream"; case 0x0607: return "DV_audio"; case 0x0701: return "noise"; case 0x0702: return "eq_noise"; case 0x0703: *mixnum = SOUND_MIXER_CD; return "cd"; case 0x0704: return "dat"; case 0x0706: return "md"; case 0x0707: return "tape"; case 0x0708: return "phono"; case 0x0709: *mixnum = SOUND_MIXER_VIDEO; return "vcr"; case 0x070b: return "dvd"; case 0x070c: return "tv"; case 0x070d: return "sat"; case 0x070e: return "cable"; case 0x0710: *mixnum = SOUND_MIXER_RADIO; return "radio"; case 0x0711: *mixnum = SOUND_MIXER_RADIO; return "xmitter"; case 0x0712: return "mtrack"; case 0x0713: *mixnum = SOUND_MIXER_SYNTH; return "synth"; } if (type < 0x201) // Unknown type return def; if (type < 0x300) { *mixnum = SOUND_MIXER_MIC; return "mic"; } return def; } static void setup_legacy_mixer (ossusb_devc * devc) { int x; // Set up the recording selector for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int i, n; unsigned char *d = un->desc; if (un->typ != TY_SELECT || strcmp (un->name, "rec.src") != 0) continue; if (!un->ctl_avail) continue; devc->rec_unit = x; un->ctl_avail = 0; devc->num_recdevs = n = d[4]; d += 5; for (i = 0; i < n; i++) { int ref = follow_source_links (devc, &devc->units[*d]); int mask; d++; if (ref < 1 || ref >= devc->nunits || devc->units[ref].mixnum == -1) continue; mask = (1 << devc->units[ref].mixnum); if (devc->recmask & mask) // Duplicate continue; UDB (cmn_err (CE_CONT, "Recsrc %d, num=%d, mask %x\n", i, ref, mask)); devc->recdevs[i] = mask; devc->recmask |= mask; if (devc->recsrc == 0) devc->recsrc = mask; } break; } // Set up the legacy mixer controls for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int mixnum; unsigned char *d = un->desc; if (!(un->ctl_avail & 0x02)) continue; if (un->typ == TY_FEAT && d[6] & 0x02) // Volume control { int t; mixnum = un->mixnum; if (mixnum == -1) { t = follow_target_links (devc, un); if (t && devc->units[t].mixnum != -1) mixnum = devc->units[t].mixnum; else { t = follow_source_links (devc, un); if (t && devc->units[t].mixnum != -1) mixnum = devc->units[t].mixnum; } } if (mixnum == -1 || un->channels > 2) continue; UDB (cmn_err (CE_CONT, "Unit %d is volume %d\n", x, mixnum)); un->ctl_avail &= ~0x02; // Reserve the volume slider devc->devmask |= (1 << mixnum); if (un->channels == 2) devc->stereodevs |= (1 << mixnum); continue; } } } static char * get_processor_type (int t) { switch (t) { case 0x01: return "upmix"; case 0x02: return "prologic"; case 0x03: return "3D"; case 0x04: return "reverb"; case 0x05: return "chorus"; case 0x06: return "compress"; default: return "proc"; } } static void parse_processing_unit (ossusb_devc * devc, unsigned char *d, int l) { usb_audio_unit_t *un; char *name; name = get_processor_type (d[4]); if ((un = setup_unit (devc, d[3], name, d, l, TY_PROC)) == NULL) return; un->subtyp = d[4]; un->ctl_avail = 1; if (un->subtyp == 1) // Upmix/downmix unit { un->channels = d[8]; un->chmask = ossusb_get_int (&d[9], 2); } } static int get_feature_mask (unsigned char *d, int channels) { int i, n, mask = 0, v; n = d[5]; for (i = 0; i <= channels; i++) { v = d[6 + i * n]; if (n > 1) v |= d[6 + i * n + 1] << 8; mask |= v; } return mask; } #if 1 static void mixer_dump (ossusb_devc * devc) { int i, j, c; usb_audio_unit_t *un; for (i = 1; i < devc->nunits; i++) { un = &devc->units[i]; if (un->typ == TY_FEAT) { cmn_err (CE_CONT, "Unit %d\n", un->num); for (j = 0; j < 8; j++) { for (c = 0; c < 7; c++) { unsigned char buf[2]; int len; buf[0] = buf[1] = 0; len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((j) << 8) | (c), // value (un->num << 8), // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len < 0) continue; cmn_err (CE_CONT, " Feature %d/%d, ch %d: ", un->num, j, c); cmn_err (CE_CONT, "%02x%02x ", buf[0], buf[1]); if (len == 1) cmn_err (CE_CONT, "(%d) ", (signed char) buf[0]); else cmn_err (CE_CONT, "(%d) ", (signed short) (buf[0] | (buf[1] << 8))); buf[0] = buf[1] = 0; len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((j) << 8) | (c), // value (un->num << 8), // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len >= 0) cmn_err (CE_CONT, "Min=%02x%02x ", buf[0], buf[1]); if (len == 1) cmn_err (CE_CONT, "(%d) ", (signed char) buf[0]); else if (len == 2) cmn_err (CE_CONT, "(%d) ", (signed short) (buf[0] | (buf[1] << 8))); buf[0] = buf[1] = 0; len = udi_usb_rcv_control_msg (devc->mixer_usbdev, 0, // endpoint GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS, // rqtype ((j) << 8) | (c), // value (un->num << 8), // index buf, // buffer 2, // buflen OSS_HZ * 2); if (len >= 0) cmn_err (CE_CONT, "max=%02x%02x ", buf[0], buf[1]); if (len == 1) cmn_err (CE_CONT, "(%d) ", (signed char) buf[0]); else if (len == 2) cmn_err (CE_CONT, "(%d) ", (signed short) (buf[0] | (buf[1] << 8))); cmn_err (CE_CONT, "\n"); } } } } } #endif /*ARGSUSED*/ static ossusb_devc * ossusb_init_audioctl (ossusb_devc * devc, udi_usb_devc * usbdev, int inum, int reinit) { int desc_len; unsigned char *desc, *d; int i, l, n = 0, p, x, mixnum = -1; char *name; usb_audio_unit_t *un; /* nsettings = udi_usbdev_get_num_altsettings (usbdev); */ devc->mixer_usbdev = usbdev; if (!init_mixer (devc)) return NULL; desc = udi_usbdev_get_altsetting (usbdev, 0, &desc_len); if (desc == NULL) { cmn_err (CE_CONT, "Can't read interface descriptors\n"); return NULL; } p = 0; while (p < desc_len) { d = desc + p; l = *d; if (usb_trace > 1) { char str[256], *s = str; sprintf (s, "Control desc(%d): ", p); s += strlen (s); for (i = 0; i < l; i++) { sprintf (s, "%02x ", d[i]); s += strlen (s); } cmn_err (CE_CONT, "%s\n", str); } #define CASE(x) case x:cmn_err(CE_CONT, #x "\n"); break; if (d[1] == CS_INTERFACE) switch (d[2]) { case AC_HEADER: if (usb_trace) { cmn_err (CE_CONT, " Audio control interface header\n"); n = d[7]; cmn_err (CE_CONT, " %d related streaming interfaces: ", n); for (i = 0; i < n; i++) { cmn_err (CE_CONT, "%d ", d[8 + i]); } cmn_err (CE_CONT, "\n"); } break; case AC_INPUT_TERMINAL: name = get_terminal_id (ossusb_get_int (&d[4], 2), "in", TY_INPUT, &mixnum); if ((un = setup_unit (devc, d[3], name, d, l, TY_INPUT)) == NULL) return NULL; un->mixnum = mixnum; un->channels = d[7]; un->chmask = ossusb_get_int (&d[8], 2); break; case AC_OUTPUT_TERMINAL: name = get_terminal_id (ossusb_get_int (&d[4], 2), "out", TY_OUTPUT, &mixnum); if ((un = setup_unit (devc, d[3], name, d, l, TY_OUTPUT)) == NULL) return NULL; un->mixnum = mixnum; break; case AC_MIXER_UNIT: if ((un = setup_unit (devc, d[3], "mix", d, l, TY_MIXER)) == NULL) return NULL; { // Check if there are any visible controls int mask = 0, nn; n = d[4]; // # of input pins d += 5 + n; nn = *d; // # of channels d += 4; // Seek to bmControls n = (n * nn + 7) / 8; for (i = 0; i < n; i++) mask |= d[i]; un->ctl_avail = mask; } break; case AC_SELECTOR_UNIT: if ((un = setup_unit (devc, d[3], "src", d, l, TY_SELECT)) == NULL) return NULL; un->ctl_avail = 1; break; case AC_FEATURE_UNIT: if ((un = setup_unit (devc, d[3], "fea", d, l, TY_FEAT)) == NULL) return NULL; un->ctl_avail = get_feature_mask (d, 2); /* For now */ break; case AC_PROCESSING_UNIT: parse_processing_unit (devc, d, l); break; case AC_EXTENSION_UNIT: if ((un = setup_unit (devc, d[3], "ext", d, l, TY_EXT)) == NULL) return NULL; un->ctl_avail = 1; break; } p += l; } // Make sure the unit names are unique */ for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int n = 0; if (un->typ != TY_SELECT) for (i = x + 1; i < devc->nunits; i++) if (strcmp (devc->units[i].name, un->name) == 0) n++; if (n > 0) { char tmpname[16]; strcpy (tmpname, un->name); n = 1; for (i = x; i < devc->nunits; i++) if (strcmp (devc->units[i].name, tmpname) == 0) { if (n > 1) sprintf (devc->units[i].name, "%s%d", tmpname, n); n++; } } // Make sure the mixer control numbers are unique too n = 0; if (un->mixnum != -1) for (i = x + 1; i < devc->nunits; i++) if (devc->units[i].mixnum == un->mixnum) n++; if (n > 0) for (i = x + 1; i < devc->nunits; i++) if (devc->units[i].mixnum == un->mixnum) { usb_audio_unit_t *uu = &devc->units[i]; switch (uu->mixnum) { case SOUND_MIXER_PCM: uu->mixnum = SOUND_MIXER_ALTPCM; break; case SOUND_MIXER_LINE: uu->mixnum = SOUND_MIXER_LINE1; break; case SOUND_MIXER_LINE1: uu->mixnum = SOUND_MIXER_LINE2; break; case SOUND_MIXER_LINE2: uu->mixnum = SOUND_MIXER_LINE3; break; case SOUND_MIXER_DIGITAL1: uu->mixnum = SOUND_MIXER_DIGITAL2; break; case SOUND_MIXER_DIGITAL2: uu->mixnum = SOUND_MIXER_DIGITAL3; break; default: uu->mixnum = -1; } } } // Handle output selector names for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int n; if (un->typ != TY_OUTPUT) continue; n = un->desc[7]; // Source ID if (n < 0 || n >= devc->nunits) continue; if (devc->units[n].target == 0) devc->units[n].target = x; if (devc->units[n].typ == TY_SELECT && usb_mixerstyle == 0) sprintf (devc->units[n].name, "%s.src", un->name); } // Find out the sources for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; d = un->desc; l = un->desclen; switch (un->typ) { case TY_INPUT: break; case TY_OUTPUT: un->source = d[7]; break; case TY_SELECT: un->source = d[5]; n = d[4]; for (i = 0; i < n; i++) if (d[i + 5] > 0 && d[i + 5] <= devc->nunits) if (devc->units[d[i + 5]].target == 0) devc->units[d[i + 5]].target = x; break; case TY_MIXER: n = d[4]; un->control_count = n; un->source = d[5]; for (i = 0; i < n; i++) if (d[i + 5] > 0 && d[i + 5] <= devc->nunits) if (devc->units[d[i + 5]].target == 0) devc->units[d[i + 5]].target = x; d += n + 5; un->channels = *d++; un->chmask = ossusb_get_int (d, 2); break; case TY_FEAT: un->source = d[4]; if (d[4] > 0 && d[4] <= devc->nunits) if (devc->units[d[4]].target == 0) devc->units[d[4]].target = x; break; //case TY_EXT: case TY_PROC: n = d[6]; un->source = d[7]; for (i = 0; i < n; i++) if (d[i + 7] > 0 && d[i + 7] <= devc->nunits) if (devc->units[d[i + 7]].target == 0) devc->units[d[i + 7]].target = x; break; } } // Trace the channel config for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x], *uu; int ref; if (un->typ == TY_INPUT || un->typ == TY_MIXER) continue; if (un->typ == TY_PROC && un->subtyp == 1) // Upmix/downmix unit continue; ref = follow_source_links (devc, un); uu = &devc->units[ref]; un->channels = uu->channels; un->chmask = uu->chmask; } // Handle feature channels for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; if (un->num == 0 || un->typ != TY_FEAT) continue; d = un->desc; l = un->desclen; un->ctl_avail = get_feature_mask (d, un->channels); } // Final checks for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int j; if (un->num == 0) { //cmn_err(CE_CONT, "Skipped undefined control %d\n", x); continue; } d = un->desc; l = un->desclen; switch (un->typ) { case TY_SELECT: n = d[4]; un->control_count = n; d += 5; break; case TY_MIXER: n = d[4]; un->control_count = n; d += 5; break; case TY_FEAT: n = d[5]; d += 6; for (i = 0; i < n * 8; i++) name = check_feature (un, d, i); for (j = 1; j <= un->channels; j++) { for (i = 0; i < n * 8; i++) name = check_feature (un, d + j * n, i); } break; } } #if 0 // Debugging if (usb_trace) for (x = 1; x < devc->nunits; x++) { usb_audio_unit_t *un = &devc->units[x]; int j; int ref = 0; if (un->num == 0) { //cmn_err(CE_CONT, "Skipped undefined control %d\n", x); continue; } d = un->desc; l = un->desclen; // ossusb_dump_desc (d, l); cmn_err (CE_CONT, "%2d: %s ", un->num, un->name); if (un->mixnum != -1) cmn_err (CE_CONT, "mix=%d ", un->mixnum); if (un->source) cmn_err (CE_CONT, "source=%d ", un->source); if (un->target) cmn_err (CE_CONT, "target=%d ", un->target); cmn_err (CE_CONT, "ch=%d/%x ", un->channels, un->chmask); switch (un->typ) { case TY_INPUT: cmn_err (CE_CONT, "Input terminal type: %04x ", ossusb_get_int (&d[4], 2)); cmn_err (CE_CONT, "Associated output 0x%02x ", d[6]); cmn_err (CE_CONT, "#chn %d ", d[7]); cmn_err (CE_CONT, "chconf %04x ", ossusb_get_int (&d[8], 2)); if (d[10]) cmn_err (CE_CONT, "chname# %d ", d[10]); if (d[11]) cmn_err (CE_CONT, "terminalname# %d (%s) ", d[11], udi_usbdev_get_string (usbdev, d[11])); break; case TY_OUTPUT: cmn_err (CE_CONT, "Output terminal type: %04x ", ossusb_get_int (&d[4], 2)); cmn_err (CE_CONT, "Associated input 0x%02x ", d[6]); cmn_err (CE_CONT, "sourceid %d ", d[7]); if (d[8]) cmn_err (CE_CONT, "terminalname# %d ", d[8]); break; case TY_SELECT: n = d[4]; d += 5; cmn_err (CE_CONT, "%d input pins (", n); for (i = 0; i < n; i++) { ref = follow_source_links (devc, &devc->units[*d]); cmn_err (CE_CONT, "%d/%s ", *d, devc->units[ref].name); d++; } cmn_err (CE_CONT, ") "); break; case TY_MIXER: n = d[4]; d += 5; cmn_err (CE_CONT, "%d inputs (", n); for (i = 0; i < n; i++) { ref = follow_source_links (devc, &devc->units[*d]); cmn_err (CE_CONT, "%d/%s ", *d, devc->units[ref].name); d++; } cmn_err (CE_CONT, ") "); break; case TY_FEAT: //ossusb_dump_desc(d, l); cmn_err (CE_CONT, "Source %d:%s ", d[4], devc->units[d[4]].name); n = d[5]; d += 6; cmn_err (CE_CONT, "main (", n); for (i = 0; i < n * 8; i++) if ((name = check_feature (un, d, i)) != NULL) cmn_err (CE_CONT, "%s ", name); cmn_err (CE_CONT, ") "); for (j = 1; j <= un->channels; j++) { cmn_err (CE_CONT, "ch %d (", j); for (i = 0; i < n * 8; i++) if ((name = check_feature (un, d + j * n, i)) != NULL) cmn_err (CE_CONT, "%s ", name); cmn_err (CE_CONT, ") "); } break; case TY_PROC: cmn_err (CE_CONT, "subtype %x Sources/%d) (", un->subtyp, n); n = d[6]; d += 7; cmn_err (CE_CONT, "%d ", *d); d++; cmn_err (CE_CONT, ") "); break; case TY_EXT: cmn_err (CE_CONT, "Extension unit %02x%02x ", d[5], d[4]); break; } cmn_err (CE_CONT, "\n"); } #endif if (usb_mixerstyle == 0) setup_legacy_mixer (devc); touch_mixer (devc->mixer_dev); if (usb_trace) mixer_dump (devc); return devc; } static ossusb_devc * find_devc (char *devpath, int vendor, int product) { int i; for (i = 0; i < ndevs; i++) { if (devc_list[i]->vendor == vendor && devc_list[i]->product == product) if (strcmp (devc_list[i]->devpath, devpath) == 0) { UDB (cmn_err (CE_CONT, "Another instance of '%s'\n", devpath)); return devc_list[i]; } } return NULL; } static void ossusb_device_disconnect (void *d) { ossusb_devc *devc = d; int i; if (devc == NULL) { cmn_err (CE_WARN, "oss_usb_device_disconnect: devc==NULL\n"); return; } if (devc->unload_func) { devc->unload_func (devc); return; } devc->disabled = 1; for (i = 0; i < devc->num_audio_engines; i++) { int dev; dev = devc->portc[i].audio_dev; if (dev < 0 || dev >= num_audio_engines) continue; audio_engines[dev]->enabled = 0; if (devc->mixer_dev >= 0) mixer_devs[devc->mixer_dev]->enabled = 0; } } static void * ossusb_device_attach (udi_usb_devc * usbdev, oss_device_t * osdev) { ossusb_devc *devc; char *devpath; int inum; int old = 1; int i; int class, subclass; int vendor, product; devpath = udi_usbdev_get_devpath (usbdev); inum = udi_usbdev_get_inum (usbdev); class = udi_usbdev_get_class (usbdev); subclass = udi_usbdev_get_subclass (usbdev); vendor = udi_usbdev_get_vendor (usbdev); product = udi_usbdev_get_product (usbdev); if ((devc = find_devc (devpath, vendor, product)) == NULL) { old = 0; if (ndevs >= MAX_DEVC) { cmn_err (CE_CONT, "Too many USB audio devices\n"); return NULL; } if ((devc = PMALLOC (osdev, sizeof (*devc))) == NULL) { cmn_err (CE_CONT, "Out of memory\n"); return NULL; } memset (devc, 0, sizeof (*devc)); devc->mixer_dev = -1; devc->osdev = osdev; osdev->devc = devc; MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV); devc->vendor = vendor; devc->product = product; devc->dev_name = udi_usbdev_get_name (usbdev); strcpy (devc->devpath, udi_usbdev_get_devpath (usbdev)); devc_list[ndevs++] = devc; } else { devc->osdev = osdev; osdev->devc = devc; } if (devc->dev_name == NULL) devc->dev_name = "Generic USB device"; oss_register_device (osdev, devc->dev_name); devc->disabled = 0; if (old) { /* Check if this interface number is already seen */ old = 0; for (i = 0; !old && i < devc->num_interfaces; i++) if (devc->inum[i] == inum) old = 1; } if (!old) { if (devc->num_interfaces >= MAX_IFACE) { cmn_err (CE_CONT, "The device has too many interfaces\n"); return NULL; } devc->usbdev[devc->num_interfaces] = usbdev; devc->last_usbdev = usbdev; devc->inum[devc->num_interfaces] = inum; devc->num_interfaces++; } else { devc->last_usbdev = usbdev; } switch (class) { case USBCLASS_AUDIO: switch (subclass) { case 1: /* Audiocontrol subclass */ devc->main_osdev = osdev; if (!usb_quiet) cmn_err (CE_CONT, "%s audioctl device %s/%d - %s\n", old ? "Reinsert of an" : "New", devc->devpath, inum, devc->dev_name); return ossusb_init_audioctl (devc, usbdev, inum, old); break; case 2: /* Audio streaming subclass */ if (!usb_quiet) cmn_err (CE_CONT, "%s audio streaming device %s/%d - %s\n", old ? "Reinsert of an" : "New", devc->devpath, inum, devc->dev_name); devc->osdev->first_mixer = devc->main_osdev->first_mixer; return ossusb_init_audiostream (devc, usbdev, inum, old); break; case 3: /* MIDI streaming subclass */ return NULL; break; default: cmn_err (CE_CONT, "Unknown USB audio device subclass %x:%d, device=%s\n", class, subclass, devc->dev_name); return NULL; } break; #if 0 case USBCLASS_HID: cmn_err (CE_CONT, "HID interface class %x:%d, device=%s\n", class, subclass, devc->dev_name); return NULL; break; #endif default: cmn_err (CE_CONT, "Unknown USB device class %x:%d, device=%s\n", class, subclass, devc->dev_name); } return devc; } static udi_usb_driver ossusb_driver = { ossusb_device_attach, ossusb_device_disconnect }; int oss_usb_attach (oss_device_t * osdev) { if (usb_trace < 0) { udi_usb_trace = 0; usb_trace = 0; usb_quiet = 1; } else if (usb_trace > 0) udi_usb_trace = usb_trace; return udi_attach_usbdriver (osdev, known_devices, &ossusb_driver); } int oss_usb_detach (oss_device_t * osdev) { if (oss_disable_device (osdev) < 0) return 0; udi_unload_usbdriver (osdev); oss_unregister_device (osdev); return 1; }