diff options
author | Takashi Iwai <tiwai@suse.de> | 2015-01-28 07:24:41 +0100 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2015-01-28 07:24:41 +0100 |
commit | 5e0ddd07fa8fcbb84faac666d36ff9c37449a849 (patch) | |
tree | 29ff039af456f0aed5280943bc9e1593893b5b21 | |
parent | ALSA: seq: increase the maximum number of queues (diff) | |
parent | ALSA: line6: Handle error from line6_pcm_acquire() (diff) | |
download | linux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.tar.xz linux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.zip |
Merge branch 'topic/line6' into for-next
-rw-r--r-- | sound/usb/line6/Kconfig | 2 | ||||
-rw-r--r-- | sound/usb/line6/capture.c | 223 | ||||
-rw-r--r-- | sound/usb/line6/capture.h | 6 | ||||
-rw-r--r-- | sound/usb/line6/driver.c | 202 | ||||
-rw-r--r-- | sound/usb/line6/driver.h | 10 | ||||
-rw-r--r-- | sound/usb/line6/midi.c | 18 | ||||
-rw-r--r-- | sound/usb/line6/midi.h | 7 | ||||
-rw-r--r-- | sound/usb/line6/pcm.c | 443 | ||||
-rw-r--r-- | sound/usb/line6/pcm.h | 304 | ||||
-rw-r--r-- | sound/usb/line6/playback.c | 284 | ||||
-rw-r--r-- | sound/usb/line6/playback.h | 6 | ||||
-rw-r--r-- | sound/usb/line6/pod.c | 45 | ||||
-rw-r--r-- | sound/usb/line6/podhd.c | 17 | ||||
-rw-r--r-- | sound/usb/line6/toneport.c | 216 | ||||
-rw-r--r-- | sound/usb/line6/variax.c | 27 |
15 files changed, 705 insertions, 1105 deletions
diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig index af20947e0bda..f4585d378ef3 100644 --- a/sound/usb/line6/Kconfig +++ b/sound/usb/line6/Kconfig @@ -29,6 +29,8 @@ config SND_USB_PODHD config SND_USB_TONEPORT tristate "TonePort GX, UX1 and UX2 USB support" select SND_USB_LINE6 + select NEW_LEDS + select LEDS_CLASS help This is a driver for TonePort GX, UX1 and UX2 devices. diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c index 5a010ba163fa..4183c5f5edc2 100644 --- a/sound/usb/line6/capture.c +++ b/sound/usb/line6/capture.c @@ -20,26 +20,24 @@ /* Find a free URB and submit it. + must be called in line6pcm->in.lock context */ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) { int index; - unsigned long flags; int i, urb_size; int ret; struct urb *urb_in; - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); index = - find_first_zero_bit(&line6pcm->active_urb_in, LINE6_ISO_BUFFERS); + find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS); if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } - urb_in = line6pcm->urb_audio_in[index]; + urb_in = line6pcm->in.urbs[index]; urb_size = 0; for (i = 0; i < LINE6_ISO_PACKETS; ++i) { @@ -51,7 +49,7 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) } urb_in->transfer_buffer = - line6pcm->buffer_in + + line6pcm->in.buffer + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_in->transfer_buffer_length = urb_size; urb_in->context = line6pcm; @@ -59,81 +57,29 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) ret = usb_submit_urb(urb_in, GFP_ATOMIC); if (ret == 0) - set_bit(index, &line6pcm->active_urb_in); + set_bit(index, &line6pcm->in.active_urbs); else dev_err(line6pcm->line6->ifcdev, "URB in #%d submission failed (%d)\n", index, ret); - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); return 0; } /* Submit all currently available capture URBs. + must be called in line6pcm->in.lock context */ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) { - int ret, i; + int ret = 0, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { ret = submit_audio_in_urb(line6pcm); if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active capture URBs. -*/ -void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_in)) { - struct urb *u = line6pcm->urb_audio_in[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active capture URBs has been - finished. -*/ -void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_in)) - alive++; - } - if (!alive) break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} + } -/* - Unlink all currently active capture URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_in_urbs(line6pcm); - line6_wait_clear_audio_in_urbs(line6pcm); + return ret; } /* @@ -150,18 +96,18 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) if (runtime == NULL) return; - if (line6pcm->pos_in_done + frames > runtime->buffer_size) { + if (line6pcm->in.pos_done + frames > runtime->buffer_size) { /* The transferred area goes over buffer boundary, copy two separate chunks. */ int len; - len = runtime->buffer_size - line6pcm->pos_in_done; + len = runtime->buffer_size - line6pcm->in.pos_done; if (len > 0) { memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, + line6pcm->in.pos_done * bytes_per_frame, fbuf, len * bytes_per_frame); memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, (frames - len) * bytes_per_frame); @@ -173,12 +119,12 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) } else { /* copy single chunk */ memcpy(runtime->dma_area + - line6pcm->pos_in_done * bytes_per_frame, fbuf, fsize); + line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize); } - line6pcm->pos_in_done += frames; - if (line6pcm->pos_in_done >= runtime->buffer_size) - line6pcm->pos_in_done -= runtime->buffer_size; + line6pcm->in.pos_done += frames; + if (line6pcm->in.pos_done >= runtime->buffer_size) + line6pcm->in.pos_done -= runtime->buffer_size; } void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) @@ -186,19 +132,15 @@ void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) struct snd_pcm_substream *substream = get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); - line6pcm->bytes_in += length; - if (line6pcm->bytes_in >= line6pcm->period_in) { - line6pcm->bytes_in %= line6pcm->period_in; + line6pcm->in.bytes += length; + if (line6pcm->in.bytes >= line6pcm->in.period) { + line6pcm->in.bytes %= line6pcm->in.period; + spin_unlock(&line6pcm->in.lock); snd_pcm_period_elapsed(substream); + spin_lock(&line6pcm->in.lock); } } -void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_in); - line6pcm->buffer_in = NULL; -} - /* * Callback for completed capture URB. */ @@ -209,14 +151,14 @@ static void audio_in_callback(struct urb *urb) struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; - line6pcm->last_frame_in = urb->start_frame; + line6pcm->in.last_frame = urb->start_frame; /* find index of URB */ for (index = 0; index < LINE6_ISO_BUFFERS; ++index) - if (urb == line6pcm->urb_audio_in[index]) + if (urb == line6pcm->in.urbs[index]) break; - spin_lock_irqsave(&line6pcm->lock_audio_in, flags); + spin_lock_irqsave(&line6pcm->in.lock, flags); for (i = 0; i < LINE6_ISO_PACKETS; ++i) { char *fbuf; @@ -243,27 +185,26 @@ static void audio_in_callback(struct urb *urb) line6pcm->prev_fbuf = fbuf; line6pcm->prev_fsize = fsize; - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags) && (fsize > 0)) - line6_capture_copy(line6pcm, fbuf, fsize); + if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) && + test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) && + fsize > 0) + line6_capture_copy(line6pcm, fbuf, fsize); } - clear_bit(index, &line6pcm->active_urb_in); + clear_bit(index, &line6pcm->in.active_urbs); - if (test_and_clear_bit(index, &line6pcm->unlink_urb_in)) + if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs)) shutdown = 1; - spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags); - if (!shutdown) { submit_audio_in_urb(line6pcm); - if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) - if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - &line6pcm->flags)) - line6_capture_check_period(line6pcm, length); + if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) && + test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) + line6_capture_check_period(line6pcm, length); } + + spin_unlock_irqrestore(&line6pcm->in.lock, flags); } /* open capture callback */ @@ -290,102 +231,16 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream) return 0; } -/* hw_params capture callback */ -static int snd_line6_capture_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return ret; - } - - line6pcm->period_in = params_period_bytes(hw_params); - return 0; -} - -/* hw_free capture callback */ -static int snd_line6_capture_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger callback */ -int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM); - - if (err < 0) - return err; - - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* capture pointer callback */ -static snd_pcm_uframes_t -snd_line6_capture_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_in_done; -} - /* capture operators */ struct snd_pcm_ops snd_line6_capture_ops = { .open = snd_line6_capture_open, .close = snd_line6_capture_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_capture_hw_params, - .hw_free = snd_line6_capture_hw_free, + .hw_params = snd_line6_hw_params, + .hw_free = snd_line6_hw_free, .prepare = snd_line6_prepare, .trigger = snd_line6_trigger, - .pointer = snd_line6_capture_pointer, + .pointer = snd_line6_pointer, }; int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) @@ -398,7 +253,7 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) struct urb *urb; /* URB for audio in: */ - urb = line6pcm->urb_audio_in[i] = + urb = line6pcm->in.urbs[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); if (urb == NULL) diff --git a/sound/usb/line6/capture.h b/sound/usb/line6/capture.h index 0939f400a405..890b21bff18c 100644 --- a/sound/usb/line6/capture.h +++ b/sound/usb/line6/capture.h @@ -24,12 +24,6 @@ extern void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length); extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_capture_buffer(struct snd_line6_pcm *line6pcm); extern int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_in_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_in_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_capture_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c index 93cd4daa56bc..a0436993a167 100644 --- a/sound/usb/line6/driver.c +++ b/sound/usb/line6/driver.c @@ -413,26 +413,12 @@ int line6_read_serial_number(struct usb_line6 *line6, int *serial_number) EXPORT_SYMBOL_GPL(line6_read_serial_number); /* - No operation (i.e., unsupported). -*/ -ssize_t line6_nop_read(struct device *dev, struct device_attribute *attr, - char *buf) -{ - return 0; -} -EXPORT_SYMBOL_GPL(line6_nop_read); - -/* Card destructor. */ static void line6_destruct(struct snd_card *card) { struct usb_line6 *line6 = card->private_data; - struct usb_device *usbdev; - - if (!line6) - return; - usbdev = line6->usbdev; + struct usb_device *usbdev = line6->usbdev; /* free buffer memory first: */ kfree(line6->buffer_message); @@ -441,82 +427,96 @@ static void line6_destruct(struct snd_card *card) /* then free URBs: */ usb_free_urb(line6->urb_listen); - /* free interface data: */ - kfree(line6); - /* decrement reference counters: */ usb_put_dev(usbdev); } +/* get data from endpoint descriptor (see usb_maxpacket): */ +static void line6_get_interval(struct usb_line6 *line6) +{ + struct usb_device *usbdev = line6->usbdev; + struct usb_host_endpoint *ep; + unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r); + unsigned epnum = usb_pipeendpoint(pipe); + + ep = usbdev->ep_in[epnum]; + if (ep) { + line6->interval = ep->desc.bInterval; + line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + } else { + dev_err(line6->ifcdev, + "endpoint not available, using fallback values"); + line6->interval = LINE6_FALLBACK_INTERVAL; + line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; + } +} + +static int line6_init_cap_control(struct usb_line6 *line6) +{ + int ret; + + /* initialize USB buffers: */ + line6->buffer_listen = kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); + if (!line6->buffer_listen) + return -ENOMEM; + + line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); + if (!line6->buffer_message) + return -ENOMEM; + + line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); + if (!line6->urb_listen) + return -ENOMEM; + + ret = line6_start_listen(line6); + if (ret < 0) { + dev_err(line6->ifcdev, "cannot start listening: %d\n", ret); + return ret; + } + + return 0; +} + /* Probe USB device. */ int line6_probe(struct usb_interface *interface, - struct usb_line6 *line6, + const struct usb_device_id *id, const struct line6_properties *properties, - int (*private_init)(struct usb_interface *, struct usb_line6 *)) + int (*private_init)(struct usb_line6 *, const struct usb_device_id *id), + size_t data_size) { struct usb_device *usbdev = interface_to_usbdev(interface); struct snd_card *card; + struct usb_line6 *line6; int interface_number; int ret; - /* we don't handle multiple configurations */ - if (usbdev->descriptor.bNumConfigurations != 1) { - ret = -ENODEV; - goto err_put; - } - - /* initialize device info: */ - dev_info(&interface->dev, "Line 6 %s found\n", properties->name); + if (WARN_ON(data_size < sizeof(*line6))) + return -EINVAL; - /* query interface number */ - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; + /* we don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return -ENODEV; - ret = usb_set_interface(usbdev, interface_number, - properties->altsetting); - if (ret < 0) { - dev_err(&interface->dev, "set_interface failed\n"); - goto err_put; - } + ret = snd_card_new(&interface->dev, + SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, data_size, &card); + if (ret < 0) + return ret; /* store basic data: */ + line6 = card->private_data; + line6->card = card; line6->properties = properties; line6->usbdev = usbdev; line6->ifcdev = &interface->dev; - /* get data from endpoint descriptor (see usb_maxpacket): */ - { - struct usb_host_endpoint *ep; - unsigned pipe = usb_rcvintpipe(usbdev, properties->ep_ctrl_r); - unsigned epnum = usb_pipeendpoint(pipe); - ep = usbdev->ep_in[epnum]; - - if (ep != NULL) { - line6->interval = ep->desc.bInterval; - line6->max_packet_size = - le16_to_cpu(ep->desc.wMaxPacketSize); - } else { - line6->interval = LINE6_FALLBACK_INTERVAL; - line6->max_packet_size = LINE6_FALLBACK_MAXPACKETSIZE; - dev_err(line6->ifcdev, - "endpoint not available, using fallback values"); - } - } - - ret = snd_card_new(line6->ifcdev, - SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, - THIS_MODULE, 0, &card); - if (ret < 0) - goto err_put; - - line6->card = card; - strcpy(card->id, line6->properties->id); + strcpy(card->id, properties->id); strcpy(card->driver, DRIVER_NAME); - strcpy(card->shortname, line6->properties->name); - sprintf(card->longname, "Line 6 %s at USB %s", line6->properties->name, + strcpy(card->shortname, properties->name); + sprintf(card->longname, "Line 6 %s at USB %s", properties->name, dev_name(line6->ifcdev)); - card->private_data = line6; card->private_free = line6_destruct; usb_set_intfdata(interface, line6); @@ -524,52 +524,43 @@ int line6_probe(struct usb_interface *interface, /* increment reference counters: */ usb_get_dev(usbdev); - if (properties->capabilities & LINE6_CAP_CONTROL) { - /* initialize USB buffers: */ - line6->buffer_listen = - kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); - if (line6->buffer_listen == NULL) { - ret = -ENOMEM; - goto err_destruct; - } + /* initialize device info: */ + dev_info(&interface->dev, "Line 6 %s found\n", properties->name); - line6->buffer_message = - kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL); - if (line6->buffer_message == NULL) { - ret = -ENOMEM; - goto err_destruct; - } + /* query interface number */ + interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL); + ret = usb_set_interface(usbdev, interface_number, + properties->altsetting); + if (ret < 0) { + dev_err(&interface->dev, "set_interface failed\n"); + goto error; + } - if (line6->urb_listen == NULL) { - ret = -ENOMEM; - goto err_destruct; - } + line6_get_interval(line6); - ret = line6_start_listen(line6); - if (ret < 0) { - dev_err(&interface->dev, "%s: usb_submit_urb failed\n", - __func__); - goto err_destruct; - } + if (properties->capabilities & LINE6_CAP_CONTROL) { + ret = line6_init_cap_control(line6); + if (ret < 0) + goto error; } /* initialize device data based on device: */ - ret = private_init(interface, line6); + ret = private_init(line6, id); if (ret < 0) - goto err_destruct; + goto error; /* creation of additional special files should go here */ dev_info(&interface->dev, "Line 6 %s now attached\n", - line6->properties->name); + properties->name); return 0; - err_destruct: + error: + if (line6->disconnect) + line6->disconnect(line6); snd_card_free(card); - err_put: return ret; } EXPORT_SYMBOL_GPL(line6_probe); @@ -579,32 +570,23 @@ EXPORT_SYMBOL_GPL(line6_probe); */ void line6_disconnect(struct usb_interface *interface) { - struct usb_line6 *line6; - struct usb_device *usbdev; - int interface_number; + struct usb_line6 *line6 = usb_get_intfdata(interface); + struct usb_device *usbdev = interface_to_usbdev(interface); - if (interface == NULL) - return; - usbdev = interface_to_usbdev(interface); - if (usbdev == NULL) + if (!line6) return; - interface_number = interface->cur_altsetting->desc.bInterfaceNumber; - line6 = usb_get_intfdata(interface); - if (!line6) + if (WARN_ON(usbdev != line6->usbdev)) return; if (line6->urb_listen != NULL) line6_stop_listen(line6); - if (usbdev != line6->usbdev) - dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n"); - snd_card_disconnect(line6->card); if (line6->line6pcm) line6_pcm_disconnect(line6->line6pcm); if (line6->disconnect) - line6->disconnect(interface); + line6->disconnect(line6); dev_info(&interface->dev, "Line 6 %s now disconnected\n", line6->properties->name); diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h index efd58ac3215b..fce10f12f0d3 100644 --- a/sound/usb/line6/driver.h +++ b/sound/usb/line6/driver.h @@ -157,13 +157,11 @@ struct usb_line6 { int message_length; void (*process_message)(struct usb_line6 *); - void (*disconnect)(struct usb_interface *); + void (*disconnect)(struct usb_line6 *line6); }; extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1, int code2, int size); -extern ssize_t line6_nop_read(struct device *dev, - struct device_attribute *attr, char *buf); extern int line6_read_data(struct usb_line6 *line6, int address, void *data, size_t datalen); extern int line6_read_serial_number(struct usb_line6 *line6, @@ -182,9 +180,11 @@ extern int line6_write_data(struct usb_line6 *line6, int address, void *data, size_t datalen); int line6_probe(struct usb_interface *interface, - struct usb_line6 *line6, + const struct usb_device_id *id, const struct line6_properties *properties, - int (*private_init)(struct usb_interface *, struct usb_line6 *)); + int (*private_init)(struct usb_line6 *, const struct usb_device_id *id), + size_t data_size); + void line6_disconnect(struct usb_interface *interface); #ifdef CONFIG_PM diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c index b5a58a7fe11a..beeedf9a2cbe 100644 --- a/sound/usb/line6/midi.c +++ b/sound/usb/line6/midi.c @@ -45,12 +45,9 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream) line6_rawmidi_substream_midi(substream)->line6; struct snd_line6_midi *line6midi = line6->line6midi; struct midi_buffer *mb = &line6midi->midibuf_out; - unsigned long flags; unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; int req, done; - spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags); - for (;;) { req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); done = snd_rawmidi_transmit_peek(substream, chunk, req); @@ -71,8 +68,6 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream) send_midi_async(line6, chunk, done); } - - spin_unlock_irqrestore(&line6->line6midi->midi_transmit_lock, flags); } /* @@ -92,7 +87,7 @@ static void midi_sent(struct urb *urb) if (status == -ESHUTDOWN) return; - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); + spin_lock_irqsave(&line6->line6midi->lock, flags); num = --line6->line6midi->num_active_send_urbs; if (num == 0) { @@ -103,12 +98,12 @@ static void midi_sent(struct urb *urb) if (num == 0) wake_up(&line6->line6midi->send_wait); - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); + spin_unlock_irqrestore(&line6->line6midi->lock, flags); } /* Send an asynchronous MIDI message. - Assumes that line6->line6midi->send_urb_lock is held + Assumes that line6->line6midi->lock is held (i.e., this function is serialized). */ static int send_midi_async(struct usb_line6 *line6, unsigned char *data, @@ -166,12 +161,12 @@ static void line6_midi_output_trigger(struct snd_rawmidi_substream *substream, line6_rawmidi_substream_midi(substream)->line6; line6->line6midi->substream_transmit = substream; - spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); + spin_lock_irqsave(&line6->line6midi->lock, flags); if (line6->line6midi->num_active_send_urbs == 0) line6_midi_transmit(substream); - spin_unlock_irqrestore(&line6->line6midi->send_urb_lock, flags); + spin_unlock_irqrestore(&line6->line6midi->lock, flags); } static void line6_midi_output_drain(struct snd_rawmidi_substream *substream) @@ -281,8 +276,7 @@ int line6_init_midi(struct usb_line6 *line6) rmidi->private_free = snd_line6_midi_free; init_waitqueue_head(&line6midi->send_wait); - spin_lock_init(&line6midi->send_urb_lock); - spin_lock_init(&line6midi->midi_transmit_lock); + spin_lock_init(&line6midi->lock); line6midi->line6 = line6; err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); diff --git a/sound/usb/line6/midi.h b/sound/usb/line6/midi.h index ba6bf3828aa5..9d9467b2613c 100644 --- a/sound/usb/line6/midi.h +++ b/sound/usb/line6/midi.h @@ -40,14 +40,9 @@ struct snd_line6_midi { int num_active_send_urbs; /** - Spin lock to protect updates of send_urb. - */ - spinlock_t send_urb_lock; - - /** Spin lock to protect MIDI buffer handling. */ - spinlock_t midi_transmit_lock; + spinlock_t lock; /** Wait queue for MIDI transmission. diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c index 8a6059adef69..8461d6bf992f 100644 --- a/sound/usb/line6/pcm.c +++ b/sound/usb/line6/pcm.c @@ -45,15 +45,22 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol, { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); int value = ucontrol->value.integer.value[0]; + int err; if (line6pcm->impulse_volume == value) return 0; line6pcm->impulse_volume = value; - if (value > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); + if (value > 0) { + err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE); + if (err < 0) { + line6pcm->impulse_volume = 0; + line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); + return err; + } + } else { + line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE); + } return 1; } @@ -90,180 +97,277 @@ static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol, return 1; } -static bool test_flags(unsigned long flags0, unsigned long flags1, - unsigned long mask) -{ - return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); -} - -int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) +/* + Unlink all currently active URBs. +*/ +static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm, + struct line6_pcm_stream *pcms) { - unsigned long flags_old, flags_new, flags_final; - int err; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old | channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - - flags_final = flags_old; - - line6pcm->prev_fbuf = NULL; - - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_in) { - line6pcm->buffer_in = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_in) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_CAPTURE_BUFFER; - } - } + int i; - if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { - /* - Waiting for completion of active URBs in the stop handler is - a bug, we therefore report an error if capturing is restarted - too soon. - */ - if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; + for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + if (test_bit(i, &pcms->active_urbs)) { + if (!test_and_set_bit(i, &pcms->unlink_urbs)) + usb_unlink_urb(pcms->urbs[i]); } - - line6pcm->count_in = 0; - line6pcm->prev_fsize = 0; - err = line6_submit_audio_in_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; - - flags_final |= channels & LINE6_BITS_CAPTURE_STREAM; } +} - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_BUFFER)) { - /* Invoked multiple times in a row so allocate once only */ - if (!line6pcm->buffer_out) { - line6pcm->buffer_out = - kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * - line6pcm->max_packet_size, GFP_KERNEL); - if (!line6pcm->buffer_out) { - err = -ENOMEM; - goto pcm_acquire_error; - } - - flags_final |= channels & LINE6_BITS_PLAYBACK_BUFFER; - } - } +/* + Wait until unlinking of all currently active URBs has been finished. +*/ +static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm, + struct line6_pcm_stream *pcms) +{ + int timeout = HZ; + int i; + int alive; - if (test_flags(flags_old, flags_new, LINE6_BITS_PLAYBACK_STREAM)) { - /* - See comment above regarding PCM restart. - */ - if (line6pcm->active_urb_out | line6pcm->unlink_urb_out) { - dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n"); - return -EBUSY; + do { + alive = 0; + for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + if (test_bit(i, &pcms->active_urbs)) + alive++; } + if (!alive) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + } while (--timeout > 0); + if (alive) + dev_err(line6pcm->line6->ifcdev, + "timeout: still %d active urbs..\n", alive); +} - line6pcm->count_out = 0; - err = line6_submit_audio_out_all_urbs(line6pcm); - - if (err < 0) - goto pcm_acquire_error; +static inline struct line6_pcm_stream * +get_stream(struct snd_line6_pcm *line6pcm, int direction) +{ + return (direction == SNDRV_PCM_STREAM_PLAYBACK) ? + &line6pcm->out : &line6pcm->in; +} - flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM; +/* allocate a buffer if not opened yet; + * call this in line6pcm.state_change mutex + */ +static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm, + struct line6_pcm_stream *pstr, int type) +{ + /* Invoked multiple times in a row so allocate once only */ + if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) { + pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS * + line6pcm->max_packet_size, GFP_KERNEL); + if (!pstr->buffer) + return -ENOMEM; } - return 0; - -pcm_acquire_error: - /* - If not all requested resources/streams could be obtained, release - those which were successfully obtained (if any). - */ - line6_pcm_release(line6pcm, flags_final & channels); - return err; } -EXPORT_SYMBOL_GPL(line6_pcm_acquire); -int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels) +/* free a buffer if all streams are closed; + * call this in line6pcm.state_change mutex + */ +static void line6_buffer_release(struct snd_line6_pcm *line6pcm, + struct line6_pcm_stream *pstr, int type) { - unsigned long flags_old, flags_new; - - do { - flags_old = ACCESS_ONCE(line6pcm->flags); - flags_new = flags_old & ~channels; - } while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) - line6_unlink_audio_in_urbs(line6pcm); - - if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { - line6_wait_clear_audio_in_urbs(line6pcm); - line6_free_capture_buffer(line6pcm); + clear_bit(type, &pstr->opened); + if (!pstr->opened) { + line6_wait_clear_audio_urbs(line6pcm, pstr); + kfree(pstr->buffer); + pstr->buffer = NULL; } +} - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM)) - line6_unlink_audio_out_urbs(line6pcm); +/* start a PCM stream */ +static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction, + int type) +{ + unsigned long flags; + struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); + int ret = 0; + + spin_lock_irqsave(&pstr->lock, flags); + if (!test_and_set_bit(type, &pstr->running)) { + if (pstr->active_urbs || pstr->unlink_urbs) { + ret = -EBUSY; + goto error; + } - if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) { - line6_wait_clear_audio_out_urbs(line6pcm); - line6_free_playback_buffer(line6pcm); + pstr->count = 0; + /* Submit all currently available URBs */ + if (direction == SNDRV_PCM_STREAM_PLAYBACK) + ret = line6_submit_audio_out_all_urbs(line6pcm); + else + ret = line6_submit_audio_in_all_urbs(line6pcm); } + error: + if (ret < 0) + clear_bit(type, &pstr->running); + spin_unlock_irqrestore(&pstr->lock, flags); + return ret; +} - return 0; +/* stop a PCM stream; this doesn't sync with the unlinked URBs */ +static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction, + int type) +{ + unsigned long flags; + struct line6_pcm_stream *pstr = get_stream(line6pcm, direction); + + spin_lock_irqsave(&pstr->lock, flags); + clear_bit(type, &pstr->running); + if (!pstr->running) { + line6_unlink_audio_urbs(line6pcm, pstr); + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + line6pcm->prev_fbuf = NULL; + line6pcm->prev_fsize = 0; + } + } + spin_unlock_irqrestore(&pstr->lock, flags); } -EXPORT_SYMBOL_GPL(line6_pcm_release); -/* trigger callback */ +/* common PCM trigger callback */ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct snd_pcm_substream *s; int err; - spin_lock(&line6pcm->lock_trigger); - clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags); + clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags); snd_pcm_group_for_each_entry(s, substream) { if (s->pcm->card != substream->pcm->card) continue; - switch (s->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - err = snd_line6_playback_trigger(line6pcm, cmd); - if (err < 0) { - spin_unlock(&line6pcm->lock_trigger); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + err = line6_stream_start(line6pcm, s->stream, + LINE6_STREAM_PCM); + if (err < 0) return err; - } - break; - case SNDRV_PCM_STREAM_CAPTURE: - err = snd_line6_capture_trigger(line6pcm, cmd); + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + line6_stream_stop(line6pcm, s->stream, + LINE6_STREAM_PCM); + break; - if (err < 0) { - spin_unlock(&line6pcm->lock_trigger); - return err; - } + case SNDRV_PCM_TRIGGER_PAUSE_PUSH: + if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + set_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); + break; + case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: + if (s->stream != SNDRV_PCM_STREAM_PLAYBACK) + return -EINVAL; + clear_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags); break; default: - dev_err(line6pcm->line6->ifcdev, - "Unknown stream direction %d\n", s->stream); + return -EINVAL; } } - spin_unlock(&line6pcm->lock_trigger); return 0; } +/* common PCM pointer callback */ +snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); + + return pstr->pos_done; +} + +/* Acquire and start duplex streams: + * type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR + */ +int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type) +{ + struct line6_pcm_stream *pstr; + int ret = 0, dir; + + mutex_lock(&line6pcm->state_mutex); + for (dir = 0; dir < 2; dir++) { + pstr = get_stream(line6pcm, dir); + ret = line6_buffer_acquire(line6pcm, pstr, type); + if (ret < 0) + goto error; + if (!pstr->running) + line6_wait_clear_audio_urbs(line6pcm, pstr); + } + for (dir = 0; dir < 2; dir++) { + ret = line6_stream_start(line6pcm, dir, type); + if (ret < 0) + goto error; + } + error: + mutex_unlock(&line6pcm->state_mutex); + if (ret < 0) + line6_pcm_release(line6pcm, type); + return ret; +} +EXPORT_SYMBOL_GPL(line6_pcm_acquire); + +/* Stop and release duplex streams */ +void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type) +{ + struct line6_pcm_stream *pstr; + int dir; + + mutex_lock(&line6pcm->state_mutex); + for (dir = 0; dir < 2; dir++) + line6_stream_stop(line6pcm, dir, type); + for (dir = 0; dir < 2; dir++) { + pstr = get_stream(line6pcm, dir); + line6_buffer_release(line6pcm, pstr, type); + } + mutex_unlock(&line6pcm->state_mutex); +} +EXPORT_SYMBOL_GPL(line6_pcm_release); + +/* common PCM hw_params callback */ +int snd_line6_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + int ret; + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); + + mutex_lock(&line6pcm->state_mutex); + ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM); + if (ret < 0) + goto error; + + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + if (ret < 0) { + line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); + goto error; + } + + pstr->period = params_period_bytes(hw_params); + error: + mutex_unlock(&line6pcm->state_mutex); + return ret; +} + +/* common PCM hw_free callback */ +int snd_line6_hw_free(struct snd_pcm_substream *substream) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); + struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); + + mutex_lock(&line6pcm->state_mutex); + line6_buffer_release(line6pcm, pstr, LINE6_STREAM_PCM); + mutex_unlock(&line6pcm->state_mutex); + return snd_pcm_lib_free_pages(substream); +} + + /* control info callback */ static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) @@ -282,7 +386,7 @@ static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol, int i; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - for (i = 2; i--;) + for (i = 0; i < 2; i++) ucontrol->value.integer.value[i] = line6pcm->volume_playback[i]; return 0; @@ -295,7 +399,7 @@ static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol, int i, changed = 0; struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); - for (i = 2; i--;) + for (i = 0; i < 2; i++) if (line6pcm->volume_playback[i] != ucontrol->value.integer.value[i]) { line6pcm->volume_playback[i] = @@ -334,21 +438,24 @@ static struct snd_kcontrol_new line6_controls[] = { /* Cleanup the PCM device. */ -static void line6_cleanup_pcm(struct snd_pcm *pcm) +static void cleanup_urbs(struct line6_pcm_stream *pcms) { int i; - struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); - for (i = LINE6_ISO_BUFFERS; i--;) { - if (line6pcm->urb_audio_out[i]) { - usb_kill_urb(line6pcm->urb_audio_out[i]); - usb_free_urb(line6pcm->urb_audio_out[i]); - } - if (line6pcm->urb_audio_in[i]) { - usb_kill_urb(line6pcm->urb_audio_in[i]); - usb_free_urb(line6pcm->urb_audio_in[i]); + for (i = 0; i < LINE6_ISO_BUFFERS; i++) { + if (pcms->urbs[i]) { + usb_kill_urb(pcms->urbs[i]); + usb_free_urb(pcms->urbs[i]); } } +} + +static void line6_cleanup_pcm(struct snd_pcm *pcm) +{ + struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm); + + cleanup_urbs(&line6pcm->out); + cleanup_urbs(&line6pcm->in); kfree(line6pcm); } @@ -383,8 +490,10 @@ static int snd_line6_new_pcm(struct usb_line6 *line6, struct snd_pcm **pcm_ret) */ void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm) { - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - line6_unlink_wait_clear_audio_in_urbs(line6pcm); + line6_unlink_audio_urbs(line6pcm, &line6pcm->out); + line6_unlink_audio_urbs(line6pcm, &line6pcm->in); + line6_wait_clear_audio_urbs(line6pcm, &line6pcm->out); + line6_wait_clear_audio_urbs(line6pcm, &line6pcm->in); } /* @@ -411,6 +520,7 @@ int line6_init_pcm(struct usb_line6 *line6, if (!line6pcm) return -ENOMEM; + mutex_init(&line6pcm->state_mutex); line6pcm->pcm = pcm; line6pcm->properties = properties; line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; @@ -424,9 +534,8 @@ int line6_init_pcm(struct usb_line6 *line6, usb_maxpacket(line6->usbdev, usb_sndisocpipe(line6->usbdev, ep_write), 1)); - spin_lock_init(&line6pcm->lock_audio_out); - spin_lock_init(&line6pcm->lock_audio_in); - spin_lock_init(&line6pcm->lock_trigger); + spin_lock_init(&line6pcm->out.lock); + spin_lock_init(&line6pcm->in.lock); line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; line6->line6pcm = line6pcm; @@ -458,30 +567,22 @@ EXPORT_SYMBOL_GPL(line6_init_pcm); int snd_line6_prepare(struct snd_pcm_substream *substream) { struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - switch (substream->stream) { - case SNDRV_PCM_STREAM_PLAYBACK: - if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) - line6_unlink_wait_clear_audio_out_urbs(line6pcm); - - break; - - case SNDRV_PCM_STREAM_CAPTURE: - if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) - line6_unlink_wait_clear_audio_in_urbs(line6pcm); - - break; - } - - if (!test_and_set_bit(LINE6_INDEX_PREPARED, &line6pcm->flags)) { - line6pcm->count_out = 0; - line6pcm->pos_out = 0; - line6pcm->pos_out_done = 0; - line6pcm->bytes_out = 0; - line6pcm->count_in = 0; - line6pcm->pos_in_done = 0; - line6pcm->bytes_in = 0; + struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream); + + mutex_lock(&line6pcm->state_mutex); + if (!pstr->running) + line6_wait_clear_audio_urbs(line6pcm, pstr); + + if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) { + line6pcm->out.count = 0; + line6pcm->out.pos = 0; + line6pcm->out.pos_done = 0; + line6pcm->out.bytes = 0; + line6pcm->in.count = 0; + line6pcm->in.pos_done = 0; + line6pcm->in.bytes = 0; } + mutex_unlock(&line6pcm->state_mutex); return 0; } diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h index c742b33666eb..42d3e6fc2c61 100644 --- a/sound/usb/line6/pcm.h +++ b/sound/usb/line6/pcm.h @@ -54,109 +54,33 @@ However, from the device's point of view, there is just a single capture and playback stream, which must be shared between these subsystems. It is therefore necessary to maintain the state of the - subsystems with respect to PCM usage. We define several constants of - the form LINE6_BIT_PCM_<subsystem>_<direction>_<resource> with the - following meanings: - *) <subsystem> is one of - -) ALSA: PCM playback and capture via ALSA - -) MONITOR: software monitoring - -) IMPULSE: optional impulse response measurement - *) <direction> is one of - -) PLAYBACK: audio output (from host to device) - -) CAPTURE: audio input (from device to host) - *) <resource> is one of - -) BUFFER: buffer required by PCM data stream - -) STREAM: actual PCM data stream - - The subsystems call line6_pcm_acquire() to acquire the (shared) - resources needed for a particular operation (e.g., allocate the buffer - for ALSA playback or start the capture stream for software monitoring). - When a resource is no longer needed, it is released by calling - line6_pcm_release(). Buffer allocation and stream startup are handled - separately to allow the ALSA kernel driver to perform them at - appropriate places (since the callback which starts a PCM stream is not - allowed to sleep). + subsystems with respect to PCM usage. + + We define two bit flags, "opened" and "running", for each playback + or capture stream. Both can contain the bit flag corresponding to + LINE6_STREAM_* type, + LINE6_STREAM_PCM = ALSA PCM playback or capture + LINE6_STREAM_MONITOR = software monitoring + IMPULSE = optional impulse response measurement + The opened flag indicates whether the buffer is allocated while + the running flag indicates whether the stream is running. + + For monitor or impulse operations, the driver needs to call + snd_line6_duplex_acquire() or snd_line6_duplex_release() with the + appropriate LINE6_STREAM_* flag. */ + +/* stream types */ enum { - /* individual bit indices: */ - LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - LINE6_INDEX_PCM_ALSA_CAPTURE_BUFFER, - LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_MONITOR_PLAYBACK_STREAM, - LINE6_INDEX_PCM_MONITOR_CAPTURE_BUFFER, - LINE6_INDEX_PCM_MONITOR_CAPTURE_STREAM, - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_BUFFER, - LINE6_INDEX_PCM_IMPULSE_PLAYBACK_STREAM, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_BUFFER, - LINE6_INDEX_PCM_IMPULSE_CAPTURE_STREAM, - LINE6_INDEX_PAUSE_PLAYBACK, - LINE6_INDEX_PREPARED, - -#define LINE6_BIT(x) LINE6_BIT_ ## x = 1 << LINE6_INDEX_ ## x - - /* individual bit masks: */ - LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), - LINE6_BIT(PCM_ALSA_PLAYBACK_STREAM), - LINE6_BIT(PCM_ALSA_CAPTURE_BUFFER), - LINE6_BIT(PCM_ALSA_CAPTURE_STREAM), - LINE6_BIT(PCM_MONITOR_PLAYBACK_BUFFER), - LINE6_BIT(PCM_MONITOR_PLAYBACK_STREAM), - LINE6_BIT(PCM_MONITOR_CAPTURE_BUFFER), - LINE6_BIT(PCM_MONITOR_CAPTURE_STREAM), - LINE6_BIT(PCM_IMPULSE_PLAYBACK_BUFFER), - LINE6_BIT(PCM_IMPULSE_PLAYBACK_STREAM), - LINE6_BIT(PCM_IMPULSE_CAPTURE_BUFFER), - LINE6_BIT(PCM_IMPULSE_CAPTURE_STREAM), - LINE6_BIT(PAUSE_PLAYBACK), - LINE6_BIT(PREPARED), - - /* combined bit masks (by operation): */ - LINE6_BITS_PCM_ALSA_BUFFER = - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER, - - LINE6_BITS_PCM_ALSA_STREAM = - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM, - - LINE6_BITS_PCM_MONITOR = - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - - LINE6_BITS_PCM_IMPULSE = - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM, - - /* combined bit masks (by direction): */ - LINE6_BITS_PLAYBACK_BUFFER = - LINE6_BIT_PCM_IMPULSE_PLAYBACK_BUFFER | - LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER | - LINE6_BIT_PCM_MONITOR_PLAYBACK_BUFFER, - - LINE6_BITS_PLAYBACK_STREAM = - LINE6_BIT_PCM_IMPULSE_PLAYBACK_STREAM | - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM | - LINE6_BIT_PCM_MONITOR_PLAYBACK_STREAM, - - LINE6_BITS_CAPTURE_BUFFER = - LINE6_BIT_PCM_IMPULSE_CAPTURE_BUFFER | - LINE6_BIT_PCM_ALSA_CAPTURE_BUFFER | - LINE6_BIT_PCM_MONITOR_CAPTURE_BUFFER, - - LINE6_BITS_CAPTURE_STREAM = - LINE6_BIT_PCM_IMPULSE_CAPTURE_STREAM | - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM | - LINE6_BIT_PCM_MONITOR_CAPTURE_STREAM, - - LINE6_BITS_STREAM = - LINE6_BITS_PLAYBACK_STREAM | - LINE6_BITS_CAPTURE_STREAM + LINE6_STREAM_PCM, + LINE6_STREAM_MONITOR, + LINE6_STREAM_IMPULSE, +}; + +/* misc bit flags for PCM operation */ +enum { + LINE6_FLAG_PAUSE_PLAYBACK, + LINE6_FLAG_PREPARED, }; struct line6_pcm_properties { @@ -165,115 +89,87 @@ struct line6_pcm_properties { int bytes_per_frame; }; -struct snd_line6_pcm { - /** - Pointer back to the Line 6 driver data structure. - */ - struct usb_line6 *line6; +struct line6_pcm_stream { + /* allocated URBs */ + struct urb *urbs[LINE6_ISO_BUFFERS]; - /** - Properties. - */ - struct line6_pcm_properties *properties; + /* Temporary buffer; + * Since the packet size is not known in advance, this buffer is + * large enough to store maximum size packets. + */ + unsigned char *buffer; - /** - ALSA pcm stream - */ - struct snd_pcm *pcm; + /* Free frame position in the buffer. */ + snd_pcm_uframes_t pos; - /** - URBs for audio playback. - */ - struct urb *urb_audio_out[LINE6_ISO_BUFFERS]; + /* Count processed bytes; + * This is modulo period size (to determine when a period is finished). + */ + unsigned bytes; - /** - URBs for audio capture. - */ - struct urb *urb_audio_in[LINE6_ISO_BUFFERS]; + /* Counter to create desired sample rate */ + unsigned count; - /** - Temporary buffer for playback. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_out; + /* period size in bytes */ + unsigned period; - /** - Temporary buffer for capture. - Since the packet size is not known in advance, this buffer is - large enough to store maximum size packets. - */ - unsigned char *buffer_in; + /* Processed frame position in the buffer; + * The contents of the ring buffer have been consumed by the USB + * subsystem (i.e., sent to the USB device) up to this position. + */ + snd_pcm_uframes_t pos_done; - /** - Previously captured frame (for software monitoring). - */ - unsigned char *prev_fbuf; + /* Bit mask of active URBs */ + unsigned long active_urbs; - /** - Size of previously captured frame (for software monitoring). - */ - int prev_fsize; + /* Bit mask of URBs currently being unlinked */ + unsigned long unlink_urbs; - /** - Free frame position in the playback buffer. - */ - snd_pcm_uframes_t pos_out; + /* Spin lock to protect updates of the buffer positions (not contents) + */ + spinlock_t lock; - /** - Count processed bytes for playback. - This is modulo period size (to determine when a period is - finished). - */ - unsigned bytes_out; + /* Bit flags for operational stream types */ + unsigned long opened; - /** - Counter to create desired playback sample rate. - */ - unsigned count_out; + /* Bit flags for running stream types */ + unsigned long running; - /** - Playback period size in bytes - */ - unsigned period_out; + int last_frame; +}; +struct snd_line6_pcm { /** - Processed frame position in the playback buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. + Pointer back to the Line 6 driver data structure. */ - snd_pcm_uframes_t pos_out_done; + struct usb_line6 *line6; /** - Count processed bytes for capture. - This is modulo period size (to determine when a period is - finished). + Properties. */ - unsigned bytes_in; + struct line6_pcm_properties *properties; /** - Counter to create desired capture sample rate. + ALSA pcm stream */ - unsigned count_in; + struct snd_pcm *pcm; - /** - Capture period size in bytes - */ - unsigned period_in; + /* protection to state changes of in/out streams */ + struct mutex state_mutex; + + /* Capture and playback streams */ + struct line6_pcm_stream in; + struct line6_pcm_stream out; /** - Processed frame position in the capture buffer. - The contents of the output ring buffer have been consumed by - the USB subsystem (i.e., sent to the USB device) up to this - position. + Previously captured frame (for software monitoring). */ - snd_pcm_uframes_t pos_in_done; + unsigned char *prev_fbuf; /** - Bit mask of active playback URBs. + Size of previously captured frame (for software monitoring). */ - unsigned long active_urb_out; + int prev_fsize; /** Maximum size of USB packet. @@ -281,38 +177,6 @@ struct snd_line6_pcm { int max_packet_size; /** - Bit mask of active capture URBs. - */ - unsigned long active_urb_in; - - /** - Bit mask of playback URBs currently being unlinked. - */ - unsigned long unlink_urb_out; - - /** - Bit mask of capture URBs currently being unlinked. - */ - unsigned long unlink_urb_in; - - /** - Spin lock to protect updates of the playback buffer positions (not - contents!) - */ - spinlock_t lock_audio_out; - - /** - Spin lock to protect updates of the capture buffer positions (not - contents!) - */ - spinlock_t lock_audio_in; - - /** - Spin lock to protect trigger. - */ - spinlock_t lock_trigger; - - /** PCM playback volume (left and right). */ int volume_playback[2]; @@ -338,19 +202,21 @@ struct snd_line6_pcm { int impulse_count; /** - Several status bits (see LINE6_BIT_*). + Several status bits (see LINE6_FLAG_*). */ unsigned long flags; - - int last_frame_in, last_frame_out; }; extern int line6_init_pcm(struct usb_line6 *line6, struct line6_pcm_properties *properties); extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); extern int snd_line6_prepare(struct snd_pcm_substream *substream); +extern int snd_line6_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params); +extern int snd_line6_hw_free(struct snd_pcm_substream *substream); +extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream); extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm); -extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels); -extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); +extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type); +extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type); #endif diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c index 1c9f95a370ff..1708c05f14db 100644 --- a/sound/usb/line6/playback.c +++ b/sound/usb/line6/playback.c @@ -37,7 +37,8 @@ static void change_volume(struct urb *urb_out, int volume[], buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); for (; p < buf_end; ++p) { - *p = (*p * volume[chn & 1]) >> 8; + int val = (*p * volume[chn & 1]) >> 8; + *p = clamp(val, 0x7fff, -0x8000); ++chn; } } else if (bytes_per_frame == 6) { @@ -51,6 +52,7 @@ static void change_volume(struct urb *urb_out, int volume[], val = p[0] + (p[1] << 8) + ((signed char)p[2] << 16); val = (val * volume[chn & 1]) >> 8; + val = clamp(val, 0x7fffff, -0x800000); p[0] = val; p[1] = val >> 8; p[2] = val >> 16; @@ -118,8 +120,10 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, po = (short *)urb_out->transfer_buffer; buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); - for (; po < buf_end; ++pi, ++po) - *po += (*pi * volume) >> 8; + for (; po < buf_end; ++pi, ++po) { + int val = *po + ((*pi * volume) >> 8); + *po = clamp(val, 0x7fff, -0x8000); + } } /* @@ -130,11 +134,11 @@ static void add_monitor_signal(struct urb *urb_out, unsigned char *signal, /* Find a free URB, prepare audio data, and submit URB. + must be called in line6pcm->out.lock context */ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) { int index; - unsigned long flags; int i, urb_size, urb_frames; int ret; const int bytes_per_frame = line6pcm->properties->bytes_per_frame; @@ -145,17 +149,15 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL); struct urb *urb_out; - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); index = - find_first_zero_bit(&line6pcm->active_urb_out, LINE6_ISO_BUFFERS); + find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS); if (index < 0 || index >= LINE6_ISO_BUFFERS) { - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); return -EINVAL; } - urb_out = line6pcm->urb_audio_out[index]; + urb_out = line6pcm->out.urbs[index]; urb_size = 0; for (i = 0; i < LINE6_ISO_PACKETS; ++i) { @@ -164,15 +166,13 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) struct usb_iso_packet_descriptor *fout = &urb_out->iso_frame_desc[i]; - if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) - fsize = line6pcm->prev_fsize; - + fsize = line6pcm->prev_fsize; if (fsize == 0) { int n; - line6pcm->count_out += frame_increment; - n = line6pcm->count_out / frame_factor; - line6pcm->count_out -= n * frame_factor; + line6pcm->out.count += frame_increment; + n = line6pcm->out.count / frame_factor; + line6pcm->out.count -= n * frame_factor; fsize = n * bytes_per_frame; } @@ -183,36 +183,35 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) if (urb_size == 0) { /* can't determine URB size */ - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); return -EINVAL; } urb_frames = urb_size / bytes_per_frame; urb_out->transfer_buffer = - line6pcm->buffer_out + + line6pcm->out.buffer + index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; urb_out->transfer_buffer_length = urb_size; urb_out->context = line6pcm; - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && - !test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { + if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) && + !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) { struct snd_pcm_runtime *runtime = get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->runtime; - if (line6pcm->pos_out + urb_frames > runtime->buffer_size) { + if (line6pcm->out.pos + urb_frames > runtime->buffer_size) { /* The transferred area goes over buffer boundary, copy the data to the temp buffer. */ int len; - len = runtime->buffer_size - line6pcm->pos_out; + len = runtime->buffer_size - line6pcm->out.pos; if (len > 0) { memcpy(urb_out->transfer_buffer, runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, + line6pcm->out.pos * bytes_per_frame, len * bytes_per_frame); memcpy(urb_out->transfer_buffer + len * bytes_per_frame, runtime->dma_area, @@ -223,26 +222,27 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) } else { memcpy(urb_out->transfer_buffer, runtime->dma_area + - line6pcm->pos_out * bytes_per_frame, + line6pcm->out.pos * bytes_per_frame, urb_out->transfer_buffer_length); } - line6pcm->pos_out += urb_frames; - if (line6pcm->pos_out >= runtime->buffer_size) - line6pcm->pos_out -= runtime->buffer_size; + line6pcm->out.pos += urb_frames; + if (line6pcm->out.pos >= runtime->buffer_size) + line6pcm->out.pos -= runtime->buffer_size; + + change_volume(urb_out, line6pcm->volume_playback, + bytes_per_frame); } else { memset(urb_out->transfer_buffer, 0, urb_out->transfer_buffer_length); } - change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); - - if (line6pcm->prev_fbuf != NULL) { - if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) { + spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING); + if (line6pcm->prev_fbuf) { + if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) { create_impulse_test_signal(line6pcm, urb_out, bytes_per_frame); - if (line6pcm->flags & - LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) { + if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) { line6_capture_copy(line6pcm, urb_out->transfer_buffer, urb_out-> @@ -251,101 +251,43 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm) urb_out->transfer_buffer_length); } } else { - if (! - (line6pcm->line6-> - properties->capabilities & LINE6_CAP_HWMON) - && (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) - && (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM)) + if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON) + && line6pcm->out.running && line6pcm->in.running) add_monitor_signal(urb_out, line6pcm->prev_fbuf, line6pcm->volume_monitor, bytes_per_frame); } + line6pcm->prev_fbuf = NULL; + line6pcm->prev_fsize = 0; } + spin_unlock(&line6pcm->in.lock); ret = usb_submit_urb(urb_out, GFP_ATOMIC); if (ret == 0) - set_bit(index, &line6pcm->active_urb_out); + set_bit(index, &line6pcm->out.active_urbs); else dev_err(line6pcm->line6->ifcdev, "URB out #%d submission failed (%d)\n", index, ret); - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); return 0; } /* Submit all currently available playback URBs. -*/ + must be called in line6pcm->out.lock context + */ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm) { - int ret, i; + int ret = 0, i; for (i = 0; i < LINE6_ISO_BUFFERS; ++i) { ret = submit_audio_out_urb(line6pcm); if (ret < 0) - return ret; - } - - return 0; -} - -/* - Unlink all currently active playback URBs. -*/ -void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - unsigned int i; - - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) { - if (!test_and_set_bit(i, &line6pcm->unlink_urb_out)) { - struct urb *u = line6pcm->urb_audio_out[i]; - - usb_unlink_urb(u); - } - } - } -} - -/* - Wait until unlinking of all currently active playback URBs has been - finished. -*/ -void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - int timeout = HZ; - unsigned int i; - int alive; - - do { - alive = 0; - for (i = LINE6_ISO_BUFFERS; i--;) { - if (test_bit(i, &line6pcm->active_urb_out)) - alive++; - } - if (!alive) break; - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } while (--timeout > 0); - if (alive) - snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive); -} - -/* - Unlink all currently active playback URBs, and wait for finishing. -*/ -void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm) -{ - line6_unlink_audio_out_urbs(line6pcm); - line6_wait_clear_audio_out_urbs(line6pcm); -} + } -void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm) -{ - kfree(line6pcm->buffer_out); - line6pcm->buffer_out = NULL; + return ret; } /* @@ -363,56 +305,56 @@ static void audio_out_callback(struct urb *urb) memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); #endif - line6pcm->last_frame_out = urb->start_frame; + line6pcm->out.last_frame = urb->start_frame; /* find index of URB */ - for (index = LINE6_ISO_BUFFERS; index--;) - if (urb == line6pcm->urb_audio_out[index]) + for (index = 0; index < LINE6_ISO_BUFFERS; index++) + if (urb == line6pcm->out.urbs[index]) break; - if (index < 0) + if (index >= LINE6_ISO_BUFFERS) return; /* URB has been unlinked asynchronously */ - for (i = LINE6_ISO_PACKETS; i--;) + for (i = 0; i < LINE6_ISO_PACKETS; i++) length += urb->iso_frame_desc[i].length; - spin_lock_irqsave(&line6pcm->lock_audio_out, flags); + spin_lock_irqsave(&line6pcm->out.lock, flags); - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags)) { + if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) { struct snd_pcm_runtime *runtime = substream->runtime; - line6pcm->pos_out_done += + line6pcm->out.pos_done += length / line6pcm->properties->bytes_per_frame; - if (line6pcm->pos_out_done >= runtime->buffer_size) - line6pcm->pos_out_done -= runtime->buffer_size; + if (line6pcm->out.pos_done >= runtime->buffer_size) + line6pcm->out.pos_done -= runtime->buffer_size; } - clear_bit(index, &line6pcm->active_urb_out); + clear_bit(index, &line6pcm->out.active_urbs); - for (i = LINE6_ISO_PACKETS; i--;) + for (i = 0; i < LINE6_ISO_PACKETS; i++) if (urb->iso_frame_desc[i].status == -EXDEV) { shutdown = 1; break; } - if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) + if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs)) shutdown = 1; - spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags); - if (!shutdown) { submit_audio_out_urb(line6pcm); - if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, - &line6pcm->flags)) { - line6pcm->bytes_out += length; - if (line6pcm->bytes_out >= line6pcm->period_out) { - line6pcm->bytes_out %= line6pcm->period_out; + if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) { + line6pcm->out.bytes += length; + if (line6pcm->out.bytes >= line6pcm->out.period) { + line6pcm->out.bytes %= line6pcm->out.period; + spin_unlock(&line6pcm->out.lock); snd_pcm_period_elapsed(substream); + spin_lock(&line6pcm->out.lock); } } } + spin_unlock_irqrestore(&line6pcm->out.lock, flags); } /* open playback callback */ @@ -438,110 +380,16 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream) return 0; } -/* hw_params playback callback */ -static int snd_line6_playback_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - int ret; - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - /* -- Florian Demski [FD] */ - /* don't ask me why, but this fixes the bug on my machine */ - if (line6pcm == NULL) { - if (substream->pcm == NULL) - return -ENOMEM; - if (substream->pcm->private_data == NULL) - return -ENOMEM; - substream->private_data = substream->pcm->private_data; - line6pcm = snd_pcm_substream_chip(substream); - } - /* -- [FD] end */ - - ret = line6_pcm_acquire(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - - if (ret < 0) - return ret; - - ret = snd_pcm_lib_malloc_pages(substream, - params_buffer_bytes(hw_params)); - if (ret < 0) { - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return ret; - } - - line6pcm->period_out = params_period_bytes(hw_params); - return 0; -} - -/* hw_free playback callback */ -static int snd_line6_playback_hw_free(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - line6_pcm_release(line6pcm, LINE6_BIT_PCM_ALSA_PLAYBACK_BUFFER); - return snd_pcm_lib_free_pages(substream); -} - -/* trigger playback callback */ -int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd) -{ - int err; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - case SNDRV_PCM_TRIGGER_RESUME: - err = line6_pcm_acquire(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_STOP: - case SNDRV_PCM_TRIGGER_SUSPEND: - err = line6_pcm_release(line6pcm, - LINE6_BIT_PCM_ALSA_PLAYBACK_STREAM); - - if (err < 0) - return err; - - break; - - case SNDRV_PCM_TRIGGER_PAUSE_PUSH: - set_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: - clear_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags); - break; - - default: - return -EINVAL; - } - - return 0; -} - -/* playback pointer callback */ -static snd_pcm_uframes_t -snd_line6_playback_pointer(struct snd_pcm_substream *substream) -{ - struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); - - return line6pcm->pos_out_done; -} - /* playback operators */ struct snd_pcm_ops snd_line6_playback_ops = { .open = snd_line6_playback_open, .close = snd_line6_playback_close, .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_line6_playback_hw_params, - .hw_free = snd_line6_playback_hw_free, + .hw_params = snd_line6_hw_params, + .hw_free = snd_line6_hw_free, .prepare = snd_line6_prepare, .trigger = snd_line6_trigger, - .pointer = snd_line6_playback_pointer, + .pointer = snd_line6_pointer, }; int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) @@ -554,7 +402,7 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) struct urb *urb; /* URB for audio out: */ - urb = line6pcm->urb_audio_out[i] = + urb = line6pcm->out.urbs[i] = usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); if (urb == NULL) diff --git a/sound/usb/line6/playback.h b/sound/usb/line6/playback.h index 78a885113221..51fce29e8726 100644 --- a/sound/usb/line6/playback.h +++ b/sound/usb/line6/playback.h @@ -30,12 +30,6 @@ extern struct snd_pcm_ops snd_line6_playback_ops; extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_free_playback_buffer(struct snd_line6_pcm *line6pcm); extern int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern void line6_unlink_wait_clear_audio_out_urbs(struct snd_line6_pcm - *line6pcm); -extern void line6_wait_clear_audio_out_urbs(struct snd_line6_pcm *line6pcm); -extern int snd_line6_playback_trigger(struct snd_line6_pcm *line6pcm, int cmd); #endif diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c index bf027fc70cba..6f7cd585f2d8 100644 --- a/sound/usb/line6/pod.c +++ b/sound/usb/line6/pod.c @@ -399,27 +399,18 @@ static struct snd_kcontrol_new pod_control_monitor = { /* POD device disconnected. */ -static void line6_pod_disconnect(struct usb_interface *interface) +static void line6_pod_disconnect(struct usb_line6 *line6) { - struct usb_line6_pod *pod; + struct usb_line6_pod *pod = (struct usb_line6_pod *)line6; + struct device *dev = line6->ifcdev; - if (interface == NULL) - return; - pod = usb_get_intfdata(interface); - - if (pod != NULL) { - struct device *dev = &interface->dev; - - if (dev != NULL) { - /* remove sysfs entries: */ - device_remove_file(dev, &dev_attr_device_id); - device_remove_file(dev, &dev_attr_firmware_version); - device_remove_file(dev, &dev_attr_serial_number); - } + /* remove sysfs entries: */ + device_remove_file(dev, &dev_attr_device_id); + device_remove_file(dev, &dev_attr_firmware_version); + device_remove_file(dev, &dev_attr_serial_number); - del_timer_sync(&pod->startup_timer); - cancel_work_sync(&pod->startup_work); - } + del_timer_sync(&pod->startup_timer); + cancel_work_sync(&pod->startup_work); } /* @@ -444,8 +435,8 @@ static int pod_create_files2(struct device *dev) /* Try to init POD device. */ -static int pod_init(struct usb_interface *interface, - struct usb_line6 *line6) +static int pod_init(struct usb_line6 *line6, + const struct usb_device_id *id) { int err; struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; @@ -456,11 +447,8 @@ static int pod_init(struct usb_interface *interface, init_timer(&pod->startup_timer); INIT_WORK(&pod->startup_work, pod_startup4); - if ((interface == NULL) || (pod == NULL)) - return -ENODEV; - /* create sysfs entries: */ - err = pod_create_files2(&interface->dev); + err = pod_create_files2(line6->ifcdev); if (err < 0) return err; @@ -603,14 +591,9 @@ static const struct line6_properties pod_properties_table[] = { static int pod_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_line6_pod *pod; - - pod = kzalloc(sizeof(*pod), GFP_KERNEL); - if (!pod) - return -ENODEV; - return line6_probe(interface, &pod->line6, + return line6_probe(interface, id, &pod_properties_table[id->driver_info], - pod_init); + pod_init, sizeof(struct usb_line6_pod)); } static struct usb_driver pod_driver = { diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c index 7217fa7e5db1..43c39886597e 100644 --- a/sound/usb/line6/podhd.c +++ b/sound/usb/line6/podhd.c @@ -87,15 +87,11 @@ static struct line6_pcm_properties podhd_pcm_properties = { /* Try to init POD HD device. */ -static int podhd_init(struct usb_interface *interface, - struct usb_line6 *line6) +static int podhd_init(struct usb_line6 *line6, + const struct usb_device_id *id) { - struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6; int err; - if ((interface == NULL) || (podhd == NULL)) - return -ENODEV; - /* initialize MIDI subsystem: */ err = line6_init_midi(line6); if (err < 0) @@ -181,14 +177,9 @@ static const struct line6_properties podhd_properties_table[] = { static int podhd_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_line6_podhd *podhd; - - podhd = kzalloc(sizeof(*podhd), GFP_KERNEL); - if (!podhd) - return -ENODEV; - return line6_probe(interface, &podhd->line6, + return line6_probe(interface, id, &podhd_properties_table[id->driver_info], - podhd_init); + podhd_init, sizeof(struct usb_line6_podhd)); } static struct usb_driver podhd_driver = { diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c index c1f61cde52ab..819e06b3f3db 100644 --- a/sound/usb/line6/toneport.c +++ b/sound/usb/line6/toneport.c @@ -14,6 +14,7 @@ #include <linux/usb.h> #include <linux/slab.h> #include <linux/module.h> +#include <linux/leds.h> #include <sound/core.h> #include <sound/control.h> @@ -32,6 +33,15 @@ enum line6_device_type { LINE6_TONEPORT_UX2, }; +struct usb_line6_toneport; + +struct toneport_led { + struct led_classdev dev; + char name[64]; + struct usb_line6_toneport *toneport; + bool registered; +}; + struct usb_line6_toneport { /** Generic Line 6 USB data. @@ -62,6 +72,9 @@ struct usb_line6_toneport { Device type. */ enum line6_device_type type; + + /* LED instances */ + struct toneport_led leds[2]; }; static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2); @@ -117,15 +130,6 @@ static struct line6_pcm_properties toneport_pcm_properties = { .bytes_per_frame = 4 }; -/* - For the led on Guitarport. - Brightness goes from 0x00 to 0x26. Set a value above this to have led - blink. - (void cmd_0x02(byte red, byte green) -*/ -static int led_red = 0x00; -static int led_green = 0x26; - static const struct { const char *name; int code; @@ -136,62 +140,6 @@ static const struct { {"Inst & Mic", 0x0901} }; -static bool toneport_has_led(enum line6_device_type type) -{ - return - (type == LINE6_GUITARPORT) || - (type == LINE6_TONEPORT_GX); - /* add your device here if you are missing support for the LEDs */ -} - -static void toneport_update_led(struct device *dev) -{ - struct usb_interface *interface = to_usb_interface(dev); - struct usb_line6_toneport *tp = usb_get_intfdata(interface); - struct usb_line6 *line6; - - if (!tp) - return; - - line6 = &tp->line6; - if (line6) - toneport_send_cmd(line6->usbdev, (led_red << 8) | 0x0002, - led_green); -} - -static ssize_t toneport_set_led_red(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_red); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static ssize_t toneport_set_led_green(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) -{ - int retval; - - retval = kstrtoint(buf, 10, &led_green); - if (retval) - return retval; - - toneport_update_led(dev); - return count; -} - -static DEVICE_ATTR(led_red, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_red); -static DEVICE_ATTR(led_green, S_IWUSR | S_IRUGO, line6_nop_read, - toneport_set_led_green); - static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2) { int ret; @@ -234,16 +182,23 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); + int err; if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) return 0; line6pcm->volume_monitor = ucontrol->value.integer.value[0]; - if (line6pcm->volume_monitor > 0) - line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); - else - line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); + if (line6pcm->volume_monitor > 0) { + err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR); + if (err < 0) { + line6pcm->volume_monitor = 0; + line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR); + return err; + } + } else { + line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR); + } return 1; } @@ -304,7 +259,7 @@ static void toneport_start_pcm(unsigned long arg) struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg; struct usb_line6 *line6 = &toneport->line6; - line6_pcm_acquire(line6->line6pcm, LINE6_BITS_PCM_MONITOR); + line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR); } /* control definition */ @@ -330,6 +285,78 @@ static struct snd_kcontrol_new toneport_control_source = { }; /* + For the led on Guitarport. + Brightness goes from 0x00 to 0x26. Set a value above this to have led + blink. + (void cmd_0x02(byte red, byte green) +*/ + +static bool toneport_has_led(enum line6_device_type type) +{ + return + (type == LINE6_GUITARPORT) || + (type == LINE6_TONEPORT_GX); + /* add your device here if you are missing support for the LEDs */ +} + +static const char * const led_colors[2] = { "red", "green" }; +static const int led_init_vals[2] = { 0x00, 0x26 }; + +static void toneport_update_led(struct usb_line6_toneport *toneport) +{ + toneport_send_cmd(toneport->line6.usbdev, + (toneport->leds[0].dev.brightness << 8) | 0x0002, + toneport->leds[1].dev.brightness); +} + +static void toneport_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct toneport_led *leds = + container_of(led_cdev, struct toneport_led, dev); + toneport_update_led(leds->toneport); +} + +static int toneport_init_leds(struct usb_line6_toneport *toneport) +{ + struct device *dev = &toneport->line6.usbdev->dev; + int i, err; + + for (i = 0; i < 2; i++) { + struct toneport_led *led = &toneport->leds[i]; + struct led_classdev *leddev = &led->dev; + + led->toneport = toneport; + snprintf(led->name, sizeof(led->name), "%s::%s", + dev_name(dev), led_colors[i]); + leddev->name = led->name; + leddev->brightness = led_init_vals[i]; + leddev->max_brightness = 0x26; + leddev->brightness_set = toneport_led_brightness_set; + err = led_classdev_register(dev, leddev); + if (err) + return err; + led->registered = true; + } + + return 0; +} + +static void toneport_remove_leds(struct usb_line6_toneport *toneport) +{ + struct toneport_led *led; + int i; + + for (i = 0; i < 2; i++) { + led = &toneport->leds[i]; + if (!led->registered) + break; + led_classdev_unregister(&led->dev); + led->registered = false; + } +} + +/* Setup Toneport device. */ static void toneport_setup(struct usb_line6_toneport *toneport) @@ -359,42 +386,38 @@ static void toneport_setup(struct usb_line6_toneport *toneport) } if (toneport_has_led(toneport->type)) - toneport_update_led(&usbdev->dev); + toneport_update_led(toneport); + + mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ); } /* Toneport device disconnected. */ -static void line6_toneport_disconnect(struct usb_interface *interface) +static void line6_toneport_disconnect(struct usb_line6 *line6) { - struct usb_line6_toneport *toneport; - u16 idProduct; - - if (interface == NULL) - return; + struct usb_line6_toneport *toneport = + (struct usb_line6_toneport *)line6; - toneport = usb_get_intfdata(interface); del_timer_sync(&toneport->timer); - idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct); - if (toneport_has_led(idProduct)) { - device_remove_file(&interface->dev, &dev_attr_led_red); - device_remove_file(&interface->dev, &dev_attr_led_green); - } + if (toneport_has_led(toneport->type)) + toneport_remove_leds(toneport); } /* Try to init Toneport device. */ -static int toneport_init(struct usb_interface *interface, - struct usb_line6 *line6) +static int toneport_init(struct usb_line6 *line6, + const struct usb_device_id *id) { int err; struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; - if ((interface == NULL) || (toneport == NULL)) - return -ENODEV; + toneport->type = id->driver_info; + setup_timer(&toneport->timer, toneport_start_pcm, + (unsigned long)toneport); line6->disconnect = line6_toneport_disconnect; @@ -431,20 +454,13 @@ static int toneport_init(struct usb_interface *interface, line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1); if (toneport_has_led(toneport->type)) { - err = device_create_file(&interface->dev, &dev_attr_led_red); - if (err < 0) - return err; - err = device_create_file(&interface->dev, &dev_attr_led_green); + err = toneport_init_leds(toneport); if (err < 0) return err; } toneport_setup(toneport); - setup_timer(&toneport->timer, toneport_start_pcm, - (unsigned long)toneport); - mod_timer(&toneport->timer, jiffies + TONEPORT_PCM_DELAY * HZ); - /* register audio system: */ return snd_card_register(line6->card); } @@ -549,15 +565,9 @@ static const struct line6_properties toneport_properties_table[] = { static int toneport_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_line6_toneport *toneport; - - toneport = kzalloc(sizeof(*toneport), GFP_KERNEL); - if (!toneport) - return -ENODEV; - toneport->type = id->driver_info; - return line6_probe(interface, &toneport->line6, + return line6_probe(interface, id, &toneport_properties_table[id->driver_info], - toneport_init); + toneport_init, sizeof(struct usb_line6_toneport)); } static struct usb_driver toneport_driver = { diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c index 99a58cbfd2da..9701ffa61365 100644 --- a/sound/usb/line6/variax.c +++ b/sound/usb/line6/variax.c @@ -210,16 +210,9 @@ static void line6_variax_process_message(struct usb_line6 *line6) /* Variax destructor. */ -static void line6_variax_disconnect(struct usb_interface *interface) +static void line6_variax_disconnect(struct usb_line6 *line6) { - struct usb_line6_variax *variax; - - if (!interface) - return; - - variax = usb_get_intfdata(interface); - if (!variax) - return; + struct usb_line6_variax *variax = (struct usb_line6_variax *)line6; del_timer(&variax->startup_timer1); del_timer(&variax->startup_timer2); @@ -231,8 +224,8 @@ static void line6_variax_disconnect(struct usb_interface *interface) /* Try to init workbench device. */ -static int variax_init(struct usb_interface *interface, - struct usb_line6 *line6) +static int variax_init(struct usb_line6 *line6, + const struct usb_device_id *id) { struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; int err; @@ -244,9 +237,6 @@ static int variax_init(struct usb_interface *interface, init_timer(&variax->startup_timer2); INIT_WORK(&variax->startup_work, variax_startup6); - if ((interface == NULL) || (variax == NULL)) - return -ENODEV; - /* initialize USB buffers: */ variax->buffer_activate = kmemdup(variax_activate, sizeof(variax_activate), GFP_KERNEL); @@ -306,14 +296,9 @@ static const struct line6_properties variax_properties_table[] = { static int variax_probe(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_line6_variax *variax; - - variax = kzalloc(sizeof(*variax), GFP_KERNEL); - if (!variax) - return -ENODEV; - return line6_probe(interface, &variax->line6, + return line6_probe(interface, id, &variax_properties_table[id->driver_info], - variax_init); + variax_init, sizeof(struct usb_line6_variax)); } static struct usb_driver variax_driver = { |