2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-18 10:13:57 +08:00

Merge branch 'topic/line6' into for-next

This commit is contained in:
Takashi Iwai 2015-01-28 07:24:41 +01:00
commit 5e0ddd07fa
15 changed files with 716 additions and 1116 deletions

View File

@ -29,6 +29,8 @@ config SND_USB_PODHD
config SND_USB_TONEPORT config SND_USB_TONEPORT
tristate "TonePort GX, UX1 and UX2 USB support" tristate "TonePort GX, UX1 and UX2 USB support"
select SND_USB_LINE6 select SND_USB_LINE6
select NEW_LEDS
select LEDS_CLASS
help help
This is a driver for TonePort GX, UX1 and UX2 devices. This is a driver for TonePort GX, UX1 and UX2 devices.

View File

@ -20,26 +20,24 @@
/* /*
Find a free URB and submit it. 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) static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
{ {
int index; int index;
unsigned long flags;
int i, urb_size; int i, urb_size;
int ret; int ret;
struct urb *urb_in; struct urb *urb_in;
spin_lock_irqsave(&line6pcm->lock_audio_in, flags);
index = 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) { 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"); dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL; return -EINVAL;
} }
urb_in = line6pcm->urb_audio_in[index]; urb_in = line6pcm->in.urbs[index];
urb_size = 0; urb_size = 0;
for (i = 0; i < LINE6_ISO_PACKETS; ++i) { 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 = urb_in->transfer_buffer =
line6pcm->buffer_in + line6pcm->in.buffer +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_in->transfer_buffer_length = urb_size; urb_in->transfer_buffer_length = urb_size;
urb_in->context = line6pcm; 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); ret = usb_submit_urb(urb_in, GFP_ATOMIC);
if (ret == 0) if (ret == 0)
set_bit(index, &line6pcm->active_urb_in); set_bit(index, &line6pcm->in.active_urbs);
else else
dev_err(line6pcm->line6->ifcdev, dev_err(line6pcm->line6->ifcdev,
"URB in #%d submission failed (%d)\n", index, ret); "URB in #%d submission failed (%d)\n", index, ret);
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
return 0; return 0;
} }
/* /*
Submit all currently available capture URBs. 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 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) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_in_urb(line6pcm); ret = submit_audio_in_urb(line6pcm);
if (ret < 0) 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; 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);
}
/* return ret;
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);
} }
/* /*
@ -150,18 +96,18 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
if (runtime == NULL) if (runtime == NULL)
return; 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, The transferred area goes over buffer boundary,
copy two separate chunks. copy two separate chunks.
*/ */
int len; int len;
len = runtime->buffer_size - line6pcm->pos_in_done; len = runtime->buffer_size - line6pcm->in.pos_done;
if (len > 0) { if (len > 0) {
memcpy(runtime->dma_area + memcpy(runtime->dma_area +
line6pcm->pos_in_done * bytes_per_frame, fbuf, line6pcm->in.pos_done * bytes_per_frame, fbuf,
len * bytes_per_frame); len * bytes_per_frame);
memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, memcpy(runtime->dma_area, fbuf + len * bytes_per_frame,
(frames - 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 { } else {
/* copy single chunk */ /* copy single chunk */
memcpy(runtime->dma_area + 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; line6pcm->in.pos_done += frames;
if (line6pcm->pos_in_done >= runtime->buffer_size) if (line6pcm->in.pos_done >= runtime->buffer_size)
line6pcm->pos_in_done -= runtime->buffer_size; line6pcm->in.pos_done -= runtime->buffer_size;
} }
void line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) 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 = struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
line6pcm->bytes_in += length; line6pcm->in.bytes += length;
if (line6pcm->bytes_in >= line6pcm->period_in) { if (line6pcm->in.bytes >= line6pcm->in.period) {
line6pcm->bytes_in %= line6pcm->period_in; line6pcm->in.bytes %= line6pcm->in.period;
spin_unlock(&line6pcm->in.lock);
snd_pcm_period_elapsed(substream); 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. * 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; 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 */ /* find index of URB */
for (index = 0; index < LINE6_ISO_BUFFERS; ++index) for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
if (urb == line6pcm->urb_audio_in[index]) if (urb == line6pcm->in.urbs[index])
break; break;
spin_lock_irqsave(&line6pcm->lock_audio_in, flags); spin_lock_irqsave(&line6pcm->in.lock, flags);
for (i = 0; i < LINE6_ISO_PACKETS; ++i) { for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
char *fbuf; char *fbuf;
@ -243,27 +185,26 @@ static void audio_in_callback(struct urb *urb)
line6pcm->prev_fbuf = fbuf; line6pcm->prev_fbuf = fbuf;
line6pcm->prev_fsize = fsize; line6pcm->prev_fsize = fsize;
if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
&line6pcm->flags) && (fsize > 0)) fsize > 0)
line6_capture_copy(line6pcm, fbuf, fsize); 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; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_in, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_in_urb(line6pcm); submit_audio_in_urb(line6pcm);
if (!(line6pcm->flags & LINE6_BITS_PCM_IMPULSE)) if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
if (test_bit(LINE6_INDEX_PCM_ALSA_CAPTURE_STREAM, test_bit(LINE6_STREAM_PCM, &line6pcm->in.running))
&line6pcm->flags)) line6_capture_check_period(line6pcm, length);
line6_capture_check_period(line6pcm, length);
} }
spin_unlock_irqrestore(&line6pcm->in.lock, flags);
} }
/* open capture callback */ /* open capture callback */
@ -290,102 +231,16 @@ static int snd_line6_capture_close(struct snd_pcm_substream *substream)
return 0; 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 */ /* capture operators */
struct snd_pcm_ops snd_line6_capture_ops = { struct snd_pcm_ops snd_line6_capture_ops = {
.open = snd_line6_capture_open, .open = snd_line6_capture_open,
.close = snd_line6_capture_close, .close = snd_line6_capture_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_capture_hw_params, .hw_params = snd_line6_hw_params,
.hw_free = snd_line6_capture_hw_free, .hw_free = snd_line6_hw_free,
.prepare = snd_line6_prepare, .prepare = snd_line6_prepare,
.trigger = snd_line6_trigger, .trigger = snd_line6_trigger,
.pointer = snd_line6_capture_pointer, .pointer = snd_line6_pointer,
}; };
int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) 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; struct urb *urb;
/* URB for audio in: */ /* URB for audio in: */
urb = line6pcm->urb_audio_in[i] = urb = line6pcm->in.urbs[i] =
usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
if (urb == NULL) if (urb == NULL)

View File

@ -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, extern void line6_capture_check_period(struct snd_line6_pcm *line6pcm,
int length); int length);
extern int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm); 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 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 #endif

