important performance, stability & safety fixes

This commit is contained in:
Šerif Rami 2025-12-01 09:17:25 +01:00
parent d05f560d71
commit 63053cac3f
5 changed files with 194 additions and 89 deletions

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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)

View File

@ -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;
}

View File

@ -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;
}