important performance, stability & safety fixes
This commit is contained in:
parent
d05f560d71
commit
63053cac3f
41
us144mkii.c
41
us144mkii.c
|
|
@ -10,7 +10,7 @@ MODULE_LICENSE("GPL");
|
|||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
|
||||
static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
|
||||
static bool enable[SNDRV_CARDS] = { 1, [1 ...(SNDRV_CARDS - 1)] = 0 };
|
||||
static int dev_idx;
|
||||
static atomic_t dev_idx = ATOMIC_INIT(0);
|
||||
|
||||
static int tascam_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *usb_id);
|
||||
|
|
@ -195,16 +195,27 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||
struct snd_card *card;
|
||||
struct tascam_card *tascam;
|
||||
int err;
|
||||
int idx;
|
||||
char *handshake_buf __free(kfree) = NULL;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 1)
|
||||
return 0;
|
||||
|
||||
if (dev_idx >= SNDRV_CARDS) return -ENODEV;
|
||||
if (!enable[dev_idx]) return -ENOENT;
|
||||
idx = atomic_fetch_inc(&dev_idx);
|
||||
if (idx >= SNDRV_CARDS) {
|
||||
atomic_dec(&dev_idx);
|
||||
return -ENODEV;
|
||||
}
|
||||
if (!enable[idx]) {
|
||||
atomic_dec(&dev_idx);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
handshake_buf = kmalloc(1, GFP_KERNEL);
|
||||
if (!handshake_buf) return -ENOMEM;
|
||||
if (!handshake_buf) {
|
||||
atomic_dec(&dev_idx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
|
||||
VENDOR_REQ_MODE_CONTROL, RT_D2H_VENDOR_DEV,
|
||||
|
|
@ -212,15 +223,19 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||
USB_CTRL_TIMEOUT_MS);
|
||||
if (err < 0) {
|
||||
dev_err(&dev->dev, "Handshake failed: %d\n", err);
|
||||
atomic_dec(&dev_idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
usb_set_interface(dev, 0, 1);
|
||||
usb_set_interface(dev, 1, 1);
|
||||
|
||||
err = snd_card_new(&dev->dev, index[dev_idx], id[dev_idx], THIS_MODULE,
|
||||
err = snd_card_new(&dev->dev, index[idx], id[idx], THIS_MODULE,
|
||||
sizeof(struct tascam_card), &card);
|
||||
if (err < 0) return err;
|
||||
if (err < 0) {
|
||||
atomic_dec(&dev_idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
tascam = card->private_data;
|
||||
card->private_free = tascam_card_private_free;
|
||||
|
|
@ -272,11 +287,11 @@ static int tascam_probe(struct usb_interface *intf, const struct usb_device_id *
|
|||
if (err < 0) goto free_card;
|
||||
|
||||
usb_set_intfdata(intf, tascam);
|
||||
dev_idx++;
|
||||
return 0;
|
||||
|
||||
free_card:
|
||||
snd_card_free(card);
|
||||
atomic_dec(&dev_idx);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
|
@ -286,11 +301,21 @@ static void tascam_disconnect(struct usb_interface *intf)
|
|||
if (!tascam) return;
|
||||
|
||||
if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
|
||||
atomic_set(&tascam->playback_active, 0);
|
||||
atomic_set(&tascam->capture_active, 0);
|
||||
|
||||
usb_kill_anchored_urbs(&tascam->playback_anchor);
|
||||
usb_kill_anchored_urbs(&tascam->feedback_anchor);
|
||||
usb_kill_anchored_urbs(&tascam->capture_anchor);
|
||||
usb_kill_anchored_urbs(&tascam->midi_anchor);
|
||||
|
||||
snd_card_disconnect(tascam->card);
|
||||
cancel_work_sync(&tascam->stop_work);
|
||||
cancel_work_sync(&tascam->stop_pcm_work);
|
||||
|
||||
usb_set_intfdata(intf, NULL);
|
||||
snd_card_free(tascam->card);
|
||||
dev_idx--;
|
||||
atomic_dec(&dev_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
23
us144mkii.h
23
us144mkii.h
|
|
@ -7,6 +7,7 @@
|
|||
#include <linux/timer.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <sound/core.h>
|
||||
#include <sound/initval.h>
|
||||
#include <sound/pcm.h>
|
||||
|
|
@ -14,19 +15,16 @@
|
|||
|
||||
#define DRIVER_NAME "us144mkii"
|
||||
|
||||
/* --- USB Device Identification --- */
|
||||
#define USB_VID_TASCAM 0x0644
|
||||
#define USB_PID_TASCAM_US144 0x800f
|
||||
#define USB_PID_TASCAM_US144MKII 0x8020
|
||||
|
||||
/* --- USB Endpoints --- */
|
||||
#define EP_PLAYBACK_FEEDBACK 0x81
|
||||
#define EP_AUDIO_OUT 0x02
|
||||
#define EP_MIDI_IN 0x83
|
||||
#define EP_MIDI_OUT 0x04
|
||||
#define EP_AUDIO_IN 0x86
|
||||
|
||||
/* --- USB Control Message Protocol --- */
|
||||
#define RT_H2D_CLASS_EP (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT)
|
||||
#define RT_D2H_CLASS_EP (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT)
|
||||
#define RT_H2D_VENDOR_DEV (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)
|
||||
|
|
@ -66,7 +64,6 @@ enum tascam_register {
|
|||
|
||||
#define REG_VAL_ENABLE 0x0101
|
||||
|
||||
/* --- URB Configuration --- */
|
||||
#define NUM_PLAYBACK_URBS 8
|
||||
#define PLAYBACK_URB_PACKETS 2
|
||||
#define NUM_FEEDBACK_URBS 2
|
||||
|
|
@ -75,38 +72,29 @@ enum tascam_register {
|
|||
#define NUM_CAPTURE_URBS 4
|
||||
#define CAPTURE_PACKET_SIZE 512
|
||||
|
||||
/* --- MIDI Configuration --- */
|
||||
#define MIDI_PACKET_SIZE 9
|
||||
#define MIDI_PAYLOAD_SIZE 8
|
||||
|
||||
/* --- Audio Format Configuration --- */
|
||||
#define BYTES_PER_SAMPLE 3
|
||||
#define NUM_CHANNELS 4
|
||||
#define BYTES_PER_FRAME (NUM_CHANNELS * BYTES_PER_SAMPLE)
|
||||
|
||||
/* --- PLL Config --- */
|
||||
#define PLL_FILTER_OLD_WEIGHT 3
|
||||
#define PLL_FILTER_NEW_WEIGHT 1
|
||||
#define PLL_FILTER_DIVISOR (PLL_FILTER_OLD_WEIGHT + PLL_FILTER_NEW_WEIGHT)
|
||||
|
||||
#define USB_CTRL_TIMEOUT_MS 1000
|
||||
|
||||
/**
|
||||
* struct tascam_card - Main driver data structure for the TASCAM US-144MKII.
|
||||
*/
|
||||
struct tascam_card {
|
||||
/* --- Core device pointers --- */
|
||||
struct usb_device *dev;
|
||||
struct usb_interface *iface0;
|
||||
struct snd_card *card;
|
||||
struct snd_pcm *pcm;
|
||||
struct snd_rawmidi *rmidi;
|
||||
|
||||
/* --- PCM Substreams --- */
|
||||
struct snd_pcm_substream *playback_substream;
|
||||
struct snd_pcm_substream *capture_substream;
|
||||
|
||||
/* --- Audio URBs and Anchors --- */
|
||||
struct urb *playback_urbs[NUM_PLAYBACK_URBS];
|
||||
size_t playback_urb_alloc_size;
|
||||
struct urb *feedback_urbs[NUM_FEEDBACK_URBS];
|
||||
|
|
@ -117,7 +105,6 @@ struct tascam_card {
|
|||
struct usb_anchor feedback_anchor;
|
||||
struct usb_anchor capture_anchor;
|
||||
|
||||
/* --- MIDI State --- */
|
||||
struct snd_rawmidi_substream *midi_input;
|
||||
struct snd_rawmidi_substream *midi_output;
|
||||
struct urb *midi_in_urb;
|
||||
|
|
@ -128,43 +115,35 @@ struct tascam_card {
|
|||
bool midi_out_active;
|
||||
spinlock_t midi_lock;
|
||||
|
||||
/* --- Stream State --- */
|
||||
spinlock_t lock;
|
||||
atomic_t playback_active;
|
||||
atomic_t capture_active;
|
||||
atomic_t active_urbs;
|
||||
int current_rate;
|
||||
|
||||
/* --- Playback Positions --- */
|
||||
u64 playback_frames_consumed;
|
||||
snd_pcm_uframes_t driver_playback_pos;
|
||||
u64 last_pb_period_pos;
|
||||
|
||||
/* --- Capture Positions --- */
|
||||
u64 capture_frames_processed;
|
||||
snd_pcm_uframes_t driver_capture_pos;
|
||||
u64 last_cap_period_pos;
|
||||
|
||||
/* --- PLL / Feedback State --- */
|
||||
u32 phase_accum;
|
||||
u32 freq_q16;
|
||||
bool feedback_synced;
|
||||
unsigned int feedback_urb_skip_count;
|
||||
|
||||
/* --- Workqueues --- */
|
||||
struct work_struct stop_work;
|
||||
struct work_struct stop_pcm_work;
|
||||
};
|
||||
|
||||
/* main.c */
|
||||
void tascam_free_urbs(struct tascam_card *tascam);
|
||||
int tascam_alloc_urbs(struct tascam_card *tascam);
|
||||
void tascam_stop_work_handler(struct work_struct *work);
|
||||
|
||||
/* us144mkii_pcm.h */
|
||||
#include "us144mkii_pcm.h"
|
||||
|
||||
/* us144mkii_midi.c */
|
||||
int tascam_create_midi(struct tascam_card *tascam);
|
||||
|
||||
#endif /* __US144MKII_H */
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_capture_hw = {
|
|||
.channels_min = NUM_CHANNELS,
|
||||
.channels_max = NUM_CHANNELS,
|
||||
.buffer_bytes_max = 1024 * 1024,
|
||||
.period_bytes_min = 64 * BYTES_PER_FRAME,
|
||||
.period_bytes_min = 32 * BYTES_PER_FRAME,
|
||||
.period_bytes_max = 1024 * BYTES_PER_FRAME,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
|
|
@ -54,11 +54,14 @@ static snd_pcm_uframes_t tascam_capture_pointer(struct snd_pcm_substream *substr
|
|||
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
||||
unsigned long flags;
|
||||
u64 pos;
|
||||
snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size;
|
||||
|
||||
if (!atomic_read(&tascam->capture_active)) return 0;
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
pos = tascam->capture_frames_processed;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
return do_div(pos, substream->runtime->buffer_size);
|
||||
|
||||
return (snd_pcm_uframes_t)(pos % buffer_size);
|
||||
}
|
||||
|
||||
static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
|
|
@ -66,6 +69,7 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
||||
int i, ret = 0;
|
||||
bool start = false;
|
||||
bool stop = false;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
|
|
@ -80,13 +84,8 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
|
||||
atomic_set(&tascam->capture_active, 0);
|
||||
|
||||
for (i = 0; i < NUM_CAPTURE_URBS; i++) {
|
||||
if (tascam->capture_urbs[i])
|
||||
usb_unlink_urb(tascam->capture_urbs[i]);
|
||||
}
|
||||
stop = true;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
@ -94,13 +93,22 @@ static int tascam_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
}
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
if (stop) {
|
||||
smp_mb();
|
||||
for (i = 0; i < NUM_CAPTURE_URBS; i++) {
|
||||
if (tascam->capture_urbs[i])
|
||||
usb_unlink_urb(tascam->capture_urbs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (start) {
|
||||
for (i = 0; i < NUM_CAPTURE_URBS; i++) {
|
||||
usb_anchor_urb(tascam->capture_urbs[i], &tascam->capture_anchor);
|
||||
if (usb_submit_urb(tascam->capture_urbs[i], GFP_ATOMIC) < 0) {
|
||||
usb_unanchor_urb(tascam->capture_urbs[i]);
|
||||
atomic_set(&tascam->capture_active, 0);
|
||||
|
||||
for (int j = 0; j <= i; j++)
|
||||
smp_mb();
|
||||
for (int j = 0; j < i; j++)
|
||||
usb_unlink_urb(tascam->capture_urbs[j]);
|
||||
ret = -EIO;
|
||||
break;
|
||||
|
|
@ -162,38 +170,48 @@ void capture_urb_complete(struct urb *urb)
|
|||
unsigned long flags;
|
||||
int frames_received;
|
||||
snd_pcm_uframes_t write_pos;
|
||||
snd_pcm_uframes_t buffer_size, period_size;
|
||||
bool need_period_elapsed = false;
|
||||
|
||||
if (!tascam)
|
||||
return;
|
||||
|
||||
if (urb->status) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tascam || !atomic_read(&tascam->capture_active)) {
|
||||
if (!atomic_read(&tascam->capture_active)) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
||||
substream = tascam->capture_substream;
|
||||
if (!substream || !substream->runtime) {
|
||||
if (!substream) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
runtime = substream->runtime;
|
||||
if (!runtime) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_size = runtime->buffer_size;
|
||||
period_size = runtime->period_size;
|
||||
frames_received = urb->actual_length / 64;
|
||||
|
||||
if (frames_received > 0) {
|
||||
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
write_pos = tascam->driver_capture_pos;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
u32 *dma_ptr = (u32 *)(runtime->dma_area + frames_to_bytes(runtime, write_pos));
|
||||
|
||||
if (write_pos + frames_received <= runtime->buffer_size) {
|
||||
if (write_pos + frames_received <= buffer_size) {
|
||||
tascam_decode_capture_chunk(urb->transfer_buffer, dma_ptr, frames_received);
|
||||
} else {
|
||||
int part1 = runtime->buffer_size - write_pos;
|
||||
int part1 = buffer_size - write_pos;
|
||||
int part2 = frames_received - part1;
|
||||
tascam_decode_capture_chunk(urb->transfer_buffer, dma_ptr, part1);
|
||||
tascam_decode_capture_chunk(urb->transfer_buffer + (part1 * 64), (u32 *)runtime->dma_area, part2);
|
||||
|
|
@ -201,21 +219,22 @@ void capture_urb_complete(struct urb *urb)
|
|||
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
tascam->driver_capture_pos += frames_received;
|
||||
if (tascam->driver_capture_pos >= runtime->buffer_size)
|
||||
tascam->driver_capture_pos -= runtime->buffer_size;
|
||||
if (tascam->driver_capture_pos >= buffer_size)
|
||||
tascam->driver_capture_pos -= buffer_size;
|
||||
|
||||
tascam->capture_frames_processed += frames_received;
|
||||
|
||||
if (runtime->period_size > 0) {
|
||||
u64 current_period = div_u64(tascam->capture_frames_processed, runtime->period_size);
|
||||
if (period_size > 0) {
|
||||
u64 current_period = div_u64(tascam->capture_frames_processed, period_size);
|
||||
if (current_period > tascam->last_cap_period_pos) {
|
||||
tascam->last_cap_period_pos = current_period;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
snd_pcm_period_elapsed(substream);
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
need_period_elapsed = true;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
if (need_period_elapsed)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
}
|
||||
|
||||
if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ static void tascam_midi_out_complete(struct urb *urb)
|
|||
unsigned long flags;
|
||||
int count;
|
||||
bool submit = false;
|
||||
bool active;
|
||||
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
|
||||
|
|
@ -18,10 +19,14 @@ static void tascam_midi_out_complete(struct urb *urb)
|
|||
return;
|
||||
}
|
||||
|
||||
active = tascam->midi_out_active;
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
count = snd_rawmidi_transmit(tascam->midi_output, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
|
||||
if (count > 0) {
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
|
||||
if (count < MIDI_PAYLOAD_SIZE)
|
||||
memset(tascam->midi_out_buf + count, 0xFD, MIDI_PAYLOAD_SIZE - count);
|
||||
|
||||
|
|
@ -29,6 +34,7 @@ static void tascam_midi_out_complete(struct urb *urb)
|
|||
urb->transfer_buffer_length = MIDI_PACKET_SIZE;
|
||||
submit = true;
|
||||
} else {
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
tascam->midi_out_active = false;
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
}
|
||||
|
|
@ -47,22 +53,24 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
|
|||
struct tascam_card *tascam = substream->rmidi->private_data;
|
||||
unsigned long flags;
|
||||
int count;
|
||||
bool do_submit = false;
|
||||
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
if (up) {
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
tascam->midi_output = substream;
|
||||
|
||||
if (!tascam->midi_out_active) {
|
||||
tascam->midi_out_active = true;
|
||||
do_submit = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
|
||||
if (do_submit) {
|
||||
count = snd_rawmidi_transmit(substream, tascam->midi_out_buf, MIDI_PAYLOAD_SIZE);
|
||||
if (count > 0) {
|
||||
tascam->midi_out_active = true;
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
|
||||
if (count < MIDI_PAYLOAD_SIZE)
|
||||
memset(tascam->midi_out_buf + count, 0xFD, MIDI_PAYLOAD_SIZE - count);
|
||||
|
||||
tascam->midi_out_buf[8] = 0xE0;
|
||||
|
||||
tascam->midi_out_urb->transfer_buffer_length = MIDI_PACKET_SIZE;
|
||||
|
||||
usb_anchor_urb(tascam->midi_out_urb, &tascam->midi_anchor);
|
||||
|
|
@ -73,12 +81,13 @@ static void tascam_midi_output_trigger(struct snd_rawmidi_substream *substream,
|
|||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
}
|
||||
} else {
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
tascam->midi_out_active = false;
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
}
|
||||
} else {
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
}
|
||||
} else {
|
||||
spin_lock_irqsave(&tascam->midi_lock, flags);
|
||||
tascam->midi_output = NULL;
|
||||
spin_unlock_irqrestore(&tascam->midi_lock, flags);
|
||||
}
|
||||
|
|
@ -175,14 +184,30 @@ int tascam_create_midi(struct tascam_card *tascam)
|
|||
tascam->rmidi = rmidi;
|
||||
|
||||
tascam->midi_out_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!tascam->midi_out_urb) {
|
||||
err = -ENOMEM;
|
||||
goto err_out_urb;
|
||||
}
|
||||
|
||||
tascam->midi_in_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!tascam->midi_out_urb || !tascam->midi_in_urb) return -ENOMEM;
|
||||
if (!tascam->midi_in_urb) {
|
||||
err = -ENOMEM;
|
||||
goto err_in_urb;
|
||||
}
|
||||
|
||||
tascam->midi_out_buf = usb_alloc_coherent(tascam->dev, MIDI_PACKET_SIZE,
|
||||
GFP_KERNEL, &tascam->midi_out_urb->transfer_dma);
|
||||
if (!tascam->midi_out_buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_out_buf;
|
||||
}
|
||||
|
||||
tascam->midi_in_buf = usb_alloc_coherent(tascam->dev, MIDI_PACKET_SIZE,
|
||||
GFP_KERNEL, &tascam->midi_in_urb->transfer_dma);
|
||||
if (!tascam->midi_out_buf || !tascam->midi_in_buf) return -ENOMEM;
|
||||
if (!tascam->midi_in_buf) {
|
||||
err = -ENOMEM;
|
||||
goto err_in_buf;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(tascam->midi_out_urb, tascam->dev,
|
||||
usb_sndbulkpipe(tascam->dev, EP_MIDI_OUT),
|
||||
|
|
@ -200,4 +225,16 @@ int tascam_create_midi(struct tascam_card *tascam)
|
|||
init_usb_anchor(&tascam->midi_anchor);
|
||||
|
||||
return 0;
|
||||
|
||||
err_in_buf:
|
||||
usb_free_coherent(tascam->dev, MIDI_PACKET_SIZE,
|
||||
tascam->midi_out_buf, tascam->midi_out_urb->transfer_dma);
|
||||
err_out_buf:
|
||||
usb_free_urb(tascam->midi_in_urb);
|
||||
tascam->midi_in_urb = NULL;
|
||||
err_in_urb:
|
||||
usb_free_urb(tascam->midi_out_urb);
|
||||
tascam->midi_out_urb = NULL;
|
||||
err_out_urb:
|
||||
return err;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const struct snd_pcm_hardware tascam_playback_hw = {
|
|||
.channels_min = NUM_CHANNELS,
|
||||
.channels_max = NUM_CHANNELS,
|
||||
.buffer_bytes_max = 1024 * 1024,
|
||||
.period_bytes_min = 64 * BYTES_PER_FRAME,
|
||||
.period_bytes_min = 32 * BYTES_PER_FRAME,
|
||||
.period_bytes_max = 1024 * BYTES_PER_FRAME,
|
||||
.periods_min = 2,
|
||||
.periods_max = 1024,
|
||||
|
|
@ -86,12 +86,14 @@ static snd_pcm_uframes_t tascam_playback_pointer(struct snd_pcm_substream *subst
|
|||
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
||||
unsigned long flags;
|
||||
u64 pos;
|
||||
snd_pcm_uframes_t buffer_size = substream->runtime->buffer_size;
|
||||
|
||||
if (!atomic_read(&tascam->playback_active)) return 0;
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
pos = tascam->playback_frames_consumed;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
return do_div(pos, substream->runtime->buffer_size);
|
||||
|
||||
return (snd_pcm_uframes_t)(pos % buffer_size);
|
||||
}
|
||||
|
||||
static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
|
|
@ -99,6 +101,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
struct tascam_card *tascam = snd_pcm_substream_chip(substream);
|
||||
int i, ret = 0;
|
||||
bool start = false;
|
||||
bool stop = false;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
|
|
@ -114,15 +117,7 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
atomic_set(&tascam->playback_active, 0);
|
||||
|
||||
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
|
||||
if (tascam->feedback_urbs[i])
|
||||
usb_unlink_urb(tascam->feedback_urbs[i]);
|
||||
}
|
||||
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
||||
if (tascam->playback_urbs[i])
|
||||
usb_unlink_urb(tascam->playback_urbs[i]);
|
||||
}
|
||||
stop = true;
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
|
|
@ -130,20 +125,54 @@ static int tascam_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
|||
}
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
if (stop) {
|
||||
smp_mb();
|
||||
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
|
||||
if (tascam->feedback_urbs[i])
|
||||
usb_unlink_urb(tascam->feedback_urbs[i]);
|
||||
}
|
||||
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
||||
if (tascam->playback_urbs[i])
|
||||
usb_unlink_urb(tascam->playback_urbs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (start) {
|
||||
for (i = 0; i < NUM_FEEDBACK_URBS; i++) {
|
||||
usb_anchor_urb(tascam->feedback_urbs[i], &tascam->feedback_anchor);
|
||||
usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC);
|
||||
if (usb_submit_urb(tascam->feedback_urbs[i], GFP_ATOMIC) < 0) {
|
||||
usb_unanchor_urb(tascam->feedback_urbs[i]);
|
||||
dev_err(&tascam->dev->dev, "Failed to submit feedback URB %d\n", i);
|
||||
atomic_set(&tascam->playback_active, 0);
|
||||
smp_mb();
|
||||
for (int j = 0; j < i; j++)
|
||||
usb_unlink_urb(tascam->feedback_urbs[j]);
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
atomic_inc(&tascam->active_urbs);
|
||||
}
|
||||
for (i = 0; i < NUM_PLAYBACK_URBS; i++) {
|
||||
usb_anchor_urb(tascam->playback_urbs[i], &tascam->playback_anchor);
|
||||
usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC);
|
||||
if (usb_submit_urb(tascam->playback_urbs[i], GFP_ATOMIC) < 0) {
|
||||
usb_unanchor_urb(tascam->playback_urbs[i]);
|
||||
dev_err(&tascam->dev->dev, "Failed to submit playback URB %d\n", i);
|
||||
atomic_set(&tascam->playback_active, 0);
|
||||
smp_mb();
|
||||
for (int j = 0; j < NUM_FEEDBACK_URBS; j++)
|
||||
usb_unlink_urb(tascam->feedback_urbs[j]);
|
||||
for (int j = 0; j < i; j++)
|
||||
usb_unlink_urb(tascam->playback_urbs[j]);
|
||||
ret = -EIO;
|
||||
goto error;
|
||||
}
|
||||
atomic_inc(&tascam->active_urbs);
|
||||
}
|
||||
} else if (atomic_read(&tascam->playback_active) == 0) {
|
||||
} else if (stop && ret == 0) {
|
||||
schedule_work(&tascam->stop_work);
|
||||
}
|
||||
|
||||
error:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -157,22 +186,34 @@ void playback_urb_complete(struct urb *urb)
|
|||
snd_pcm_uframes_t frames_to_copy;
|
||||
int i;
|
||||
unsigned long flags;
|
||||
bool need_period_elapsed = false;
|
||||
snd_pcm_uframes_t buffer_size, period_size;
|
||||
|
||||
if (!tascam)
|
||||
return;
|
||||
|
||||
if (urb->status) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
if (!tascam || !atomic_read(&tascam->playback_active)) {
|
||||
if (!atomic_read(&tascam->playback_active)) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
||||
substream = tascam->playback_substream;
|
||||
if (!substream || !substream->runtime) {
|
||||
if (!substream) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
runtime = substream->runtime;
|
||||
if (!runtime) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
||||
buffer_size = runtime->buffer_size;
|
||||
period_size = runtime->period_size;
|
||||
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
|
|
@ -188,14 +229,14 @@ void playback_urb_complete(struct urb *urb)
|
|||
|
||||
offset_frames = tascam->driver_playback_pos;
|
||||
frames_to_copy = bytes_to_frames(runtime, total_bytes_for_urb);
|
||||
tascam->driver_playback_pos = (offset_frames + frames_to_copy) % runtime->buffer_size;
|
||||
tascam->driver_playback_pos = (offset_frames + frames_to_copy) % buffer_size;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
if (total_bytes_for_urb > 0) {
|
||||
u8 *dst_buf = urb->transfer_buffer;
|
||||
size_t ptr_bytes = frames_to_bytes(runtime, offset_frames);
|
||||
if (offset_frames + frames_to_copy > runtime->buffer_size) {
|
||||
size_t part1 = runtime->buffer_size - offset_frames;
|
||||
if (offset_frames + frames_to_copy > buffer_size) {
|
||||
size_t part1 = buffer_size - offset_frames;
|
||||
memcpy(dst_buf, runtime->dma_area + ptr_bytes, frames_to_bytes(runtime, part1));
|
||||
memcpy(dst_buf + frames_to_bytes(runtime, part1), runtime->dma_area, total_bytes_for_urb - frames_to_bytes(runtime, part1));
|
||||
} else {
|
||||
|
|
@ -206,17 +247,18 @@ void playback_urb_complete(struct urb *urb)
|
|||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
tascam->playback_frames_consumed += frames_to_copy;
|
||||
|
||||
if (runtime->period_size > 0) {
|
||||
u64 current_period = div_u64(tascam->playback_frames_consumed, runtime->period_size);
|
||||
if (period_size > 0) {
|
||||
u64 current_period = div_u64(tascam->playback_frames_consumed, period_size);
|
||||
if (current_period > tascam->last_pb_period_pos) {
|
||||
tascam->last_pb_period_pos = current_period;
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
snd_pcm_period_elapsed(substream);
|
||||
spin_lock_irqsave(&tascam->lock, flags);
|
||||
need_period_elapsed = true;
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&tascam->lock, flags);
|
||||
|
||||
if (need_period_elapsed)
|
||||
snd_pcm_period_elapsed(substream);
|
||||
|
||||
if (usb_submit_urb(urb, GFP_ATOMIC) < 0)
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
}
|
||||
|
|
@ -227,11 +269,14 @@ void feedback_urb_complete(struct urb *urb)
|
|||
int ret, p;
|
||||
unsigned long flags;
|
||||
|
||||
if (!tascam)
|
||||
return;
|
||||
|
||||
if (urb->status) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
if (!tascam || !atomic_read(&tascam->playback_active)) {
|
||||
if (!atomic_read(&tascam->playback_active)) {
|
||||
atomic_dec(&tascam->active_urbs);
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue