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:
commit
5e0ddd07fa
@ -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.
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 = {
|
||||||
|
@ -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 = {
|
||||||
|
Loading…
Reference in New Issue
Block a user