View File

@ -412,27 +412,13 @@ int line6_read_serial_number(struct usb_line6 *line6, int *serial_number)
} }
EXPORT_SYMBOL_GPL(line6_read_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. Card destructor.
*/ */
static void line6_destruct(struct snd_card *card) static void line6_destruct(struct snd_card *card)
{ {
struct usb_line6 *line6 = card->private_data; struct usb_line6 *line6 = card->private_data;
struct usb_device *usbdev; struct usb_device *usbdev = line6->usbdev;
if (!line6)
return;
usbdev = line6->usbdev;
/* free buffer memory first: */ /* free buffer memory first: */
kfree(line6->buffer_message); kfree(line6->buffer_message);
@ -441,31 +427,102 @@ static void line6_destruct(struct snd_card *card)
/* then free URBs: */ /* then free URBs: */
usb_free_urb(line6->urb_listen); usb_free_urb(line6->urb_listen);
/* free interface data: */
kfree(line6);
/* decrement reference counters: */ /* decrement reference counters: */
usb_put_dev(usbdev); 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. Probe USB device.
*/ */
int line6_probe(struct usb_interface *interface, int line6_probe(struct usb_interface *interface,
struct usb_line6 *line6, const struct usb_device_id *id,
const struct line6_properties *properties, 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 usb_device *usbdev = interface_to_usbdev(interface);
struct snd_card *card; struct snd_card *card;
struct usb_line6 *line6;
int interface_number; int interface_number;
int ret; int ret;
if (WARN_ON(data_size < sizeof(*line6)))
return -EINVAL;
/* we don't handle multiple configurations */ /* we don't handle multiple configurations */
if (usbdev->descriptor.bNumConfigurations != 1) { if (usbdev->descriptor.bNumConfigurations != 1)
ret = -ENODEV; return -ENODEV;
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;
strcpy(card->id, properties->id);
strcpy(card->driver, DRIVER_NAME);
strcpy(card->shortname, properties->name);
sprintf(card->longname, "Line 6 %s at USB %s", properties->name,
dev_name(line6->ifcdev));
card->private_free = line6_destruct;
usb_set_intfdata(interface, line6);
/* increment reference counters: */
usb_get_dev(usbdev);
/* initialize device info: */ /* initialize device info: */
dev_info(&interface->dev, "Line 6 %s found\n", properties->name); dev_info(&interface->dev, "Line 6 %s found\n", properties->name);
@ -474,102 +531,36 @@ int line6_probe(struct usb_interface *interface,
interface_number = interface->cur_altsetting->desc.bInterfaceNumber; interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
ret = usb_set_interface(usbdev, interface_number, ret = usb_set_interface(usbdev, interface_number,
properties->altsetting); properties->altsetting);
if (ret < 0) { if (ret < 0) {
dev_err(&interface->dev, "set_interface failed\n"); dev_err(&interface->dev, "set_interface failed\n");
goto err_put; goto error;
} }
/* store basic data: */ line6_get_interval(line6);
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->driver, DRIVER_NAME);
strcpy(card->shortname, line6->properties->name);
sprintf(card->longname, "Line 6 %s at USB %s", line6->properties->name,
dev_name(line6->ifcdev));
card->private_data = line6;
card->private_free = line6_destruct;
usb_set_intfdata(interface, line6);
/* increment reference counters: */
usb_get_dev(usbdev);
if (properties->capabilities & LINE6_CAP_CONTROL) { if (properties->capabilities & LINE6_CAP_CONTROL) {
/* initialize USB buffers: */ ret = line6_init_cap_control(line6);
line6->buffer_listen = if (ret < 0)
kmalloc(LINE6_BUFSIZE_LISTEN, GFP_KERNEL); goto error;
if (line6->buffer_listen == NULL) {
ret = -ENOMEM;
goto err_destruct;
}
line6->buffer_message =
kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
if (line6->buffer_message == NULL) {
ret = -ENOMEM;
goto err_destruct;
}
line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
if (line6->urb_listen == NULL) {
ret = -ENOMEM;
goto err_destruct;
}
ret = line6_start_listen(line6);
if (ret < 0) {
dev_err(&interface->dev, "%s: usb_submit_urb failed\n",
__func__);
goto err_destruct;
}
} }
/* initialize device data based on device: */ /* initialize device data based on device: */
ret = private_init(interface, line6); ret = private_init(line6, id);
if (ret < 0) if (ret < 0)
goto err_destruct; goto error;
/* creation of additional special files should go here */ /* creation of additional special files should go here */
dev_info(&interface->dev, "Line 6 %s now attached\n", dev_info(&interface->dev, "Line 6 %s now attached\n",
line6->properties->name); properties->name);
return 0; return 0;
err_destruct: error:
if (line6->disconnect)
line6->disconnect(line6);
snd_card_free(card); snd_card_free(card);
err_put:
return ret; return ret;
} }
EXPORT_SYMBOL_GPL(line6_probe); EXPORT_SYMBOL_GPL(line6_probe);
@ -579,32 +570,23 @@ EXPORT_SYMBOL_GPL(line6_probe);
*/ */
void line6_disconnect(struct usb_interface *interface) void line6_disconnect(struct usb_interface *interface)
{ {
struct usb_line6 *line6; struct usb_line6 *line6 = usb_get_intfdata(interface);
struct usb_device *usbdev; struct usb_device *usbdev = interface_to_usbdev(interface);
int interface_number;
if (interface == NULL)
return;
usbdev = interface_to_usbdev(interface);
if (usbdev == NULL)
return;
interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
line6 = usb_get_intfdata(interface);
if (!line6) if (!line6)
return; return;
if (WARN_ON(usbdev != line6->usbdev))
return;
if (line6->urb_listen != NULL) if (line6->urb_listen != NULL)
line6_stop_listen(line6); line6_stop_listen(line6);
if (usbdev != line6->usbdev)
dev_err(line6->ifcdev, "driver bug: inconsistent usb device\n");
snd_card_disconnect(line6->card); snd_card_disconnect(line6->card);
if (line6->line6pcm) if (line6->line6pcm)
line6_pcm_disconnect(line6->line6pcm); line6_pcm_disconnect(line6->line6pcm);
if (line6->disconnect) if (line6->disconnect)
line6->disconnect(interface); line6->disconnect(line6);
dev_info(&interface->dev, "Line 6 %s now disconnected\n", dev_info(&interface->dev, "Line 6 %s now disconnected\n",
line6->properties->name); line6->properties->name);

View File

@ -157,13 +157,11 @@ struct usb_line6 {
int message_length; int message_length;
void (*process_message)(struct usb_line6 *); 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, extern char *line6_alloc_sysex_buffer(struct usb_line6 *line6, int code1,
int code2, int size); 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, extern int line6_read_data(struct usb_line6 *line6, int address, void *data,
size_t datalen); size_t datalen);
extern int line6_read_serial_number(struct usb_line6 *line6, 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); size_t datalen);
int line6_probe(struct usb_interface *interface, int line6_probe(struct usb_interface *interface,
struct usb_line6 *line6, const struct usb_device_id *id,
const struct line6_properties *properties, 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); void line6_disconnect(struct usb_interface *interface);
#ifdef CONFIG_PM #ifdef CONFIG_PM

View File

@ -45,12 +45,9 @@ static void line6_midi_transmit(struct snd_rawmidi_substream *substream)
line6_rawmidi_substream_midi(substream)->line6; line6_rawmidi_substream_midi(substream)->line6;
struct snd_line6_midi *line6midi = line6->line6midi; struct snd_line6_midi *line6midi = line6->line6midi;
struct midi_buffer *mb = &line6midi->midibuf_out; struct midi_buffer *mb = &line6midi->midibuf_out;
unsigned long flags;
unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE]; unsigned char chunk[LINE6_FALLBACK_MAXPACKETSIZE];
int req, done; int req, done;
spin_lock_irqsave(&line6->line6midi->midi_transmit_lock, flags);
for (;;) { for (;;) {
req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size); req = min(line6_midibuf_bytes_free(mb), line6->max_packet_size);
done = snd_rawmidi_transmit_peek(substream, chunk, req); 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); 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) if (status == -ESHUTDOWN)
return; return;
spin_lock_irqsave(&line6->line6midi->send_urb_lock, flags); spin_lock_irqsave(&line6->line6midi->lock, flags);
num = --line6->line6midi->num_active_send_urbs; num = --line6->line6midi->num_active_send_urbs;
if (num == 0) { if (num == 0) {
@ -103,12 +98,12 @@ static void midi_sent(struct urb *urb)
if (num == 0) if (num == 0)
wake_up(&line6->line6midi->send_wait); 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. 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). (i.e., this function is serialized).
*/ */
static int send_midi_async(struct usb_line6 *line6, unsigned char *data, 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_rawmidi_substream_midi(substream)->line6;
line6->line6midi->substream_transmit = substream; 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) if (line6->line6midi->num_active_send_urbs == 0)
line6_midi_transmit(substream); 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) 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; rmidi->private_free = snd_line6_midi_free;
init_waitqueue_head(&line6midi->send_wait); init_waitqueue_head(&line6midi->send_wait);
spin_lock_init(&line6midi->send_urb_lock); spin_lock_init(&line6midi->lock);
spin_lock_init(&line6midi->midi_transmit_lock);
line6midi->line6 = line6; line6midi->line6 = line6;
err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0); err = line6_midibuf_init(&line6midi->midibuf_in, MIDI_BUFFER_SIZE, 0);

View File

@ -39,15 +39,10 @@ struct snd_line6_midi {
*/ */
int num_active_send_urbs; 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. Spin lock to protect MIDI buffer handling.
*/ */
spinlock_t midi_transmit_lock; spinlock_t lock;
/** /**
Wait queue for MIDI transmission. Wait queue for MIDI transmission.

View File

@ -45,15 +45,22 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
{ {
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
int value = ucontrol->value.integer.value[0]; int value = ucontrol->value.integer.value[0];
int err;
if (line6pcm->impulse_volume == value) if (line6pcm->impulse_volume == value)
return 0; return 0;
line6pcm->impulse_volume = value; line6pcm->impulse_volume = value;
if (value > 0) if (value > 0) {
line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_IMPULSE); err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
else if (err < 0) {
line6_pcm_release(line6pcm, LINE6_BITS_PCM_IMPULSE); line6pcm->impulse_volume = 0;
line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
return err;
}
} else {
line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
}
return 1; return 1;
} }
@ -90,180 +97,277 @@ static int snd_line6_impulse_period_put(struct snd_kcontrol *kcontrol,
return 1; return 1;
} }
static bool test_flags(unsigned long flags0, unsigned long flags1, /*
unsigned long mask) Unlink all currently active URBs.
*/
static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
struct line6_pcm_stream *pcms)
{ {
return ((flags0 & mask) == 0) && ((flags1 & mask) != 0); int i;
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]);
}
}
} }
int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int channels) /*
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)
{ {
unsigned long flags_old, flags_new, flags_final; int timeout = HZ;
int err; int i;
int alive;
do { do {
flags_old = ACCESS_ONCE(line6pcm->flags); alive = 0;
flags_new = flags_old | channels; for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
} while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); if (test_bit(i, &pcms->active_urbs))
alive++;
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;
} }
} if (!alive)
break;
if (test_flags(flags_old, flags_new, LINE6_BITS_CAPTURE_STREAM)) { set_current_state(TASK_UNINTERRUPTIBLE);
/* schedule_timeout(1);
Waiting for completion of active URBs in the stop handler is } while (--timeout > 0);
a bug, we therefore report an error if capturing is restarted if (alive)
too soon. dev_err(line6pcm->line6->ifcdev,
*/ "timeout: still %d active urbs..\n", alive);
if (line6pcm->active_urb_in | line6pcm->unlink_urb_in) {
dev_err(line6pcm->line6->ifcdev, "Device not yet ready\n");
return -EBUSY;
}
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;
}
}
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;
}
line6pcm->count_out = 0;
err = line6_submit_audio_out_all_urbs(line6pcm);
if (err < 0)
goto pcm_acquire_error;
flags_final |= channels & LINE6_BITS_PLAYBACK_STREAM;
}
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) static inline struct line6_pcm_stream *
get_stream(struct snd_line6_pcm *line6pcm, int direction)
{ {
unsigned long flags_old, flags_new; return (direction == SNDRV_PCM_STREAM_PLAYBACK) ?
&line6pcm->out : &line6pcm->in;
}
do { /* allocate a buffer if not opened yet;
flags_old = ACCESS_ONCE(line6pcm->flags); * call this in line6pcm.state_change mutex
flags_new = flags_old & ~channels; */
} while (cmpxchg(&line6pcm->flags, flags_old, flags_new) != flags_old); static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
struct line6_pcm_stream *pstr, int type)
if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_STREAM)) {
line6_unlink_audio_in_urbs(line6pcm); /* Invoked multiple times in a row so allocate once only */
if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) {
if (test_flags(flags_new, flags_old, LINE6_BITS_CAPTURE_BUFFER)) { pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
line6_wait_clear_audio_in_urbs(line6pcm); line6pcm->max_packet_size, GFP_KERNEL);
line6_free_capture_buffer(line6pcm); if (!pstr->buffer)
return -ENOMEM;
} }
if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_STREAM))
line6_unlink_audio_out_urbs(line6pcm);
if (test_flags(flags_new, flags_old, LINE6_BITS_PLAYBACK_BUFFER)) {
line6_wait_clear_audio_out_urbs(line6pcm);
line6_free_playback_buffer(line6pcm);
}
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(line6_pcm_release);
/* trigger callback */ /* 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)
{
clear_bit(type, &pstr->opened);
if (!pstr->opened) {
line6_wait_clear_audio_urbs(line6pcm, pstr);
kfree(pstr->buffer);
pstr->buffer = NULL;
}
}
/* 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;
}
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;
}
/* 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);
}
/* common PCM trigger callback */
int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd) int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s; struct snd_pcm_substream *s;
int err; int err;
spin_lock(&line6pcm->lock_trigger); clear_bit(LINE6_FLAG_PREPARED, &line6pcm->flags);
clear_bit(LINE6_INDEX_PREPARED, &line6pcm->flags);
snd_pcm_group_for_each_entry(s, substream) { snd_pcm_group_for_each_entry(s, substream) {
if (s->pcm->card != substream->pcm->card) if (s->pcm->card != substream->pcm->card)
continue; continue;
switch (s->stream) {
case SNDRV_PCM_STREAM_PLAYBACK:
err = snd_line6_playback_trigger(line6pcm, cmd);
if (err < 0) { switch (cmd) {
spin_unlock(&line6pcm->lock_trigger); 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; return err;
}
break; break;
case SNDRV_PCM_STREAM_CAPTURE: case SNDRV_PCM_TRIGGER_STOP:
err = snd_line6_capture_trigger(line6pcm, cmd); case SNDRV_PCM_TRIGGER_SUSPEND:
line6_stream_stop(line6pcm, s->stream,
LINE6_STREAM_PCM);
break;
if (err < 0) { case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
spin_unlock(&line6pcm->lock_trigger); if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
return err; 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; break;
default: default:
dev_err(line6pcm->line6->ifcdev, return -EINVAL;
"Unknown stream direction %d\n", s->stream);
} }
} }
spin_unlock(&line6pcm->lock_trigger);
return 0; 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 */ /* control info callback */
static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol, static int snd_line6_control_playback_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo) struct snd_ctl_elem_info *uinfo)
@ -282,7 +386,7 @@ static int snd_line6_control_playback_get(struct snd_kcontrol *kcontrol,
int i; int i;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); 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]; ucontrol->value.integer.value[i] = line6pcm->volume_playback[i];
return 0; return 0;
@ -295,7 +399,7 @@ static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
int i, changed = 0; int i, changed = 0;
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
for (i = 2; i--;) for (i = 0; i < 2; i++)
if (line6pcm->volume_playback[i] != if (line6pcm->volume_playback[i] !=
ucontrol->value.integer.value[i]) { ucontrol->value.integer.value[i]) {
line6pcm->volume_playback[i] = line6pcm->volume_playback[i] =
@ -334,21 +438,24 @@ static struct snd_kcontrol_new line6_controls[] = {
/* /*
Cleanup the PCM device. Cleanup the PCM device.
*/ */
static void line6_cleanup_pcm(struct snd_pcm *pcm) static void cleanup_urbs(struct line6_pcm_stream *pcms)
{ {
int i; int i;
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
for (i = LINE6_ISO_BUFFERS; i--;) { for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
if (line6pcm->urb_audio_out[i]) { if (pcms->urbs[i]) {
usb_kill_urb(line6pcm->urb_audio_out[i]); usb_kill_urb(pcms->urbs[i]);
usb_free_urb(line6pcm->urb_audio_out[i]); usb_free_urb(pcms->urbs[i]);
}
if (line6pcm->urb_audio_in[i]) {
usb_kill_urb(line6pcm->urb_audio_in[i]);
usb_free_urb(line6pcm->urb_audio_in[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); 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) void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm)
{ {
line6_unlink_wait_clear_audio_out_urbs(line6pcm); line6_unlink_audio_urbs(line6pcm, &line6pcm->out);
line6_unlink_wait_clear_audio_in_urbs(line6pcm); 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) if (!line6pcm)
return -ENOMEM; return -ENOMEM;
mutex_init(&line6pcm->state_mutex);
line6pcm->pcm = pcm; line6pcm->pcm = pcm;
line6pcm->properties = properties; line6pcm->properties = properties;
line6pcm->volume_playback[0] = line6pcm->volume_playback[1] = 255; 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_maxpacket(line6->usbdev,
usb_sndisocpipe(line6->usbdev, ep_write), 1)); usb_sndisocpipe(line6->usbdev, ep_write), 1));
spin_lock_init(&line6pcm->lock_audio_out); spin_lock_init(&line6pcm->out.lock);
spin_lock_init(&line6pcm->lock_audio_in); spin_lock_init(&line6pcm->in.lock);
spin_lock_init(&line6pcm->lock_trigger);
line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD; line6pcm->impulse_period = LINE6_IMPULSE_DEFAULT_PERIOD;
line6->line6pcm = line6pcm; line6->line6pcm = line6pcm;
@ -458,30 +567,22 @@ EXPORT_SYMBOL_GPL(line6_init_pcm);
int snd_line6_prepare(struct snd_pcm_substream *substream) int snd_line6_prepare(struct snd_pcm_substream *substream)
{ {
struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
switch (substream->stream) { mutex_lock(&line6pcm->state_mutex);
case SNDRV_PCM_STREAM_PLAYBACK: if (!pstr->running)
if ((line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM) == 0) line6_wait_clear_audio_urbs(line6pcm, pstr);
line6_unlink_wait_clear_audio_out_urbs(line6pcm);
break; if (!test_and_set_bit(LINE6_FLAG_PREPARED, &line6pcm->flags)) {
line6pcm->out.count = 0;
case SNDRV_PCM_STREAM_CAPTURE: line6pcm->out.pos = 0;
if ((line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) == 0) line6pcm->out.pos_done = 0;
line6_unlink_wait_clear_audio_in_urbs(line6pcm); line6pcm->out.bytes = 0;
line6pcm->in.count = 0;
break; line6pcm->in.pos_done = 0;
} line6pcm->in.bytes = 0;
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;
} }
mutex_unlock(&line6pcm->state_mutex);
return 0; return 0;
} }

View File

@ -54,109 +54,33 @@
However, from the device's point of view, there is just a single However, from the device's point of view, there is just a single
capture and playback stream, which must be shared between these capture and playback stream, which must be shared between these
subsystems. It is therefore necessary to maintain the state of the subsystems. It is therefore necessary to maintain the state of the
subsystems with respect to PCM usage. We define several constants of subsystems with respect to PCM usage.
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) We define two bit flags, "opened" and "running", for each playback
resources needed for a particular operation (e.g., allocate the buffer or capture stream. Both can contain the bit flag corresponding to
for ALSA playback or start the capture stream for software monitoring). LINE6_STREAM_* type,
When a resource is no longer needed, it is released by calling LINE6_STREAM_PCM = ALSA PCM playback or capture
line6_pcm_release(). Buffer allocation and stream startup are handled LINE6_STREAM_MONITOR = software monitoring
separately to allow the ALSA kernel driver to perform them at IMPULSE = optional impulse response measurement
appropriate places (since the callback which starts a PCM stream is not The opened flag indicates whether the buffer is allocated while
allowed to sleep). 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 { enum {
/* individual bit indices: */ LINE6_STREAM_PCM,
LINE6_INDEX_PCM_ALSA_PLAYBACK_BUFFER, LINE6_STREAM_MONITOR,
LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, LINE6_STREAM_IMPULSE,
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 /* misc bit flags for PCM operation */
enum {
/* individual bit masks: */ LINE6_FLAG_PAUSE_PLAYBACK,
LINE6_BIT(PCM_ALSA_PLAYBACK_BUFFER), LINE6_FLAG_PREPARED,
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
}; };
struct line6_pcm_properties { struct line6_pcm_properties {
@ -165,6 +89,55 @@ struct line6_pcm_properties {
int bytes_per_frame; int bytes_per_frame;
}; };
struct line6_pcm_stream {
/* allocated URBs */
struct urb *urbs[LINE6_ISO_BUFFERS];
/* Temporary buffer;
* Since the packet size is not known in advance, this buffer is
* large enough to store maximum size packets.
*/
unsigned char *buffer;
/* Free frame position in the buffer. */
snd_pcm_uframes_t pos;
/* Count processed bytes;
* This is modulo period size (to determine when a period is finished).
*/
unsigned bytes;
/* Counter to create desired sample rate */
unsigned count;
/* period size in bytes */
unsigned period;
/* 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;
/* Bit mask of active URBs */
unsigned long active_urbs;
/* Bit mask of URBs currently being unlinked */
unsigned long unlink_urbs;
/* Spin lock to protect updates of the buffer positions (not contents)
*/
spinlock_t lock;
/* Bit flags for operational stream types */
unsigned long opened;
/* Bit flags for running stream types */
unsigned long running;
int last_frame;
};
struct snd_line6_pcm { struct snd_line6_pcm {
/** /**
Pointer back to the Line 6 driver data structure. Pointer back to the Line 6 driver data structure.
@ -181,29 +154,12 @@ struct snd_line6_pcm {
*/ */
struct snd_pcm *pcm; struct snd_pcm *pcm;
/** /* protection to state changes of in/out streams */
URBs for audio playback. struct mutex state_mutex;
*/
struct urb *urb_audio_out[LINE6_ISO_BUFFERS];
/** /* Capture and playback streams */
URBs for audio capture. struct line6_pcm_stream in;
*/ struct line6_pcm_stream out;
struct urb *urb_audio_in[LINE6_ISO_BUFFERS];
/**
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;
/**
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;
/** /**
Previously captured frame (for software monitoring). Previously captured frame (for software monitoring).
@ -215,103 +171,11 @@ struct snd_line6_pcm {
*/ */
int prev_fsize; int prev_fsize;
/**
Free frame position in the playback buffer.
*/
snd_pcm_uframes_t pos_out;
/**
Count processed bytes for playback.
This is modulo period size (to determine when a period is
finished).
*/
unsigned bytes_out;
/**
Counter to create desired playback sample rate.
*/
unsigned count_out;
/**
Playback period size in bytes
*/
unsigned period_out;
/**
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.
*/
snd_pcm_uframes_t pos_out_done;
/**
Count processed bytes for capture.
This is modulo period size (to determine when a period is
finished).
*/
unsigned bytes_in;
/**
Counter to create desired capture sample rate.
*/
unsigned count_in;
/**
Capture period size in bytes
*/
unsigned period_in;
/**
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.
*/
snd_pcm_uframes_t pos_in_done;
/**
Bit mask of active playback URBs.
*/
unsigned long active_urb_out;
/** /**
Maximum size of USB packet. Maximum size of USB packet.
*/ */
int max_packet_size; 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). PCM playback volume (left and right).
*/ */
@ -338,19 +202,21 @@ struct snd_line6_pcm {
int impulse_count; int impulse_count;
/** /**
Several status bits (see LINE6_BIT_*). Several status bits (see LINE6_FLAG_*).
*/ */
unsigned long flags; unsigned long flags;
int last_frame_in, last_frame_out;
}; };
extern int line6_init_pcm(struct usb_line6 *line6, extern int line6_init_pcm(struct usb_line6 *line6,
struct line6_pcm_properties *properties); struct line6_pcm_properties *properties);
extern int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd); 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_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 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_acquire(struct snd_line6_pcm *line6pcm, int type);
extern int line6_pcm_release(struct snd_line6_pcm *line6pcm, int channels); extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
#endif #endif

View File

@ -37,7 +37,8 @@ static void change_volume(struct urb *urb_out, int volume[],
buf_end = p + urb_out->transfer_buffer_length / sizeof(*p); buf_end = p + urb_out->transfer_buffer_length / sizeof(*p);
for (; p < buf_end; ++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; ++chn;
} }
} else if (bytes_per_frame == 6) { } 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 = p[0] + (p[1] << 8) + ((signed char)p[2] << 16);
val = (val * volume[chn & 1]) >> 8; val = (val * volume[chn & 1]) >> 8;
val = clamp(val, 0x7fffff, -0x800000);
p[0] = val; p[0] = val;
p[1] = val >> 8; p[1] = val >> 8;
p[2] = val >> 16; 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; po = (short *)urb_out->transfer_buffer;
buf_end = po + urb_out->transfer_buffer_length / sizeof(*po); buf_end = po + urb_out->transfer_buffer_length / sizeof(*po);
for (; po < buf_end; ++pi, ++po) for (; po < buf_end; ++pi, ++po) {
*po += (*pi * volume) >> 8; 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. 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) static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
{ {
int index; int index;
unsigned long flags;
int i, urb_size, urb_frames; int i, urb_size, urb_frames;
int ret; int ret;
const int bytes_per_frame = line6pcm->properties->bytes_per_frame; 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); (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
struct urb *urb_out; struct urb *urb_out;
spin_lock_irqsave(&line6pcm->lock_audio_out, flags);
index = 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) { 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"); dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL; return -EINVAL;
} }
urb_out = line6pcm->urb_audio_out[index]; urb_out = line6pcm->out.urbs[index];
urb_size = 0; urb_size = 0;
for (i = 0; i < LINE6_ISO_PACKETS; ++i) { 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 = struct usb_iso_packet_descriptor *fout =
&urb_out->iso_frame_desc[i]; &urb_out->iso_frame_desc[i];
if (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM) fsize = line6pcm->prev_fsize;
fsize = line6pcm->prev_fsize;
if (fsize == 0) { if (fsize == 0) {
int n; int n;
line6pcm->count_out += frame_increment; line6pcm->out.count += frame_increment;
n = line6pcm->count_out / frame_factor; n = line6pcm->out.count / frame_factor;
line6pcm->count_out -= n * frame_factor; line6pcm->out.count -= n * frame_factor;
fsize = n * bytes_per_frame; fsize = n * bytes_per_frame;
} }
@ -183,36 +183,35 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
if (urb_size == 0) { if (urb_size == 0) {
/* can't determine URB size */ /* can't determine URB size */
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n"); dev_err(line6pcm->line6->ifcdev, "driver bug: urb_size = 0\n");
return -EINVAL; return -EINVAL;
} }
urb_frames = urb_size / bytes_per_frame; urb_frames = urb_size / bytes_per_frame;
urb_out->transfer_buffer = urb_out->transfer_buffer =
line6pcm->buffer_out + line6pcm->out.buffer +
index * LINE6_ISO_PACKETS * line6pcm->max_packet_size; index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
urb_out->transfer_buffer_length = urb_size; urb_out->transfer_buffer_length = urb_size;
urb_out->context = line6pcm; urb_out->context = line6pcm;
if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, &line6pcm->flags) && if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running) &&
!test_bit(LINE6_INDEX_PAUSE_PLAYBACK, &line6pcm->flags)) { !test_bit(LINE6_FLAG_PAUSE_PLAYBACK, &line6pcm->flags)) {
struct snd_pcm_runtime *runtime = struct snd_pcm_runtime *runtime =
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK)->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, The transferred area goes over buffer boundary,
copy the data to the temp buffer. copy the data to the temp buffer.
*/ */
int len; int len;
len = runtime->buffer_size - line6pcm->pos_out; len = runtime->buffer_size - line6pcm->out.pos;
if (len > 0) { if (len > 0) {
memcpy(urb_out->transfer_buffer, memcpy(urb_out->transfer_buffer,
runtime->dma_area + runtime->dma_area +
line6pcm->pos_out * bytes_per_frame, line6pcm->out.pos * bytes_per_frame,
len * bytes_per_frame); len * bytes_per_frame);
memcpy(urb_out->transfer_buffer + memcpy(urb_out->transfer_buffer +
len * bytes_per_frame, runtime->dma_area, len * bytes_per_frame, runtime->dma_area,
@ -223,26 +222,27 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
} else { } else {
memcpy(urb_out->transfer_buffer, memcpy(urb_out->transfer_buffer,
runtime->dma_area + runtime->dma_area +
line6pcm->pos_out * bytes_per_frame, line6pcm->out.pos * bytes_per_frame,
urb_out->transfer_buffer_length); urb_out->transfer_buffer_length);
} }
line6pcm->pos_out += urb_frames; line6pcm->out.pos += urb_frames;
if (line6pcm->pos_out >= runtime->buffer_size) if (line6pcm->out.pos >= runtime->buffer_size)
line6pcm->pos_out -= runtime->buffer_size; line6pcm->out.pos -= runtime->buffer_size;
change_volume(urb_out, line6pcm->volume_playback,
bytes_per_frame);
} else { } else {
memset(urb_out->transfer_buffer, 0, memset(urb_out->transfer_buffer, 0,
urb_out->transfer_buffer_length); urb_out->transfer_buffer_length);
} }
change_volume(urb_out, line6pcm->volume_playback, bytes_per_frame); spin_lock_nested(&line6pcm->in.lock, SINGLE_DEPTH_NESTING);
if (line6pcm->prev_fbuf) {
if (line6pcm->prev_fbuf != NULL) { if (test_bit(LINE6_STREAM_IMPULSE, &line6pcm->out.running)) {
if (line6pcm->flags & LINE6_BITS_PCM_IMPULSE) {
create_impulse_test_signal(line6pcm, urb_out, create_impulse_test_signal(line6pcm, urb_out,
bytes_per_frame); bytes_per_frame);
if (line6pcm->flags & if (test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) {
LINE6_BIT_PCM_ALSA_CAPTURE_STREAM) {
line6_capture_copy(line6pcm, line6_capture_copy(line6pcm,
urb_out->transfer_buffer, urb_out->transfer_buffer,
urb_out-> urb_out->
@ -251,101 +251,43 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out->transfer_buffer_length); urb_out->transfer_buffer_length);
} }
} else { } else {
if (! if (!(line6pcm->line6->properties->capabilities & LINE6_CAP_HWMON)
(line6pcm->line6-> && line6pcm->out.running && line6pcm->in.running)
properties->capabilities & LINE6_CAP_HWMON)
&& (line6pcm->flags & LINE6_BITS_PLAYBACK_STREAM)
&& (line6pcm->flags & LINE6_BITS_CAPTURE_STREAM))
add_monitor_signal(urb_out, line6pcm->prev_fbuf, add_monitor_signal(urb_out, line6pcm->prev_fbuf,
line6pcm->volume_monitor, line6pcm->volume_monitor,
bytes_per_frame); bytes_per_frame);
} }
line6pcm->prev_fbuf = NULL;
line6pcm->prev_fsize = 0;
} }
spin_unlock(&line6pcm->in.lock);
ret = usb_submit_urb(urb_out, GFP_ATOMIC); ret = usb_submit_urb(urb_out, GFP_ATOMIC);
if (ret == 0) if (ret == 0)
set_bit(index, &line6pcm->active_urb_out); set_bit(index, &line6pcm->out.active_urbs);
else else
dev_err(line6pcm->line6->ifcdev, dev_err(line6pcm->line6->ifcdev,
"URB out #%d submission failed (%d)\n", index, ret); "URB out #%d submission failed (%d)\n", index, ret);
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
return 0; return 0;
} }
/* /*
Submit all currently available playback URBs. 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 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) { for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
ret = submit_audio_out_urb(line6pcm); ret = submit_audio_out_urb(line6pcm);
if (ret < 0) 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; 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);
}
/* return ret;
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;
} }
/* /*
@ -363,56 +305,56 @@ static void audio_out_callback(struct urb *urb)
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length); memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
#endif #endif
line6pcm->last_frame_out = urb->start_frame; line6pcm->out.last_frame = urb->start_frame;
/* find index of URB */ /* find index of URB */
for (index = LINE6_ISO_BUFFERS; index--;) for (index = 0; index < LINE6_ISO_BUFFERS; index++)
if (urb == line6pcm->urb_audio_out[index]) if (urb == line6pcm->out.urbs[index])
break; break;
if (index < 0) if (index >= LINE6_ISO_BUFFERS)
return; /* URB has been unlinked asynchronously */ 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; 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; struct snd_pcm_runtime *runtime = substream->runtime;
line6pcm->pos_out_done += line6pcm->out.pos_done +=
length / line6pcm->properties->bytes_per_frame; length / line6pcm->properties->bytes_per_frame;
if (line6pcm->pos_out_done >= runtime->buffer_size) if (line6pcm->out.pos_done >= runtime->buffer_size)
line6pcm->pos_out_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) { if (urb->iso_frame_desc[i].status == -EXDEV) {
shutdown = 1; shutdown = 1;
break; break;
} }
if (test_and_clear_bit(index, &line6pcm->unlink_urb_out)) if (test_and_clear_bit(index, &line6pcm->out.unlink_urbs))
shutdown = 1; shutdown = 1;
spin_unlock_irqrestore(&line6pcm->lock_audio_out, flags);
if (!shutdown) { if (!shutdown) {
submit_audio_out_urb(line6pcm); submit_audio_out_urb(line6pcm);
if (test_bit(LINE6_INDEX_PCM_ALSA_PLAYBACK_STREAM, if (test_bit(LINE6_STREAM_PCM, &line6pcm->out.running)) {
&line6pcm->flags)) { line6pcm->out.bytes += length;
line6pcm->bytes_out += length; if (line6pcm->out.bytes >= line6pcm->out.period) {
if (line6pcm->bytes_out >= line6pcm->period_out) { line6pcm->out.bytes %= line6pcm->out.period;
line6pcm->bytes_out %= line6pcm->period_out; spin_unlock(&line6pcm->out.lock);
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
spin_lock(&line6pcm->out.lock);
} }
} }
} }
spin_unlock_irqrestore(&line6pcm->out.lock, flags);
} }
/* open playback callback */ /* open playback callback */
@ -438,110 +380,16 @@ static int snd_line6_playback_close(struct snd_pcm_substream *substream)
return 0; 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 */ /* playback operators */
struct snd_pcm_ops snd_line6_playback_ops = { struct snd_pcm_ops snd_line6_playback_ops = {
.open = snd_line6_playback_open, .open = snd_line6_playback_open,
.close = snd_line6_playback_close, .close = snd_line6_playback_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.hw_params = snd_line6_playback_hw_params, .hw_params = snd_line6_hw_params,
.hw_free = snd_line6_playback_hw_free, .hw_free = snd_line6_hw_free,
.prepare = snd_line6_prepare, .prepare = snd_line6_prepare,
.trigger = snd_line6_trigger, .trigger = snd_line6_trigger,
.pointer = snd_line6_playback_pointer, .pointer = snd_line6_pointer,
}; };
int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm) 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; struct urb *urb;
/* URB for audio out: */ /* URB for audio out: */
urb = line6pcm->urb_audio_out[i] = urb = line6pcm->out.urbs[i] =
usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL);
if (urb == NULL) if (urb == NULL)

