diff options
Diffstat (limited to 'sound/usb')
-rw-r--r-- | sound/usb/6fire/chip.c | 4 | ||||
-rw-r--r-- | sound/usb/6fire/comm.c | 5 | ||||
-rw-r--r-- | sound/usb/6fire/comm.h | 2 | ||||
-rw-r--r-- | sound/usb/6fire/control.c | 8 | ||||
-rw-r--r-- | sound/usb/6fire/control.h | 2 | ||||
-rw-r--r-- | sound/usb/6fire/firmware.h | 2 | ||||
-rw-r--r-- | sound/usb/6fire/midi.c | 2 | ||||
-rw-r--r-- | sound/usb/6fire/midi.h | 2 | ||||
-rw-r--r-- | sound/usb/6fire/pcm.c | 11 | ||||
-rw-r--r-- | sound/usb/6fire/pcm.h | 2 | ||||
-rw-r--r-- | sound/usb/Kconfig | 2 | ||||
-rw-r--r-- | sound/usb/caiaq/control.c | 8 | ||||
-rw-r--r-- | sound/usb/caiaq/device.c | 6 | ||||
-rw-r--r-- | sound/usb/card.c | 7 | ||||
-rw-r--r-- | sound/usb/card.h | 2 | ||||
-rw-r--r-- | sound/usb/endpoint.c | 53 | ||||
-rw-r--r-- | sound/usb/endpoint.h | 5 | ||||
-rw-r--r-- | sound/usb/format.c | 10 | ||||
-rw-r--r-- | sound/usb/midi.c | 91 | ||||
-rw-r--r-- | sound/usb/mixer.c | 74 | ||||
-rw-r--r-- | sound/usb/mixer.h | 1 | ||||
-rw-r--r-- | sound/usb/mixer_quirks.c | 223 | ||||
-rw-r--r-- | sound/usb/pcm.c | 175 | ||||
-rw-r--r-- | sound/usb/quirks-table.h | 198 | ||||
-rw-r--r-- | sound/usb/stream.c | 230 | ||||
-rw-r--r-- | sound/usb/usbaudio.h | 1 |
26 files changed, 974 insertions, 152 deletions
diff --git a/sound/usb/6fire/chip.c b/sound/usb/6fire/chip.c index fc8cc823e438..4394ae796356 100644 --- a/sound/usb/6fire/chip.c +++ b/sound/usb/6fire/chip.c @@ -82,8 +82,8 @@ static void usb6fire_chip_destroy(struct sfire_chip *chip) } } -static int __devinit usb6fire_chip_probe(struct usb_interface *intf, - const struct usb_device_id *usb_id) +static int usb6fire_chip_probe(struct usb_interface *intf, + const struct usb_device_id *usb_id) { int ret; int i; diff --git a/sound/usb/6fire/comm.c b/sound/usb/6fire/comm.c index 6c3d531a250e..9e6e3ffd86bb 100644 --- a/sound/usb/6fire/comm.c +++ b/sound/usb/6fire/comm.c @@ -125,16 +125,17 @@ static int usb6fire_comm_write16(struct comm_runtime *rt, u8 request, return usb6fire_comm_send_buffer(buffer, rt->chip->dev); } -int __devinit usb6fire_comm_init(struct sfire_chip *chip) +int usb6fire_comm_init(struct sfire_chip *chip) { struct comm_runtime *rt = kzalloc(sizeof(struct comm_runtime), GFP_KERNEL); - struct urb *urb = &rt->receiver; + struct urb *urb; int ret; if (!rt) return -ENOMEM; + urb = &rt->receiver; rt->serial = 1; rt->chip = chip; usb_init_urb(urb); diff --git a/sound/usb/6fire/comm.h b/sound/usb/6fire/comm.h index d2af0a5ddcf3..6a0840b0dcff 100644 --- a/sound/usb/6fire/comm.h +++ b/sound/usb/6fire/comm.h @@ -36,7 +36,7 @@ struct comm_runtime { u8 vh, u8 vl); }; -int __devinit usb6fire_comm_init(struct sfire_chip *chip); +int usb6fire_comm_init(struct sfire_chip *chip); void usb6fire_comm_abort(struct sfire_chip *chip); void usb6fire_comm_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_COMM_H */ diff --git a/sound/usb/6fire/control.c b/sound/usb/6fire/control.c index 07ed914d5e71..f6434c245720 100644 --- a/sound/usb/6fire/control.c +++ b/sound/usb/6fire/control.c @@ -411,7 +411,7 @@ static int usb6fire_control_digital_thru_get(struct snd_kcontrol *kcontrol, return 0; } -static struct __devinitdata snd_kcontrol_new vol_elements[] = { +static struct snd_kcontrol_new vol_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Playback Volume", @@ -451,7 +451,7 @@ static struct __devinitdata snd_kcontrol_new vol_elements[] = { {} }; -static struct __devinitdata snd_kcontrol_new mute_elements[] = { +static struct snd_kcontrol_new mute_elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Analog Playback Switch", @@ -485,7 +485,7 @@ static struct __devinitdata snd_kcontrol_new mute_elements[] = { {} }; -static struct __devinitdata snd_kcontrol_new elements[] = { +static struct snd_kcontrol_new elements[] = { { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "Line/Phono Capture Route", @@ -561,7 +561,7 @@ static int usb6fire_control_add_virtual( return 0; } -int __devinit usb6fire_control_init(struct sfire_chip *chip) +int usb6fire_control_init(struct sfire_chip *chip) { int i; int ret; diff --git a/sound/usb/6fire/control.h b/sound/usb/6fire/control.h index 9a596d95474a..5a40ba143489 100644 --- a/sound/usb/6fire/control.h +++ b/sound/usb/6fire/control.h @@ -50,7 +50,7 @@ struct control_runtime { u8 ivol_updated; }; -int __devinit usb6fire_control_init(struct sfire_chip *chip); +int usb6fire_control_init(struct sfire_chip *chip); void usb6fire_control_abort(struct sfire_chip *chip); void usb6fire_control_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_CONTROL_H */ diff --git a/sound/usb/6fire/firmware.h b/sound/usb/6fire/firmware.h index 008569895381..c109c4f75aba 100644 --- a/sound/usb/6fire/firmware.h +++ b/sound/usb/6fire/firmware.h @@ -22,6 +22,6 @@ enum /* firmware state of device */ FW_NOT_READY = 1 }; -int __devinit usb6fire_fw_init(struct usb_interface *intf); +int usb6fire_fw_init(struct usb_interface *intf); #endif /* USB6FIRE_FIRMWARE_H */ diff --git a/sound/usb/6fire/midi.c b/sound/usb/6fire/midi.c index f0e5179b242b..26722423330d 100644 --- a/sound/usb/6fire/midi.c +++ b/sound/usb/6fire/midi.c @@ -146,7 +146,7 @@ static struct snd_rawmidi_ops in_ops = { .trigger = usb6fire_midi_in_trigger }; -int __devinit usb6fire_midi_init(struct sfire_chip *chip) +int usb6fire_midi_init(struct sfire_chip *chip) { int ret; struct midi_runtime *rt = kzalloc(sizeof(struct midi_runtime), diff --git a/sound/usb/6fire/midi.h b/sound/usb/6fire/midi.h index 5114eccc1d8e..c321006e5430 100644 --- a/sound/usb/6fire/midi.h +++ b/sound/usb/6fire/midi.h @@ -38,7 +38,7 @@ struct midi_runtime { void (*in_received)(struct midi_runtime *rt, u8 *data, int length); }; -int __devinit usb6fire_midi_init(struct sfire_chip *chip); +int usb6fire_midi_init(struct sfire_chip *chip); void usb6fire_midi_abort(struct sfire_chip *chip); void usb6fire_midi_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_MIDI_H */ diff --git a/sound/usb/6fire/pcm.c b/sound/usb/6fire/pcm.c index c97d05f0e966..e2ca12fe92e9 100644 --- a/sound/usb/6fire/pcm.c +++ b/sound/usb/6fire/pcm.c @@ -135,6 +135,9 @@ static void usb6fire_pcm_stream_stop(struct pcm_runtime *rt) struct control_runtime *ctrl_rt = rt->chip->control; if (rt->stream_state != STREAM_DISABLED) { + + rt->stream_state = STREAM_STOPPING; + for (i = 0; i < PCM_N_URBS; i++) { usb_kill_urb(&rt->in_urbs[i].instance); usb_kill_urb(&rt->out_urbs[i].instance); @@ -559,9 +562,9 @@ static struct snd_pcm_ops pcm_ops = { .pointer = usb6fire_pcm_pointer, }; -static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb, - struct sfire_chip *chip, bool in, int ep, - void (*handler)(struct urb *)) +static void usb6fire_pcm_init_urb(struct pcm_urb *urb, + struct sfire_chip *chip, bool in, int ep, + void (*handler)(struct urb *)) { urb->chip = chip; usb_init_urb(&urb->instance); @@ -578,7 +581,7 @@ static void __devinit usb6fire_pcm_init_urb(struct pcm_urb *urb, urb->instance.number_of_packets = PCM_N_PACKETS_PER_URB; } -int __devinit usb6fire_pcm_init(struct sfire_chip *chip) +int usb6fire_pcm_init(struct sfire_chip *chip) { int i; int ret; diff --git a/sound/usb/6fire/pcm.h b/sound/usb/6fire/pcm.h index 3104301b257d..9b01133ee3fe 100644 --- a/sound/usb/6fire/pcm.h +++ b/sound/usb/6fire/pcm.h @@ -69,7 +69,7 @@ struct pcm_runtime { bool stream_wait_cond; }; -int __devinit usb6fire_pcm_init(struct sfire_chip *chip); +int usb6fire_pcm_init(struct sfire_chip *chip); void usb6fire_pcm_abort(struct sfire_chip *chip); void usb6fire_pcm_destroy(struct sfire_chip *chip); #endif /* USB6FIRE_PCM_H */ diff --git a/sound/usb/Kconfig b/sound/usb/Kconfig index ff77b28f3da1..225dfd737265 100644 --- a/sound/usb/Kconfig +++ b/sound/usb/Kconfig @@ -90,7 +90,7 @@ config SND_USB_CAIAQ_INPUT config SND_USB_US122L tristate "Tascam US-122L USB driver" - depends on X86 && EXPERIMENTAL + depends on X86 select SND_HWDEP select SND_RAWMIDI help diff --git a/sound/usb/caiaq/control.c b/sound/usb/caiaq/control.c index 00e5d0a469e1..adb8d03267a0 100644 --- a/sound/usb/caiaq/control.c +++ b/sound/usb/caiaq/control.c @@ -137,7 +137,7 @@ static int control_put(struct snd_kcontrol *kcontrol, return 1; } -static struct snd_kcontrol_new kcontrol_template __devinitdata = { +static struct snd_kcontrol_new kcontrol_template = { .iface = SNDRV_CTL_ELEM_IFACE_HWDEP, .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, .index = 0, @@ -489,8 +489,8 @@ static struct caiaq_controller kontrols4_controller[] = { { "LED: FX2: Mode", 133 | CNT_INTVAL }, }; -static int __devinit add_controls(struct caiaq_controller *c, int num, - struct snd_usb_caiaqdev *dev) +static int add_controls(struct caiaq_controller *c, int num, + struct snd_usb_caiaqdev *dev) { int i, ret; struct snd_kcontrol *kc; @@ -507,7 +507,7 @@ static int __devinit add_controls(struct caiaq_controller *c, int num, return 0; } -int __devinit snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) +int snd_usb_caiaq_control_init(struct snd_usb_caiaqdev *dev) { int ret = 0; diff --git a/sound/usb/caiaq/device.c b/sound/usb/caiaq/device.c index 7da0d0aa72cb..c828f8189c25 100644 --- a/sound/usb/caiaq/device.c +++ b/sound/usb/caiaq/device.c @@ -289,7 +289,7 @@ int snd_usb_caiaq_set_auto_msg(struct snd_usb_caiaqdev *dev, tmp, sizeof(tmp)); } -static void __devinit setup_card(struct snd_usb_caiaqdev *dev) +static void setup_card(struct snd_usb_caiaqdev *dev) { int ret; char val[4]; @@ -407,7 +407,7 @@ static int create_card(struct usb_device *usb_dev, return 0; } -static int __devinit init_card(struct snd_usb_caiaqdev *dev) +static int init_card(struct snd_usb_caiaqdev *dev) { char *c, usbpath[32]; struct usb_device *usb_dev = dev->chip.dev; @@ -481,7 +481,7 @@ static int __devinit init_card(struct snd_usb_caiaqdev *dev) return 0; } -static int __devinit snd_probe(struct usb_interface *intf, +static int snd_probe(struct usb_interface *intf, const struct usb_device_id *id) { int ret; diff --git a/sound/usb/card.c b/sound/usb/card.c index dbf7999d18b4..ccf95cfe186f 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -25,9 +25,6 @@ * * NOTES: * - * - async unlink should be used for avoiding the sleep inside lock. - * 2.4.22 usb-uhci seems buggy for async unlinking and results in - * oops. in such a cse, pass async_unlink=0 option. * - the linked URBs would be preferred but not used so far because of * the instability of unlinking. * - type II is not supported properly. there is no device which supports @@ -83,7 +80,6 @@ static bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;/* Enable this card * static int vid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 }; static int nrpacks = 8; /* max. number of packets per urb */ -static bool async_unlink = 1; static int device_setup[SNDRV_CARDS]; /* device parameter for this card */ static bool ignore_ctl_error; @@ -99,8 +95,6 @@ module_param_array(pid, int, NULL, 0444); MODULE_PARM_DESC(pid, "Product ID for the USB audio device."); module_param(nrpacks, int, 0644); MODULE_PARM_DESC(nrpacks, "Max. number of packets per URB."); -module_param(async_unlink, bool, 0444); -MODULE_PARM_DESC(async_unlink, "Use async unlink mode."); module_param_array(device_setup, int, NULL, 0444); MODULE_PARM_DESC(device_setup, "Specific device setup (if needed)."); module_param(ignore_ctl_error, bool, 0444); @@ -345,7 +339,6 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx, chip->card = card; chip->setup = device_setup[idx]; chip->nrpacks = nrpacks; - chip->async_unlink = async_unlink; chip->probing = 1; chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor), diff --git a/sound/usb/card.h b/sound/usb/card.h index 814cb357ff88..8a751b4887ea 100644 --- a/sound/usb/card.h +++ b/sound/usb/card.h @@ -27,6 +27,7 @@ struct audioformat { unsigned int nr_rates; /* number of rate table entries */ unsigned int *rate_table; /* rate table */ unsigned char clock; /* associated clock */ + struct snd_pcm_chmap_elem *chmap; /* (optional) channel map */ }; struct snd_usb_substream; @@ -109,6 +110,7 @@ struct snd_usb_substream { struct audioformat *cur_audiofmt; /* current audioformat pointer (for hw_params callback) */ snd_pcm_format_t pcm_format; /* current audio format (for hw_params callback) */ unsigned int channels; /* current number of channels (for hw_params callback) */ + unsigned int channels_max; /* max channels in the all audiofmts */ unsigned int cur_rate; /* current rate (for hw_params callback) */ unsigned int period_bytes; /* current period bytes (for hw_params callback) */ unsigned int altset_idx; /* USB data format: index of alternate setting */ diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c index 34de6f2faf61..21049b882ee6 100644 --- a/sound/usb/endpoint.c +++ b/sound/usb/endpoint.c @@ -485,15 +485,10 @@ __exit_unlock: static int wait_clear_urbs(struct snd_usb_endpoint *ep) { unsigned long end_time = jiffies + msecs_to_jiffies(1000); - unsigned int i; int alive; do { - alive = 0; - for (i = 0; i < ep->nurbs; i++) - if (test_bit(i, &ep->active_mask)) - alive++; - + alive = bitmap_weight(&ep->active_mask, ep->nurbs); if (!alive) break; @@ -520,33 +515,24 @@ void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep) /* * unlink active urbs. */ -static int deactivate_urbs(struct snd_usb_endpoint *ep, int force, int can_sleep) +static int deactivate_urbs(struct snd_usb_endpoint *ep, bool force) { unsigned int i; - int async; if (!force && ep->chip->shutdown) /* to be sure... */ return -EBADFD; - async = !can_sleep && ep->chip->async_unlink; - clear_bit(EP_FLAG_RUNNING, &ep->flags); INIT_LIST_HEAD(&ep->ready_playback_urbs); ep->next_packet_read_pos = 0; ep->next_packet_write_pos = 0; - if (!async && in_interrupt()) - return 0; - for (i = 0; i < ep->nurbs; i++) { if (test_bit(i, &ep->active_mask)) { if (!test_and_set_bit(i, &ep->unlink_mask)) { struct urb *u = ep->urb[i].urb; - if (async) - usb_unlink_urb(u); - else - usb_kill_urb(u); + usb_unlink_urb(u); } } } @@ -566,7 +552,7 @@ static void release_urbs(struct snd_usb_endpoint *ep, int force) ep->prepare_data_urb = NULL; /* stop urbs */ - deactivate_urbs(ep, force, 1); + deactivate_urbs(ep, force); wait_clear_urbs(ep); for (i = 0; i < ep->nurbs; i++) @@ -829,7 +815,7 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, * * Returns an error if the URB submission failed, 0 in all other cases. */ -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep) { int err; unsigned int i; @@ -842,7 +828,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) return 0; /* just to be sure */ - deactivate_urbs(ep, 0, can_sleep); + deactivate_urbs(ep, false); if (can_sleep) wait_clear_urbs(ep); @@ -896,7 +882,7 @@ int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep) __error: clear_bit(EP_FLAG_RUNNING, &ep->flags); ep->use_count--; - deactivate_urbs(ep, 0, 0); + deactivate_urbs(ep, false); return -EPIPE; } @@ -910,9 +896,11 @@ __error: * actually be deactivated. * * Must be balanced to calls of snd_usb_endpoint_start(). + * + * The caller needs to synchronize the pending stop operation via + * snd_usb_endpoint_sync_pending_stop(). */ -void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, - int force, int can_sleep, int wait) +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep) { if (!ep) return; @@ -921,16 +909,12 @@ void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, return; if (--ep->use_count == 0) { - deactivate_urbs(ep, force, can_sleep); + deactivate_urbs(ep, false); ep->data_subs = NULL; ep->sync_slave = NULL; ep->retire_data_urb = NULL; ep->prepare_data_urb = NULL; - - if (wait) - wait_clear_urbs(ep); - else - set_bit(EP_FLAG_STOPPING, &ep->flags); + set_bit(EP_FLAG_STOPPING, &ep->flags); } } @@ -952,7 +936,7 @@ int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep) if (!ep) return -EINVAL; - deactivate_urbs(ep, 1, 1); + deactivate_urbs(ep, true); wait_clear_urbs(ep); if (ep->use_count != 0) @@ -1034,15 +1018,18 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep, /* * Iterate through the inbound packet and prepare the lengths * for the output packet. The OUT packet we are about to send - * will have the same amount of payload bytes than the IN - * packet we just received. + * will have the same amount of payload bytes per stride as the + * IN packet we just received. Since the actual size is scaled + * by the stride, use the sender stride to calculate the length + * in case the number of channels differ between the implicitly + * fed-back endpoint and the synchronizing endpoint. */ out_packet->packets = in_ctx->packets; for (i = 0; i < in_ctx->packets; i++) { if (urb->iso_frame_desc[i].status == 0) out_packet->packet_size[i] = - urb->iso_frame_desc[i].actual_length / ep->stride; + urb->iso_frame_desc[i].actual_length / sender->stride; else out_packet->packet_size[i] = 0; } diff --git a/sound/usb/endpoint.h b/sound/usb/endpoint.h index 3d4c9705041f..447902dd8a4a 100644 --- a/sound/usb/endpoint.h +++ b/sound/usb/endpoint.h @@ -16,9 +16,8 @@ int snd_usb_endpoint_set_params(struct snd_usb_endpoint *ep, struct audioformat *fmt, struct snd_usb_endpoint *sync_ep); -int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, int can_sleep); -void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep, - int force, int can_sleep, int wait); +int snd_usb_endpoint_start(struct snd_usb_endpoint *ep, bool can_sleep); +void snd_usb_endpoint_stop(struct snd_usb_endpoint *ep); void snd_usb_endpoint_sync_pending_stop(struct snd_usb_endpoint *ep); int snd_usb_endpoint_activate(struct snd_usb_endpoint *ep); int snd_usb_endpoint_deactivate(struct snd_usb_endpoint *ep); diff --git a/sound/usb/format.c b/sound/usb/format.c index ddfef57c4c9f..e831ee4238bb 100644 --- a/sound/usb/format.c +++ b/sound/usb/format.c @@ -155,7 +155,7 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof if (fmt[0] < offset + 1 + 3 * (nr_rates ? nr_rates : 2)) { snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n", chip->dev->devnum, fp->iface, fp->altsetting); - return -1; + return -EINVAL; } if (nr_rates) { @@ -167,7 +167,7 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL); if (fp->rate_table == NULL) { snd_printk(KERN_ERR "cannot malloc\n"); - return -1; + return -ENOMEM; } fp->nr_rates = 0; @@ -198,7 +198,7 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof } if (!fp->nr_rates) { hwc_debug("All rates were zero. Skipping format!\n"); - return -1; + return -EINVAL; } } else { /* continuous rates */ @@ -383,7 +383,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, fp->formats = parse_audio_format_i_type(chip, fp, format, fmt, protocol); if (!fp->formats) - return -1; + return -EINVAL; } /* gather possible sample rates */ @@ -409,7 +409,7 @@ static int parse_audio_format_i(struct snd_usb_audio *chip, if (fp->channels < 1) { snd_printk(KERN_ERR "%d:%u:%d : invalid channels %d\n", chip->dev->devnum, fp->iface, fp->altsetting, fp->channels); - return -1; + return -EINVAL; } return ret; diff --git a/sound/usb/midi.c b/sound/usb/midi.c index eeefbce3873c..34b9bb7fe87c 100644 --- a/sound/usb/midi.c +++ b/sound/usb/midi.c @@ -116,6 +116,7 @@ struct snd_usb_midi { struct list_head list; struct timer_list error_timer; spinlock_t disc_lock; + struct rw_semaphore disc_rwsem; struct mutex mutex; u32 usb_id; int next_midi_device; @@ -125,8 +126,10 @@ struct snd_usb_midi { struct snd_usb_midi_in_endpoint *in; } endpoints[MIDI_MAX_ENDPOINTS]; unsigned long input_triggered; - unsigned int opened; + bool autopm_reference; + unsigned int opened[2]; unsigned char disconnected; + unsigned char input_running; struct snd_kcontrol *roland_load_ctl; }; @@ -148,7 +151,6 @@ struct snd_usb_midi_out_endpoint { struct snd_usb_midi_out_endpoint* ep; struct snd_rawmidi_substream *substream; int active; - bool autopm_reference; uint8_t cable; /* cable number << 4 */ uint8_t state; #define STATE_UNKNOWN 0 @@ -1033,29 +1035,58 @@ static void update_roland_altsetting(struct snd_usb_midi* umidi) snd_usbmidi_input_start(&umidi->list); } -static void substream_open(struct snd_rawmidi_substream *substream, int open) +static int substream_open(struct snd_rawmidi_substream *substream, int dir, + int open) { struct snd_usb_midi* umidi = substream->rmidi->private_data; struct snd_kcontrol *ctl; + int err; + + down_read(&umidi->disc_rwsem); + if (umidi->disconnected) { + up_read(&umidi->disc_rwsem); + return open ? -ENODEV : 0; + } mutex_lock(&umidi->mutex); if (open) { - if (umidi->opened++ == 0 && umidi->roland_load_ctl) { - ctl = umidi->roland_load_ctl; - ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(umidi->card, + if (!umidi->opened[0] && !umidi->opened[1]) { + err = usb_autopm_get_interface(umidi->iface); + umidi->autopm_reference = err >= 0; + if (err < 0 && err != -EACCES) { + mutex_unlock(&umidi->mutex); + up_read(&umidi->disc_rwsem); + return -EIO; + } + if (umidi->roland_load_ctl) { + ctl = umidi->roland_load_ctl; + ctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(umidi->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); - update_roland_altsetting(umidi); + update_roland_altsetting(umidi); + } } + umidi->opened[dir]++; + if (umidi->opened[1]) + snd_usbmidi_input_start(&umidi->list); } else { - if (--umidi->opened == 0 && umidi->roland_load_ctl) { - ctl = umidi->roland_load_ctl; - ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; - snd_ctl_notify(umidi->card, + umidi->opened[dir]--; + if (!umidi->opened[1]) + snd_usbmidi_input_stop(&umidi->list); + if (!umidi->opened[0] && !umidi->opened[1]) { + if (umidi->roland_load_ctl) { + ctl = umidi->roland_load_ctl; + ctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_INACTIVE; + snd_ctl_notify(umidi->card, SNDRV_CTL_EVENT_MASK_INFO, &ctl->id); + } + if (umidi->autopm_reference) + usb_autopm_put_interface(umidi->iface); } } mutex_unlock(&umidi->mutex); + up_read(&umidi->disc_rwsem); + return 0; } static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) @@ -1063,7 +1094,6 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) struct snd_usb_midi* umidi = substream->rmidi->private_data; struct usbmidi_out_port* port = NULL; int i, j; - int err; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) if (umidi->endpoints[i].out) @@ -1076,25 +1106,15 @@ static int snd_usbmidi_output_open(struct snd_rawmidi_substream *substream) snd_BUG(); return -ENXIO; } - err = usb_autopm_get_interface(umidi->iface); - port->autopm_reference = err >= 0; - if (err < 0 && err != -EACCES) - return -EIO; + substream->runtime->private_data = port; port->state = STATE_UNKNOWN; - substream_open(substream, 1); - return 0; + return substream_open(substream, 0, 1); } static int snd_usbmidi_output_close(struct snd_rawmidi_substream *substream) { - struct snd_usb_midi* umidi = substream->rmidi->private_data; - struct usbmidi_out_port *port = substream->runtime->private_data; - - substream_open(substream, 0); - if (port->autopm_reference) - usb_autopm_put_interface(umidi->iface); - return 0; + return substream_open(substream, 0, 0); } static void snd_usbmidi_output_trigger(struct snd_rawmidi_substream *substream, int up) @@ -1147,14 +1167,12 @@ static void snd_usbmidi_output_drain(struct snd_rawmidi_substream *substream) static int snd_usbmidi_input_open(struct snd_rawmidi_substream *substream) { - substream_open(substream, 1); - return 0; + return substream_open(substream, 1, 1); } static int snd_usbmidi_input_close(struct snd_rawmidi_substream *substream) { - substream_open(substream, 0); - return 0; + return substream_open(substream, 1, 0); } static void snd_usbmidi_input_trigger(struct snd_rawmidi_substream *substream, int up) @@ -1403,9 +1421,12 @@ void snd_usbmidi_disconnect(struct list_head* p) * a timer may submit an URB. To reliably break the cycle * a flag under lock must be used */ + down_write(&umidi->disc_rwsem); spin_lock_irq(&umidi->disc_lock); umidi->disconnected = 1; spin_unlock_irq(&umidi->disc_lock); + up_write(&umidi->disc_rwsem); + for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->out) @@ -2060,12 +2081,15 @@ void snd_usbmidi_input_stop(struct list_head* p) unsigned int i, j; umidi = list_entry(p, struct snd_usb_midi, list); + if (!umidi->input_running) + return; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) { struct snd_usb_midi_endpoint* ep = &umidi->endpoints[i]; if (ep->in) for (j = 0; j < INPUT_URBS; ++j) usb_kill_urb(ep->in->urbs[j]); } + umidi->input_running = 0; } static void snd_usbmidi_input_start_ep(struct snd_usb_midi_in_endpoint* ep) @@ -2090,8 +2114,11 @@ void snd_usbmidi_input_start(struct list_head* p) int i; umidi = list_entry(p, struct snd_usb_midi, list); + if (umidi->input_running || !umidi->opened[1]) + return; for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) snd_usbmidi_input_start_ep(umidi->endpoints[i].in); + umidi->input_running = 1; } /* @@ -2117,6 +2144,7 @@ int snd_usbmidi_create(struct snd_card *card, umidi->usb_protocol_ops = &snd_usbmidi_standard_ops; init_timer(&umidi->error_timer); spin_lock_init(&umidi->disc_lock); + init_rwsem(&umidi->disc_rwsem); mutex_init(&umidi->mutex); umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor), le16_to_cpu(umidi->dev->descriptor.idProduct)); @@ -2229,9 +2257,6 @@ int snd_usbmidi_create(struct snd_card *card, } list_add_tail(&umidi->list, midi_list); - - for (i = 0; i < MIDI_MAX_ENDPOINTS; ++i) - snd_usbmidi_input_start_ep(umidi->endpoints[i].in); return 0; } diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c index 298070e8f2d4..ed4d89c8b52a 100644 --- a/sound/usb/mixer.c +++ b/sound/usb/mixer.c @@ -382,6 +382,8 @@ error: static int get_ctl_value(struct usb_mixer_elem_info *cval, int request, int validx, int *value_ret) { + validx += cval->idx_off; + return (cval->mixer->protocol == UAC_VERSION_1) ? get_ctl_value_v1(cval, request, validx, value_ret) : get_ctl_value_v2(cval, request, validx, value_ret); @@ -432,6 +434,8 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval, unsigned char buf[2]; int idx = 0, val_len, err, timeout = 10; + validx += cval->idx_off; + if (cval->mixer->protocol == UAC_VERSION_1) { val_len = cval->val_type >= USB_MIXER_S16 ? 2 : 1; } else { /* UAC_VERSION_2 */ @@ -719,8 +723,19 @@ static int check_input_term(struct mixer_build *state, int id, struct usb_audio_ return 0; } case UAC1_PROCESSING_UNIT: - case UAC1_EXTENSION_UNIT: { + case UAC1_EXTENSION_UNIT: + /* UAC2_PROCESSING_UNIT_V2 */ + /* UAC2_EFFECT_UNIT */ { struct uac_processing_unit_descriptor *d = p1; + + if (state->mixer->protocol == UAC_VERSION_2 && + hdr[2] == UAC2_EFFECT_UNIT) { + /* UAC2/UAC1 unit IDs overlap here in an + * uncompatible way. Ignore this unit for now. + */ + return 0; + } + if (d->bNrInPins) { id = d->baSourceID[0]; break; /* continue to parse */ @@ -791,6 +806,33 @@ static void volume_control_quirks(struct usb_mixer_elem_info *cval, struct snd_kcontrol *kctl) { switch (cval->mixer->chip->usb_id) { + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + if (strcmp(kctl->id.name, "Effect Duration") == 0) { + cval->min = 0x0000; + cval->max = 0xffff; + cval->res = 0x00e6; + break; + } + if (strcmp(kctl->id.name, "Effect Volume") == 0 || + strcmp(kctl->id.name, "Effect Feedback Volume") == 0) { + cval->min = 0x00; + cval->max = 0xff; + break; + } + if (strstr(kctl->id.name, "Effect Return") != NULL) { + cval->min = 0xb706; + cval->max = 0xff7b; + cval->res = 0x0073; + break; + } + if ((strstr(kctl->id.name, "Playback Volume") != NULL) || + (strstr(kctl->id.name, "Effect Send") != NULL)) { + cval->min = 0xb5fb; /* -73 dB = 0xb6ff */ + cval->max = 0xfcfe; + cval->res = 0x0073; + } + break; + case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ if (strcmp(kctl->id.name, "Effect Duration") == 0) { @@ -1094,6 +1136,32 @@ static size_t append_ctl_name(struct snd_kcontrol *kctl, const char *str) return strlcat(kctl->id.name, str, sizeof(kctl->id.name)); } +/* A lot of headsets/headphones have a "Speaker" mixer. Make sure we + rename it to "Headphone". We determine if something is a headphone + similar to how udev determines form factor. */ +static void check_no_speaker_on_headset(struct snd_kcontrol *kctl, + struct snd_card *card) +{ + const char *names_to_check[] = { + "Headset", "headset", "Headphone", "headphone", NULL}; + const char **s; + bool found = 0; + + if (strcmp("Speaker", kctl->id.name)) + return; + + for (s = names_to_check; *s; s++) + if (strstr(card->shortname, *s)) { + found = 1; + break; + } + + if (!found) + return; + + strlcpy(kctl->id.name, "Headphone", sizeof(kctl->id.name)); +} + static void build_feature_ctl(struct mixer_build *state, void *raw_desc, unsigned int ctl_mask, int control, struct usb_audio_term *iterm, int unitid, @@ -1180,6 +1248,10 @@ static void build_feature_ctl(struct mixer_build *state, void *raw_desc, len = snprintf(kctl->id.name, sizeof(kctl->id.name), "Feature %d", unitid); } + + if (!mapped_name) + check_no_speaker_on_headset(kctl, state->mixer->chip->card); + /* determine the stream direction: * if the connected output is USB stream, then it's likely a * capture stream. otherwise it should be playback (hopefully :) diff --git a/sound/usb/mixer.h b/sound/usb/mixer.h index a7f3d45a8acf..aab80df201bd 100644 --- a/sound/usb/mixer.h +++ b/sound/usb/mixer.h @@ -43,6 +43,7 @@ struct usb_mixer_elem_info { unsigned int id; unsigned int control; /* CS or ICN (high byte) */ unsigned int cmask; /* channel mask bitmap: 0 = master */ + unsigned int idx_off; /* Control index offset */ unsigned int ch_readonly; unsigned int master_readonly; int channels; diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c index ae2b71435220..0422b1360af3 100644 --- a/sound/usb/mixer_quirks.c +++ b/sound/usb/mixer_quirks.c @@ -63,11 +63,12 @@ static void usb_mixer_elem_free(struct snd_kcontrol *kctl) * Since there doesn't seem to be a devices that needs a multichannel * version, we keep it mono for simplicity. */ -static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, +static int snd_create_std_mono_ctl_offset(struct usb_mixer_interface *mixer, unsigned int unitid, unsigned int control, unsigned int cmask, int val_type, + unsigned int idx_off, const char *name, snd_kcontrol_tlv_rw_t *tlv_callback) { @@ -85,6 +86,7 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, cval->channels = 1; cval->control = control; cval->cmask = cmask; + cval->idx_off = idx_off; /* get_min_max() is called only for integer volumes later, * so provide a short-cut for booleans */ @@ -120,6 +122,18 @@ static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, return 0; } +static int snd_create_std_mono_ctl(struct usb_mixer_interface *mixer, + unsigned int unitid, + unsigned int control, + unsigned int cmask, + int val_type, + const char *name, + snd_kcontrol_tlv_rw_t *tlv_callback) +{ + return snd_create_std_mono_ctl_offset(mixer, unitid, control, cmask, + val_type, 0 /* Offset */, name, tlv_callback); +} + /* * Create a set of standard UAC controls from a table */ @@ -416,6 +430,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry, } } +/* ASUS Xonar U1 / U3 controls */ + static int snd_xonar_u1_switch_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -621,11 +637,13 @@ static int snd_nativeinstruments_create_mixer(struct usb_mixer_interface *mixer, } /* M-Audio FastTrack Ultra quirks */ -/* FTU Effect switch */ +/* FTU Effect switch (also used by C400) */ struct snd_ftu_eff_switch_priv_val { struct usb_mixer_interface *mixer; int cached_value; int is_cached; + int bUnitID; + int validx; }; static int snd_ftu_eff_switch_info(struct snd_kcontrol *kcontrol, @@ -660,9 +678,8 @@ static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, struct snd_ftu_eff_switch_priv_val *pval; int err; unsigned char value[2]; + int id, validx; - const int id = 6; - const int validx = 1; const int val_len = 2; value[0] = 0x00; @@ -684,6 +701,8 @@ static int snd_ftu_eff_switch_get(struct snd_kcontrol *kctl, if (snd_BUG_ON(!chip)) return -EINVAL; + id = pval->bUnitID; + validx = pval->validx; down_read(&mixer->chip->shutdown_rwsem); if (mixer->chip->shutdown) @@ -714,10 +733,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, struct usb_mixer_interface *mixer; int changed, cur_val, err, new_val; unsigned char value[2]; + int id, validx; - - const int id = 6; - const int validx = 1; const int val_len = 2; changed = 0; @@ -735,6 +752,9 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, if (snd_BUG_ON(!chip)) return -EINVAL; + id = pval->bUnitID; + validx = pval->validx; + if (!pval->is_cached) { /* Read current value */ down_read(&mixer->chip->shutdown_rwsem); @@ -779,7 +799,8 @@ static int snd_ftu_eff_switch_put(struct snd_kcontrol *kctl, return changed; } -static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) +static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer, + int validx, int bUnitID) { static struct snd_kcontrol_new template = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, @@ -802,6 +823,8 @@ static int snd_ftu_create_effect_switch(struct usb_mixer_interface *mixer) pval->cached_value = 0; pval->is_cached = 0; pval->mixer = mixer; + pval->bUnitID = bUnitID; + pval->validx = validx; template.private_value = (unsigned long) pval; kctl = snd_ctl_new1(&template, mixer->chip); @@ -960,9 +983,10 @@ static int snd_ftu_create_mixer(struct usb_mixer_interface *mixer) if (err < 0) return err; - err = snd_ftu_create_effect_switch(mixer); + err = snd_ftu_create_effect_switch(mixer, 1, 6); if (err < 0) return err; + err = snd_ftu_create_effect_volume_ctl(mixer); if (err < 0) return err; @@ -1005,6 +1029,178 @@ void snd_emuusb_set_samplerate(struct snd_usb_audio *chip, } } +/* M-Audio Fast Track C400 */ +/* C400 volume controls, this control needs a volume quirk, see mixer.c */ +static int snd_c400_create_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask, offset; + int out, chan, err; + + const unsigned int id = 0x40; + const int val_type = USB_MIXER_S16; + const int control = 1; + + for (chan = 0; chan < 10; chan++) { + for (out = 0; out < 6; out++) { + if (chan < 6) { + snprintf(name, sizeof(name), + "PCM%d-Out%d Playback Volume", + chan + 1, out + 1); + } else { + snprintf(name, sizeof(name), + "In%d-Out%d Playback Volume", + chan - 5, out + 1); + } + + cmask = (out == 0) ? 0 : 1 << (out - 1); + offset = chan * 6; + err = snd_create_std_mono_ctl_offset(mixer, id, control, + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + } + + return 0; +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_volume_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Volume"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_U8; + const unsigned int control = 3; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_duration_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Duration"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_S16; + const unsigned int control = 4; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, snd_usb_mixer_vol_tlv); +} + +/* This control needs a volume quirk, see mixer.c */ +static int snd_c400_create_effect_feedback_ctl(struct usb_mixer_interface *mixer) +{ + static const char name[] = "Effect Feedback Volume"; + const unsigned int id = 0x43; + const int val_type = USB_MIXER_U8; + const unsigned int control = 5; + const unsigned int cmask = 0; + + return snd_create_std_mono_ctl(mixer, id, control, cmask, val_type, + name, NULL); +} + +static int snd_c400_create_effect_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask; + int chan, err; + + const unsigned int id = 0x42; + const int val_type = USB_MIXER_S16; + const int control = 1; + + for (chan = 0; chan < 10; chan++) { + if (chan < 6) { + snprintf(name, sizeof(name), + "Effect Send DOut%d", + chan + 1); + } else { + snprintf(name, sizeof(name), + "Effect Send AIn%d", + chan - 5); + } + + cmask = (chan == 0) ? 0 : 1 << (chan - 1); + err = snd_create_std_mono_ctl(mixer, id, control, + cmask, val_type, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + + return 0; +} + +static int snd_c400_create_effect_ret_vol_ctls(struct usb_mixer_interface *mixer) +{ + char name[64]; + unsigned int cmask; + int chan, err; + + const unsigned int id = 0x40; + const int val_type = USB_MIXER_S16; + const int control = 1; + const int chan_id[6] = { 0, 7, 2, 9, 4, 0xb }; + const unsigned int offset = 0x3c; + /* { 0x3c, 0x43, 0x3e, 0x45, 0x40, 0x47 } */ + + for (chan = 0; chan < 6; chan++) { + snprintf(name, sizeof(name), + "Effect Return %d", + chan + 1); + + cmask = (chan_id[chan] == 0) ? 0 : 1 << (chan_id[chan] - 1); + err = snd_create_std_mono_ctl_offset(mixer, id, control, + cmask, val_type, offset, name, + &snd_usb_mixer_vol_tlv); + if (err < 0) + return err; + } + + return 0; +} + +static int snd_c400_create_mixer(struct usb_mixer_interface *mixer) +{ + int err; + + err = snd_c400_create_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_ret_vol_ctls(mixer); + if (err < 0) + return err; + + err = snd_ftu_create_effect_switch(mixer, 2, 0x43); + if (err < 0) + return err; + + err = snd_c400_create_effect_volume_ctl(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_duration_ctl(mixer); + if (err < 0) + return err; + + err = snd_c400_create_effect_feedback_ctl(mixer); + if (err < 0) + return err; + + return 0; +} + /* * The mixer units for Ebox-44 are corrupt, and even where they * are valid they presents mono controls as L and R channels of @@ -1102,13 +1298,18 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer) snd_audigy2nx_proc_read); break; + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + err = snd_c400_create_mixer(mixer); + break; + case USB_ID(0x0763, 0x2080): /* M-Audio Fast Track Ultra */ case USB_ID(0x0763, 0x2081): /* M-Audio Fast Track Ultra 8R */ err = snd_ftu_create_mixer(mixer); break; - case USB_ID(0x0b05, 0x1739): - case USB_ID(0x0b05, 0x1743): + case USB_ID(0x0b05, 0x1739): /* ASUS Xonar U1 */ + case USB_ID(0x0b05, 0x1743): /* ASUS Xonar U1 (2) */ + case USB_ID(0x0b05, 0x17a0): /* ASUS Xonar U3 */ err = snd_xonar_u1_controls_create(mixer); break; diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c index ef6fa24fc473..c6593101c049 100644 --- a/sound/usb/pcm.c +++ b/sound/usb/pcm.c @@ -46,6 +46,9 @@ snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs, int frame_diff; int est_delay; + if (!subs->last_delay) + return 0; /* short path */ + current_frame_number = usb_get_current_frame_number(subs->dev); /* * HCD implementations use different widths, use lower 8 bits. @@ -75,7 +78,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream return SNDRV_PCM_POS_XRUN; spin_lock(&subs->lock); hwptr_done = subs->hwptr_done; - substream->runtime->delay = snd_usb_pcm_delay(subs, + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + substream->runtime->delay = snd_usb_pcm_delay(subs, substream->runtime->rate); spin_unlock(&subs->lock); return hwptr_done / (substream->runtime->frame_bits >> 3); @@ -173,11 +177,8 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface, { struct usb_device *dev = chip->dev; unsigned char data[1]; - unsigned int ep; int err; - ep = get_endpoint(alts, 0)->bEndpointAddress; - data[0] = 1; if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR, USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT, @@ -214,7 +215,7 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface, } } -static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) +static int start_endpoints(struct snd_usb_substream *subs, bool can_sleep) { int err; @@ -266,16 +267,18 @@ static int start_endpoints(struct snd_usb_substream *subs, int can_sleep) return 0; } -static void stop_endpoints(struct snd_usb_substream *subs, - int force, int can_sleep, int wait) +static void stop_endpoints(struct snd_usb_substream *subs, bool wait) { if (test_and_clear_bit(SUBSTREAM_FLAG_SYNC_EP_STARTED, &subs->flags)) - snd_usb_endpoint_stop(subs->sync_endpoint, - force, can_sleep, wait); + snd_usb_endpoint_stop(subs->sync_endpoint); if (test_and_clear_bit(SUBSTREAM_FLAG_DATA_EP_STARTED, &subs->flags)) - snd_usb_endpoint_stop(subs->data_endpoint, - force, can_sleep, wait); + snd_usb_endpoint_stop(subs->data_endpoint); + + if (wait) { + snd_usb_endpoint_sync_pending_stop(subs->sync_endpoint); + snd_usb_endpoint_sync_pending_stop(subs->data_endpoint); + } } static int deactivate_endpoints(struct snd_usb_substream *subs) @@ -359,6 +362,19 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; switch (subs->stream->chip->usb_id) { + case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ + if (is_playback) { + implicit_fb = 1; + ep = 0x81; + iface = usb_ifnum_to_if(dev, 3); + + if (!iface || iface->num_altsetting == 0) + return -EINVAL; + + alts = &iface->altsetting[1]; + goto add_sync_ep; + } + break; case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */ case USB_ID(0x0763, 0x2081): if (is_playback) { @@ -381,7 +397,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt) /* ... and check descriptor size before accessing bSynchAddress because there is a version of the SB Audigy 2 NX firmware lacking the audio fields in the endpoint descriptors */ - if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != 0x01 || + if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC || (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE && get_endpoint(alts, 1)->bSynchAddress != 0 && !implicit_fb)) { @@ -438,6 +454,103 @@ add_sync_ep: } /* + * Return the score of matching two audioformats. + * Veto the audioformat if: + * - It has no channels for some reason. + * - Requested PCM format is not supported. + * - Requested sample rate is not supported. + */ +static int match_endpoint_audioformats(struct audioformat *fp, + struct audioformat *match, int rate, + snd_pcm_format_t pcm_format) +{ + int i; + int score = 0; + + if (fp->channels < 1) { + snd_printdd("%s: (fmt @%p) no channels\n", __func__, fp); + return 0; + } + + if (!(fp->formats & (1ULL << pcm_format))) { + snd_printdd("%s: (fmt @%p) no match for format %d\n", __func__, + fp, pcm_format); + return 0; + } + + for (i = 0; i < fp->nr_rates; i++) { + if (fp->rate_table[i] == rate) { + score++; + break; + } + } + if (!score) { + snd_printdd("%s: (fmt @%p) no match for rate %d\n", __func__, + fp, rate); + return 0; + } + + if (fp->channels == match->channels) + score++; + + snd_printdd("%s: (fmt @%p) score %d\n", __func__, fp, score); + + return score; +} + +/* + * Configure the sync ep using the rate and pcm format of the data ep. + */ +static int configure_sync_endpoint(struct snd_usb_substream *subs) +{ + int ret; + struct audioformat *fp; + struct audioformat *sync_fp = NULL; + int cur_score = 0; + int sync_period_bytes = subs->period_bytes; + struct snd_usb_substream *sync_subs = + &subs->stream->substream[subs->direction ^ 1]; + + /* Try to find the best matching audioformat. */ + list_for_each_entry(fp, &sync_subs->fmt_list, list) { + int score = match_endpoint_audioformats(fp, subs->cur_audiofmt, + subs->cur_rate, subs->pcm_format); + + if (score > cur_score) { + sync_fp = fp; + cur_score = score; + } + } + + if (unlikely(sync_fp == NULL)) { + snd_printk(KERN_ERR "%s: no valid audioformat for sync ep %x found\n", + __func__, sync_subs->ep_num); + return -EINVAL; + } + + /* + * Recalculate the period bytes if channel number differ between + * data and sync ep audioformat. + */ + if (sync_fp->channels != subs->channels) { + sync_period_bytes = (subs->period_bytes / subs->channels) * + sync_fp->channels; + snd_printdd("%s: adjusted sync ep period bytes (%d -> %d)\n", + __func__, subs->period_bytes, sync_period_bytes); + } + + ret = snd_usb_endpoint_set_params(subs->sync_endpoint, + subs->pcm_format, + sync_fp->channels, + sync_period_bytes, + subs->cur_rate, + sync_fp, + NULL); + + return ret; +} + +/* * configure endpoint params * * called during initial setup and upon resume @@ -447,7 +560,7 @@ static int configure_endpoint(struct snd_usb_substream *subs) int ret; /* format changed */ - stop_endpoints(subs, 0, 0, 0); + stop_endpoints(subs, true); ret = snd_usb_endpoint_set_params(subs->data_endpoint, subs->pcm_format, subs->channels, @@ -459,13 +572,8 @@ static int configure_endpoint(struct snd_usb_substream *subs) return ret; if (subs->sync_endpoint) - ret = snd_usb_endpoint_set_params(subs->sync_endpoint, - subs->pcm_format, - subs->channels, - subs->period_bytes, - subs->cur_rate, - subs->cur_audiofmt, - NULL); + ret = configure_sync_endpoint(subs); + return ret; } @@ -533,7 +641,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream) subs->period_bytes = 0; down_read(&subs->stream->chip->shutdown_rwsem); if (!subs->stream->chip->shutdown) { - stop_endpoints(subs, 0, 1, 1); + stop_endpoints(subs, true); deactivate_endpoints(subs); } up_read(&subs->stream->chip->shutdown_rwsem); @@ -608,7 +716,7 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream) /* for playback, submit the URBs now; otherwise, the first hwptr_done * updates for all URBs would happen at the same time when starting */ if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) - ret = start_endpoints(subs, 1); + ret = start_endpoints(subs, true); unlock: up_read(&subs->stream->chip->shutdown_rwsem); @@ -1013,7 +1121,7 @@ static int snd_usb_pcm_close(struct snd_pcm_substream *substream, int direction) struct snd_usb_stream *as = snd_pcm_substream_chip(substream); struct snd_usb_substream *subs = &as->substream[direction]; - stop_endpoints(subs, 0, 0, 0); + stop_endpoints(subs, true); if (!as->chip->shutdown && subs->interface >= 0) { usb_set_interface(subs->dev, subs->interface, 0); @@ -1195,6 +1303,9 @@ static void retire_playback_urb(struct snd_usb_substream *subs, return; spin_lock_irqsave(&subs->lock, flags); + if (!subs->last_delay) + goto out; /* short path */ + est_delay = snd_usb_pcm_delay(subs, runtime->rate); /* update delay with exact number of samples played */ if (processed > subs->last_delay) @@ -1212,6 +1323,15 @@ static void retire_playback_urb(struct snd_usb_substream *subs, snd_printk(KERN_DEBUG "delay: estimated %d, actual %d\n", est_delay, subs->last_delay); + if (!subs->running) { + /* update last_frame_number for delay counting here since + * prepare_playback_urb won't be called during pause + */ + subs->last_frame_number = + usb_get_current_frame_number(subs->dev) & 0xff; + } + + out: spin_unlock_irqrestore(&subs->lock, flags); } @@ -1248,12 +1368,13 @@ static int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substrea subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs, 0, 0, 0); + stop_endpoints(subs, false); subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: subs->data_endpoint->prepare_data_urb = NULL; - subs->data_endpoint->retire_data_urb = NULL; + /* keep retire_data_urb for delay calculation */ + subs->data_endpoint->retire_data_urb = retire_playback_urb; subs->running = 0; return 0; } @@ -1269,7 +1390,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream switch (cmd) { case SNDRV_PCM_TRIGGER_START: - err = start_endpoints(subs, 0); + err = start_endpoints(subs, false); if (err < 0) return err; @@ -1277,7 +1398,7 @@ static int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream subs->running = 1; return 0; case SNDRV_PCM_TRIGGER_STOP: - stop_endpoints(subs, 0, 0, 0); + stop_endpoints(subs, false); subs->running = 0; return 0; case SNDRV_PCM_TRIGGER_PAUSE_PUSH: diff --git a/sound/usb/quirks-table.h b/sound/usb/quirks-table.h index 88d8cebbb244..49f9af995d7a 100644 --- a/sound/usb/quirks-table.h +++ b/sound/usb/quirks-table.h @@ -1457,6 +1457,40 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + /* Advanced mode of the Roland VG-99, with MIDI and 24-bit PCM at 44.1 + * kHz. In standard mode, the device has ID 0582:00b3, and offers + * 16-bit PCM at 44.1 kHz with no MIDI. + */ + USB_DEVICE(0x0582, 0x00b2), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Roland", + .product_name = "VG-99", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = (const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_MIDI_FIXED_ENDPOINT, + .data = & (const struct snd_usb_midi_endpoint_info) { + .out_cables = 0x0003, + .in_cables = 0x0003 + } + }, + { + .ifnum = -1 + } + } + } +}, +{ /* Roland SonicCell */ USB_DEVICE(0x0582, 0x00c2), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { @@ -2163,6 +2197,77 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, { + USB_DEVICE_VENDOR_SPEC(0x0763, 0x2030), + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + /* .vendor_name = "M-Audio", */ + /* .product_name = "Fast Track C400", */ + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_MIXER, + }, + /* Playback */ + { + .ifnum = 2, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 6, + .iface = 2, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = 0x09, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x81, + } + }, + /* Capture */ + { + .ifnum = 3, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 4, + .iface = 3, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x81, + .ep_attr = 0x05, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_96000, + .rate_min = 44100, + .rate_max = 96000, + .nr_rates = 4, + .rate_table = (unsigned int[]) { + 44100, 48000, 88200, 96000 + }, + .clock = 0x81, + } + }, + /* MIDI */ + { + .ifnum = -1 /* Interface = 4 */ + } + } + } +}, +{ USB_DEVICE_VENDOR_SPEC(0x0763, 0x2080), .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { /* .vendor_name = "M-Audio", */ @@ -2880,6 +2985,99 @@ YAMAHA_DEVICE(0x7010, "UB99"), } }, +/* Reloop Play */ +{ + USB_DEVICE(0x200c, 0x100b), + .bInterfaceClass = USB_CLASS_PER_INTERFACE, + .driver_info = (unsigned long) &(const struct snd_usb_audio_quirk) { + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = &(const struct snd_usb_audio_quirk[]) { + { + .ifnum = 0, + .type = QUIRK_AUDIO_STANDARD_MIXER, + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_FIXED_ENDPOINT, + .data = &(const struct audioformat) { + .formats = SNDRV_PCM_FMTBIT_S24_3LE, + .channels = 4, + .iface = 1, + .altsetting = 1, + .altset_idx = 1, + .attributes = UAC_EP_CS_ATTR_SAMPLE_RATE, + .endpoint = 0x01, + .ep_attr = USB_ENDPOINT_SYNC_ADAPTIVE, + .rates = SNDRV_PCM_RATE_44100 | + SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .nr_rates = 2, + .rate_table = (unsigned int[]) { + 44100, 48000 + } + } + }, + { + .ifnum = -1 + } + } + } +}, + +{ + /* + * Focusrite Scarlett 18i6 + * + * Avoid mixer creation, which otherwise fails because some of + * the interface descriptor subtypes for interface 0 are + * unknown. That should be fixed or worked-around but this at + * least allows the device to be used successfully with a DAW + * and an external mixer. See comments below about other + * ignored interfaces. + */ + USB_DEVICE(0x1235, 0x8004), + .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) { + .vendor_name = "Focusrite", + .product_name = "Scarlett 18i6", + .ifnum = QUIRK_ANY_INTERFACE, + .type = QUIRK_COMPOSITE, + .data = & (const struct snd_usb_audio_quirk[]) { + { + /* InterfaceSubClass 1 (Control Device) */ + .ifnum = 0, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 1, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + .ifnum = 2, + .type = QUIRK_AUDIO_STANDARD_INTERFACE + }, + { + /* InterfaceSubClass 1 (Control Device) */ + .ifnum = 3, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = 4, + .type = QUIRK_MIDI_STANDARD_INTERFACE + }, + { + /* InterfaceSubClass 1 (Device Firmware Update) */ + .ifnum = 5, + .type = QUIRK_IGNORE_INTERFACE + }, + { + .ifnum = -1 + } + } + } +}, + { /* * Some USB MIDI devices don't have an audio control interface, diff --git a/sound/usb/stream.c b/sound/usb/stream.c index 1de0c8c002a8..ad181d538bd9 100644 --- a/sound/usb/stream.c +++ b/sound/usb/stream.c @@ -23,6 +23,8 @@ #include <sound/core.h> #include <sound/pcm.h> +#include <sound/control.h> +#include <sound/tlv.h> #include "usbaudio.h" #include "card.h" @@ -47,6 +49,7 @@ static void free_substream(struct snd_usb_substream *subs) list_for_each_safe(p, n, &subs->fmt_list) { struct audioformat *fp = list_entry(p, struct audioformat, list); kfree(fp->rate_table); + kfree(fp->chmap); kfree(fp); } kfree(subs->rate_list.list); @@ -99,6 +102,206 @@ static void snd_usb_init_substream(struct snd_usb_stream *as, subs->num_formats++; subs->fmt_type = fp->fmt_type; subs->ep_num = fp->endpoint; + if (fp->channels > subs->channels_max) + subs->channels_max = fp->channels; +} + +/* kctl callbacks for usb-audio channel maps */ +static int usb_chmap_ctl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct snd_usb_substream *subs = info->private_data; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = subs->channels_max; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = SNDRV_CHMAP_LAST; + return 0; +} + +/* check whether a duplicated entry exists in the audiofmt list */ +static bool have_dup_chmap(struct snd_usb_substream *subs, + struct audioformat *fp) +{ + struct list_head *p; + + for (p = fp->list.prev; p != &subs->fmt_list; p = p->prev) { + struct audioformat *prev; + prev = list_entry(p, struct audioformat, list); + if (prev->chmap && + !memcmp(prev->chmap, fp->chmap, sizeof(*fp->chmap))) + return true; + } + return false; +} + +static int usb_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag, + unsigned int size, unsigned int __user *tlv) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct snd_usb_substream *subs = info->private_data; + struct audioformat *fp; + unsigned int __user *dst; + int count = 0; + + if (size < 8) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv)) + return -EFAULT; + size -= 8; + dst = tlv + 2; + list_for_each_entry(fp, &subs->fmt_list, list) { + int i, ch_bytes; + + if (!fp->chmap) + continue; + if (have_dup_chmap(subs, fp)) + continue; + /* copy the entry */ + ch_bytes = fp->chmap->channels * 4; + if (size < 8 + ch_bytes) + return -ENOMEM; + if (put_user(SNDRV_CTL_TLVT_CHMAP_FIXED, dst) || + put_user(ch_bytes, dst + 1)) + return -EFAULT; + dst += 2; + for (i = 0; i < fp->chmap->channels; i++, dst++) { + if (put_user(fp->chmap->map[i], dst)) + return -EFAULT; + } + + count += 8 + ch_bytes; + size -= 8 + ch_bytes; + } + if (put_user(count, tlv + 1)) + return -EFAULT; + return 0; +} + +static int usb_chmap_ctl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol); + struct snd_usb_substream *subs = info->private_data; + struct snd_pcm_chmap_elem *chmap = NULL; + int i; + + memset(ucontrol->value.integer.value, 0, + sizeof(ucontrol->value.integer.value)); + if (subs->cur_audiofmt) + chmap = subs->cur_audiofmt->chmap; + if (chmap) { + for (i = 0; i < chmap->channels; i++) + ucontrol->value.integer.value[i] = chmap->map[i]; + } + return 0; +} + +/* create a chmap kctl assigned to the given USB substream */ +static int add_chmap(struct snd_pcm *pcm, int stream, + struct snd_usb_substream *subs) +{ + struct audioformat *fp; + struct snd_pcm_chmap *chmap; + struct snd_kcontrol *kctl; + int err; + + list_for_each_entry(fp, &subs->fmt_list, list) + if (fp->chmap) + goto ok; + /* no chmap is found */ + return 0; + + ok: + err = snd_pcm_add_chmap_ctls(pcm, stream, NULL, 0, 0, &chmap); + if (err < 0) + return err; + + /* override handlers */ + chmap->private_data = subs; + kctl = chmap->kctl; + kctl->info = usb_chmap_ctl_info; + kctl->get = usb_chmap_ctl_get; + kctl->tlv.c = usb_chmap_ctl_tlv; + + return 0; +} + +/* convert from USB ChannelConfig bits to ALSA chmap element */ +static struct snd_pcm_chmap_elem *convert_chmap(int channels, unsigned int bits, + int protocol) +{ + static unsigned int uac1_maps[] = { + SNDRV_CHMAP_FL, /* left front */ + SNDRV_CHMAP_FR, /* right front */ + SNDRV_CHMAP_FC, /* center front */ + SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_SL, /* left surround */ + SNDRV_CHMAP_SR, /* right surround */ + SNDRV_CHMAP_FLC, /* left of center */ + SNDRV_CHMAP_FRC, /* right of center */ + SNDRV_CHMAP_RC, /* surround */ + SNDRV_CHMAP_SL, /* side left */ + SNDRV_CHMAP_SR, /* side right */ + SNDRV_CHMAP_TC, /* top */ + 0 /* terminator */ + }; + static unsigned int uac2_maps[] = { + SNDRV_CHMAP_FL, /* front left */ + SNDRV_CHMAP_FR, /* front right */ + SNDRV_CHMAP_FC, /* front center */ + SNDRV_CHMAP_LFE, /* LFE */ + SNDRV_CHMAP_RL, /* back left */ + SNDRV_CHMAP_RR, /* back right */ + SNDRV_CHMAP_FLC, /* front left of center */ + SNDRV_CHMAP_FRC, /* front right of center */ + SNDRV_CHMAP_RC, /* back center */ + SNDRV_CHMAP_SL, /* side left */ + SNDRV_CHMAP_SR, /* side right */ + SNDRV_CHMAP_TC, /* top center */ + SNDRV_CHMAP_TFL, /* top front left */ + SNDRV_CHMAP_TFC, /* top front center */ + SNDRV_CHMAP_TFR, /* top front right */ + SNDRV_CHMAP_TRL, /* top back left */ + SNDRV_CHMAP_TRC, /* top back center */ + SNDRV_CHMAP_TRR, /* top back right */ + SNDRV_CHMAP_TFLC, /* top front left of center */ + SNDRV_CHMAP_TFRC, /* top front right of center */ + SNDRV_CHMAP_LLFE, /* left LFE */ + SNDRV_CHMAP_RLFE, /* right LFE */ + SNDRV_CHMAP_TSL, /* top side left */ + SNDRV_CHMAP_TSR, /* top side right */ + SNDRV_CHMAP_BC, /* bottom center */ + SNDRV_CHMAP_BLC, /* bottom left center */ + SNDRV_CHMAP_BRC, /* bottom right center */ + 0 /* terminator */ + }; + struct snd_pcm_chmap_elem *chmap; + const unsigned int *maps; + int c; + + if (!bits) + return NULL; + if (channels > ARRAY_SIZE(chmap->map)) + return NULL; + + chmap = kzalloc(sizeof(*chmap), GFP_KERNEL); + if (!chmap) + return NULL; + + maps = protocol == UAC_VERSION_2 ? uac2_maps : uac1_maps; + chmap->channels = channels; + c = 0; + for (; bits && *maps; maps++, bits >>= 1) { + if (bits & 1) + chmap->map[c++] = *maps; + } + + for (; c < channels; c++) + chmap->map[c] = SNDRV_CHMAP_UNKNOWN; + + return chmap; } /* @@ -140,7 +343,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, if (err < 0) return err; snd_usb_init_substream(as, stream, fp); - return 0; + return add_chmap(as->pcm, stream, subs); } /* create a new pcm */ @@ -174,7 +377,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip, snd_usb_proc_pcm_format_add(as); - return 0; + return add_chmap(pcm, stream, &as->substream[stream]); } static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, @@ -218,8 +421,11 @@ static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip, return attributes; } -static struct uac2_input_terminal_descriptor * - snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, +/* find an input terminal descriptor (either UAC1 or UAC2) with the given + * terminal id + */ +static void * +snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface, int terminal_id) { struct uac2_input_terminal_descriptor *term = NULL; @@ -261,6 +467,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) struct audioformat *fp = NULL; int num, protocol, clock = 0; struct uac_format_type_i_continuous_descriptor *fmt; + unsigned int chconfig; dev = chip->dev; @@ -300,6 +507,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) if (snd_usb_apply_interface_quirk(chip, iface_no, altno)) continue; + chconfig = 0; /* get audio formats */ switch (protocol) { default: @@ -311,6 +519,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) case UAC_VERSION_1: { struct uac1_as_header_descriptor *as = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL); + struct uac_input_terminal_descriptor *iterm; if (!as) { snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n", @@ -325,6 +534,14 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) } format = le16_to_cpu(as->wFormatTag); /* remember the format value */ + + iterm = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf, + as->bTerminalLink); + if (iterm) { + num_channels = iterm->bNrChannels; + chconfig = le16_to_cpu(iterm->wChannelConfig); + } + break; } @@ -355,6 +572,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) as->bTerminalLink); if (input_term) { clock = input_term->bCSourceID; + chconfig = le32_to_cpu(input_term->bmChannelConfig); break; } @@ -413,13 +631,13 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) fp->ep_attr = get_endpoint(alts, 0)->bmAttributes; fp->datainterval = snd_usb_parse_datainterval(chip, alts); fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize); - /* num_channels is only set for v2 interfaces */ fp->channels = num_channels; if (snd_usb_get_speed(dev) == USB_SPEED_HIGH) fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1) * (fp->maxpacksize & 0x7ff); fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no); fp->clock = clock; + fp->chmap = convert_chmap(num_channels, chconfig, protocol); /* some quirks for attributes here */ @@ -455,6 +673,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) /* ok, let's parse further... */ if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) { kfree(fp->rate_table); + kfree(fp->chmap); kfree(fp); fp = NULL; continue; @@ -464,6 +683,7 @@ int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no) err = snd_usb_add_audio_stream(chip, stream, fp); if (err < 0) { kfree(fp->rate_table); + kfree(fp->chmap); kfree(fp); return err; } diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h index ef42797f56fb..1ac3fd9cc5a6 100644 --- a/sound/usb/usbaudio.h +++ b/sound/usb/usbaudio.h @@ -56,7 +56,6 @@ struct snd_usb_audio { int setup; /* from the 'device_setup' module param */ int nrpacks; /* from the 'nrpacks' module param */ - int async_unlink; /* from the 'async_unlink' module param */ struct usb_host_interface *ctrl_intf; /* the audio control interface */ }; |