summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2015-01-28 07:24:41 +0100
committerTakashi Iwai <tiwai@suse.de>2015-01-28 07:24:41 +0100
commit5e0ddd07fa8fcbb84faac666d36ff9c37449a849 (patch)
tree29ff039af456f0aed5280943bc9e1593893b5b21
parent1001fb810b1295d0600c0c6bdcb17889460470a5 (diff)
parent247d95ee6dd22e5323ecf7a73ff64110ef2fa2da (diff)
downloadlinux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.tar.gz
linux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.tar.bz2
linux-5e0ddd07fa8fcbb84faac666d36ff9c37449a849.zip
Merge branch 'topic/line6' into for-next
-rw-r--r--sound/usb/line6/Kconfig2
-rw-r--r--sound/usb/line6/capture.c223
-rw-r--r--sound/usb/line6/capture.h6
-rw-r--r--sound/usb/line6/driver.c202
-rw-r--r--sound/usb/line6/driver.h10
-rw-r--r--sound/usb/line6/midi.c18
-rw-r--r--sound/usb/line6/midi.h7
-rw-r--r--sound/usb/line6/pcm.c443
-rw-r--r--sound/usb/line6/pcm.h304
-rw-r--r--sound/usb/line6/playback.c284
-rw-r--r--sound/usb/line6/playback.h6
-rw-r--r--sound/usb/line6/pod.c45
-rw-r--r--sound/usb/line6/podhd.c17
-rw-r--r--sound/usb/line6/toneport.c216
-rw-r--r--sound/usb/line6/variax.c27
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->l