View File

@ -30,12 +30,6 @@
extern struct snd_pcm_ops snd_line6_playback_ops; extern struct snd_pcm_ops snd_line6_playback_ops;
extern int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm); 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 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 #endif

View File

@ -399,27 +399,18 @@ static struct snd_kcontrol_new pod_control_monitor = {
/* /*
POD device disconnected. 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) /* remove sysfs entries: */
return; device_remove_file(dev, &dev_attr_device_id);
pod = usb_get_intfdata(interface); device_remove_file(dev, &dev_attr_firmware_version);
device_remove_file(dev, &dev_attr_serial_number);
if (pod != NULL) { del_timer_sync(&pod->startup_timer);
struct device *dev = &interface->dev; cancel_work_sync(&pod->startup_work);
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);
}
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. Try to init POD device.
*/ */
static int pod_init(struct usb_interface *interface, static int pod_init(struct usb_line6 *line6,
struct usb_line6 *line6) const struct usb_device_id *id)
{ {
int err; int err;
struct usb_line6_pod *pod = (struct usb_line6_pod *) line6; 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_timer(&pod->startup_timer);
INIT_WORK(&pod->startup_work, pod_startup4); INIT_WORK(&pod->startup_work, pod_startup4);
if ((interface == NULL) || (pod == NULL))
return -ENODEV;
/* create sysfs entries: */ /* create sysfs entries: */
err = pod_create_files2(&interface->dev); err = pod_create_files2(line6->ifcdev);
if (err < 0) if (err < 0)
return err; return err;
@ -603,14 +591,9 @@ static const struct line6_properties pod_properties_table[] = {
static int pod_probe(struct usb_interface *interface, static int pod_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_line6_pod *pod; return line6_probe(interface, id,
pod = kzalloc(sizeof(*pod), GFP_KERNEL);
if (!pod)
return -ENODEV;
return line6_probe(interface, &pod->line6,
&pod_properties_table[id->driver_info], &pod_properties_table[id->driver_info],
pod_init); pod_init, sizeof(struct usb_line6_pod));
} }
static struct usb_driver pod_driver = { static struct usb_driver pod_driver = {

View File

@ -87,15 +87,11 @@ static struct line6_pcm_properties podhd_pcm_properties = {
/* /*
Try to init POD HD device. Try to init POD HD device.
*/ */
static int podhd_init(struct usb_interface *interface, static int podhd_init(struct usb_line6 *line6,
struct usb_line6 *line6) const struct usb_device_id *id)
{ {
struct usb_line6_podhd *podhd = (struct usb_line6_podhd *) line6;
int err; int err;
if ((interface == NULL) || (podhd == NULL))
return -ENODEV;
/* initialize MIDI subsystem: */ /* initialize MIDI subsystem: */
err = line6_init_midi(line6); err = line6_init_midi(line6);
if (err < 0) if (err < 0)
@ -181,14 +177,9 @@ static const struct line6_properties podhd_properties_table[] = {
static int podhd_probe(struct usb_interface *interface, static int podhd_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_line6_podhd *podhd; return line6_probe(interface, id,
podhd = kzalloc(sizeof(*podhd), GFP_KERNEL);
if (!podhd)
return -ENODEV;
return line6_probe(interface, &podhd->line6,
&podhd_properties_table[id->driver_info], &podhd_properties_table[id->driver_info],
podhd_init); podhd_init, sizeof(struct usb_line6_podhd));
} }
static struct usb_driver podhd_driver = { static struct usb_driver podhd_driver = {

View File

@ -14,6 +14,7 @@
#include <linux/usb.h> #include <linux/usb.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/leds.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/control.h> #include <sound/control.h>
@ -32,6 +33,15 @@ enum line6_device_type {
LINE6_TONEPORT_UX2, 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 { struct usb_line6_toneport {
/** /**
Generic Line 6 USB data. Generic Line 6 USB data.
@ -62,6 +72,9 @@ struct usb_line6_toneport {
Device type. Device type.
*/ */
enum line6_device_type 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); 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 .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 { static const struct {
const char *name; const char *name;
int code; int code;
@ -136,62 +140,6 @@ static const struct {
{"Inst & Mic", 0x0901} {"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) static int toneport_send_cmd(struct usb_device *usbdev, int cmd1, int cmd2)
{ {
int ret; int ret;
@ -234,16 +182,23 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol); struct snd_line6_pcm *line6pcm = snd_kcontrol_chip(kcontrol);
int err;
if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor) if (ucontrol->value.integer.value[0] == line6pcm->volume_monitor)
return 0; return 0;
line6pcm->volume_monitor = ucontrol->value.integer.value[0]; line6pcm->volume_monitor = ucontrol->value.integer.value[0];
if (line6pcm->volume_monitor > 0) if (line6pcm->volume_monitor > 0) {
line6_pcm_acquire(line6pcm, LINE6_BITS_PCM_MONITOR); err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
else if (err < 0) {
line6_pcm_release(line6pcm, LINE6_BITS_PCM_MONITOR); line6pcm->volume_monitor = 0;
line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
return err;
}
} else {
line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
}
return 1; 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_toneport *toneport = (struct usb_line6_toneport *)arg;
struct usb_line6 *line6 = &toneport->line6; 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 */ /* control definition */
@ -329,6 +284,78 @@ static struct snd_kcontrol_new toneport_control_source = {
.put = snd_toneport_source_put .put = snd_toneport_source_put
}; };
/*
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. Setup Toneport device.
*/ */
@ -359,42 +386,38 @@ static void toneport_setup(struct usb_line6_toneport *toneport)
} }
if (toneport_has_led(toneport->type)) 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. 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; struct usb_line6_toneport *toneport =
u16 idProduct; (struct usb_line6_toneport *)line6;
if (interface == NULL)
return;
toneport = usb_get_intfdata(interface);
del_timer_sync(&toneport->timer); del_timer_sync(&toneport->timer);
idProduct = le16_to_cpu(toneport->line6.usbdev->descriptor.idProduct);
if (toneport_has_led(idProduct)) { if (toneport_has_led(toneport->type))
device_remove_file(&interface->dev, &dev_attr_led_red); toneport_remove_leds(toneport);
device_remove_file(&interface->dev, &dev_attr_led_green);
}
} }
/* /*
Try to init Toneport device. Try to init Toneport device.
*/ */
static int toneport_init(struct usb_interface *interface, static int toneport_init(struct usb_line6 *line6,
struct usb_line6 *line6) const struct usb_device_id *id)
{ {
int err; int err;
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6; struct usb_line6_toneport *toneport = (struct usb_line6_toneport *) line6;
if ((interface == NULL) || (toneport == NULL)) toneport->type = id->driver_info;
return -ENODEV; setup_timer(&toneport->timer, toneport_start_pcm,
(unsigned long)toneport);
line6->disconnect = line6_toneport_disconnect; 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); line6_read_data(line6, 0x80c2, &toneport->firmware_version, 1);
if (toneport_has_led(toneport->type)) { if (toneport_has_led(toneport->type)) {
err = device_create_file(&interface->dev, &dev_attr_led_red); err = toneport_init_leds(toneport);
if (err < 0)
return err;
err = device_create_file(&interface->dev, &dev_attr_led_green);
if (err < 0) if (err < 0)
return err; return err;
} }
toneport_setup(toneport); 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: */ /* register audio system: */
return snd_card_register(line6->card); 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, static int toneport_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_line6_toneport *toneport; return line6_probe(interface, id,
toneport = kzalloc(sizeof(*toneport), GFP_KERNEL);
if (!toneport)
return -ENODEV;
toneport->type = id->driver_info;
return line6_probe(interface, &toneport->line6,
&toneport_properties_table[id->driver_info], &toneport_properties_table[id->driver_info],
toneport_init); toneport_init, sizeof(struct usb_line6_toneport));
} }
static struct usb_driver toneport_driver = { static struct usb_driver toneport_driver = {

View File

@ -210,16 +210,9 @@ static void line6_variax_process_message(struct usb_line6 *line6)
/* /*
Variax destructor. 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; struct usb_line6_variax *variax = (struct usb_line6_variax *)line6;
if (!interface)
return;
variax = usb_get_intfdata(interface);
if (!variax)
return;
del_timer(&variax->startup_timer1); del_timer(&variax->startup_timer1);
del_timer(&variax->startup_timer2); del_timer(&variax->startup_timer2);
@ -231,8 +224,8 @@ static void line6_variax_disconnect(struct usb_interface *interface)
/* /*
Try to init workbench device. Try to init workbench device.
*/ */
static int variax_init(struct usb_interface *interface, static int variax_init(struct usb_line6 *line6,
struct usb_line6 *line6) const struct usb_device_id *id)
{ {
struct usb_line6_variax *variax = (struct usb_line6_variax *) line6; struct usb_line6_variax *variax = (struct usb_line6_variax *) line6;
int err; int err;
@ -244,9 +237,6 @@ static int variax_init(struct usb_interface *interface,
init_timer(&variax->startup_timer2); init_timer(&variax->startup_timer2);
INIT_WORK(&variax->startup_work, variax_startup6); INIT_WORK(&variax->startup_work, variax_startup6);
if ((interface == NULL) || (variax == NULL))
return -ENODEV;
/* initialize USB buffers: */ /* initialize USB buffers: */
variax->buffer_activate = kmemdup(variax_activate, variax->buffer_activate = kmemdup(variax_activate,
sizeof(variax_activate), GFP_KERNEL); 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, static int variax_probe(struct usb_interface *interface,
const struct usb_device_id *id) const struct usb_device_id *id)
{ {
struct usb_line6_variax *variax; return line6_probe(interface, id,
variax = kzalloc(sizeof(*variax), GFP_KERNEL);
if (!variax)
return -ENODEV;
return line6_probe(interface, &variax->line6,
&variax_properties_table[id->driver_info], &variax_properties_table[id->driver_info],
variax_init); variax_init, sizeof(struct usb_line6_variax));
} }
static struct usb_driver variax_driver = { static struct usb_driver variax_driver = {