162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Line 6 Linux USB driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2004-2010 Markus Grabner (grabner@icg.tugraz.at) 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/slab.h> 962306a36Sopenharmony_ci#include <sound/core.h> 1062306a36Sopenharmony_ci#include <sound/pcm.h> 1162306a36Sopenharmony_ci#include <sound/pcm_params.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "capture.h" 1462306a36Sopenharmony_ci#include "driver.h" 1562306a36Sopenharmony_ci#include "pcm.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci/* 1862306a36Sopenharmony_ci Find a free URB and submit it. 1962306a36Sopenharmony_ci must be called in line6pcm->in.lock context 2062306a36Sopenharmony_ci*/ 2162306a36Sopenharmony_cistatic int submit_audio_in_urb(struct snd_line6_pcm *line6pcm) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int index; 2462306a36Sopenharmony_ci int i, urb_size; 2562306a36Sopenharmony_ci int ret; 2662306a36Sopenharmony_ci struct urb *urb_in; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci index = find_first_zero_bit(&line6pcm->in.active_urbs, 2962306a36Sopenharmony_ci line6pcm->line6->iso_buffers); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci if (index < 0 || index >= line6pcm->line6->iso_buffers) { 3262306a36Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, "no free URB found\n"); 3362306a36Sopenharmony_ci return -EINVAL; 3462306a36Sopenharmony_ci } 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci urb_in = line6pcm->in.urbs[index]; 3762306a36Sopenharmony_ci urb_size = 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci for (i = 0; i < LINE6_ISO_PACKETS; ++i) { 4062306a36Sopenharmony_ci struct usb_iso_packet_descriptor *fin = 4162306a36Sopenharmony_ci &urb_in->iso_frame_desc[i]; 4262306a36Sopenharmony_ci fin->offset = urb_size; 4362306a36Sopenharmony_ci fin->length = line6pcm->max_packet_size_in; 4462306a36Sopenharmony_ci urb_size += line6pcm->max_packet_size_in; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci urb_in->transfer_buffer = 4862306a36Sopenharmony_ci line6pcm->in.buffer + 4962306a36Sopenharmony_ci index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in; 5062306a36Sopenharmony_ci urb_in->transfer_buffer_length = urb_size; 5162306a36Sopenharmony_ci urb_in->context = line6pcm; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci ret = usb_submit_urb(urb_in, GFP_ATOMIC); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci if (ret == 0) 5662306a36Sopenharmony_ci set_bit(index, &line6pcm->in.active_urbs); 5762306a36Sopenharmony_ci else 5862306a36Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, 5962306a36Sopenharmony_ci "URB in #%d submission failed (%d)\n", index, ret); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci return 0; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* 6562306a36Sopenharmony_ci Submit all currently available capture URBs. 6662306a36Sopenharmony_ci must be called in line6pcm->in.lock context 6762306a36Sopenharmony_ci*/ 6862306a36Sopenharmony_ciint line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int ret = 0, i; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci for (i = 0; i < line6pcm->line6->iso_buffers; ++i) { 7362306a36Sopenharmony_ci ret = submit_audio_in_urb(line6pcm); 7462306a36Sopenharmony_ci if (ret < 0) 7562306a36Sopenharmony_ci break; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci return ret; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/* 8262306a36Sopenharmony_ci Copy data into ALSA capture buffer. 8362306a36Sopenharmony_ci*/ 8462306a36Sopenharmony_civoid line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci struct snd_pcm_substream *substream = 8762306a36Sopenharmony_ci get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); 8862306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 8962306a36Sopenharmony_ci const int bytes_per_frame = 9062306a36Sopenharmony_ci line6pcm->properties->bytes_per_channel * 9162306a36Sopenharmony_ci line6pcm->properties->capture_hw.channels_max; 9262306a36Sopenharmony_ci int frames = fsize / bytes_per_frame; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci if (runtime == NULL) 9562306a36Sopenharmony_ci return; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci if (line6pcm->in.pos_done + frames > runtime->buffer_size) { 9862306a36Sopenharmony_ci /* 9962306a36Sopenharmony_ci The transferred area goes over buffer boundary, 10062306a36Sopenharmony_ci copy two separate chunks. 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci int len; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci len = runtime->buffer_size - line6pcm->in.pos_done; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci if (len > 0) { 10762306a36Sopenharmony_ci memcpy(runtime->dma_area + 10862306a36Sopenharmony_ci line6pcm->in.pos_done * bytes_per_frame, fbuf, 10962306a36Sopenharmony_ci len * bytes_per_frame); 11062306a36Sopenharmony_ci memcpy(runtime->dma_area, fbuf + len * bytes_per_frame, 11162306a36Sopenharmony_ci (frames - len) * bytes_per_frame); 11262306a36Sopenharmony_ci } else { 11362306a36Sopenharmony_ci /* this is somewhat paranoid */ 11462306a36Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, 11562306a36Sopenharmony_ci "driver bug: len = %d\n", len); 11662306a36Sopenharmony_ci } 11762306a36Sopenharmony_ci } else { 11862306a36Sopenharmony_ci /* copy single chunk */ 11962306a36Sopenharmony_ci memcpy(runtime->dma_area + 12062306a36Sopenharmony_ci line6pcm->in.pos_done * bytes_per_frame, fbuf, fsize); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci line6pcm->in.pos_done += frames; 12462306a36Sopenharmony_ci if (line6pcm->in.pos_done >= runtime->buffer_size) 12562306a36Sopenharmony_ci line6pcm->in.pos_done -= runtime->buffer_size; 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_civoid line6_capture_check_period(struct snd_line6_pcm *line6pcm, int length) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct snd_pcm_substream *substream = 13162306a36Sopenharmony_ci get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE); 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci line6pcm->in.bytes += length; 13462306a36Sopenharmony_ci if (line6pcm->in.bytes >= line6pcm->in.period) { 13562306a36Sopenharmony_ci line6pcm->in.bytes %= line6pcm->in.period; 13662306a36Sopenharmony_ci spin_unlock(&line6pcm->in.lock); 13762306a36Sopenharmony_ci snd_pcm_period_elapsed(substream); 13862306a36Sopenharmony_ci spin_lock(&line6pcm->in.lock); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci} 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci/* 14362306a36Sopenharmony_ci * Callback for completed capture URB. 14462306a36Sopenharmony_ci */ 14562306a36Sopenharmony_cistatic void audio_in_callback(struct urb *urb) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci int i, index, length = 0, shutdown = 0; 14862306a36Sopenharmony_ci unsigned long flags; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci line6pcm->in.last_frame = urb->start_frame; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* find index of URB */ 15562306a36Sopenharmony_ci for (index = 0; index < line6pcm->line6->iso_buffers; ++index) 15662306a36Sopenharmony_ci if (urb == line6pcm->in.urbs[index]) 15762306a36Sopenharmony_ci break; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci spin_lock_irqsave(&line6pcm->in.lock, flags); 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci for (i = 0; i < LINE6_ISO_PACKETS; ++i) { 16262306a36Sopenharmony_ci char *fbuf; 16362306a36Sopenharmony_ci int fsize; 16462306a36Sopenharmony_ci struct usb_iso_packet_descriptor *fin = &urb->iso_frame_desc[i]; 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci if (fin->status == -EXDEV) { 16762306a36Sopenharmony_ci shutdown = 1; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci fbuf = urb->transfer_buffer + fin->offset; 17262306a36Sopenharmony_ci fsize = fin->actual_length; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci if (fsize > line6pcm->max_packet_size_in) { 17562306a36Sopenharmony_ci dev_err(line6pcm->line6->ifcdev, 17662306a36Sopenharmony_ci "driver and/or device bug: packet too large (%d > %d)\n", 17762306a36Sopenharmony_ci fsize, line6pcm->max_packet_size_in); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci length += fsize; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1, 18362306a36Sopenharmony_ci "The following code assumes LINE6_ISO_PACKETS == 1"); 18462306a36Sopenharmony_ci /* TODO: 18562306a36Sopenharmony_ci * Also, if iso_buffers != 2, the prev frame is almost random at 18662306a36Sopenharmony_ci * playback side. 18762306a36Sopenharmony_ci * This needs to be redesigned. It should be "stable", but we may 18862306a36Sopenharmony_ci * experience sync problems on such high-speed configs. 18962306a36Sopenharmony_ci */ 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci line6pcm->prev_fbuf = fbuf; 19262306a36Sopenharmony_ci line6pcm->prev_fsize = fsize / 19362306a36Sopenharmony_ci (line6pcm->properties->bytes_per_channel * 19462306a36Sopenharmony_ci line6pcm->properties->capture_hw.channels_max); 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) && 19762306a36Sopenharmony_ci test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) && 19862306a36Sopenharmony_ci fsize > 0) 19962306a36Sopenharmony_ci line6_capture_copy(line6pcm, fbuf, fsize); 20062306a36Sopenharmony_ci } 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci clear_bit(index, &line6pcm->in.active_urbs); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci if (test_and_clear_bit(index, &line6pcm->in.unlink_urbs)) 20562306a36Sopenharmony_ci shutdown = 1; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci if (!shutdown) { 20862306a36Sopenharmony_ci submit_audio_in_urb(line6pcm); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) && 21162306a36Sopenharmony_ci test_bit(LINE6_STREAM_PCM, &line6pcm->in.running)) 21262306a36Sopenharmony_ci line6_capture_check_period(line6pcm, length); 21362306a36Sopenharmony_ci } 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci spin_unlock_irqrestore(&line6pcm->in.lock, flags); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci/* open capture callback */ 21962306a36Sopenharmony_cistatic int snd_line6_capture_open(struct snd_pcm_substream *substream) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int err; 22262306a36Sopenharmony_ci struct snd_pcm_runtime *runtime = substream->runtime; 22362306a36Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci err = snd_pcm_hw_constraint_ratdens(runtime, 0, 22662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_RATE, 22762306a36Sopenharmony_ci &line6pcm->properties->rates); 22862306a36Sopenharmony_ci if (err < 0) 22962306a36Sopenharmony_ci return err; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci runtime->hw = line6pcm->properties->capture_hw; 23462306a36Sopenharmony_ci return 0; 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* close capture callback */ 23862306a36Sopenharmony_cistatic int snd_line6_capture_close(struct snd_pcm_substream *substream) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER); 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci/* capture operators */ 24762306a36Sopenharmony_ciconst struct snd_pcm_ops snd_line6_capture_ops = { 24862306a36Sopenharmony_ci .open = snd_line6_capture_open, 24962306a36Sopenharmony_ci .close = snd_line6_capture_close, 25062306a36Sopenharmony_ci .hw_params = snd_line6_hw_params, 25162306a36Sopenharmony_ci .hw_free = snd_line6_hw_free, 25262306a36Sopenharmony_ci .prepare = snd_line6_prepare, 25362306a36Sopenharmony_ci .trigger = snd_line6_trigger, 25462306a36Sopenharmony_ci .pointer = snd_line6_pointer, 25562306a36Sopenharmony_ci}; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ciint line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct usb_line6 *line6 = line6pcm->line6; 26062306a36Sopenharmony_ci int i; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci line6pcm->in.urbs = kcalloc(line6->iso_buffers, sizeof(struct urb *), 26362306a36Sopenharmony_ci GFP_KERNEL); 26462306a36Sopenharmony_ci if (line6pcm->in.urbs == NULL) 26562306a36Sopenharmony_ci return -ENOMEM; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* create audio URBs and fill in constant values: */ 26862306a36Sopenharmony_ci for (i = 0; i < line6->iso_buffers; ++i) { 26962306a36Sopenharmony_ci struct urb *urb; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* URB for audio in: */ 27262306a36Sopenharmony_ci urb = line6pcm->in.urbs[i] = 27362306a36Sopenharmony_ci usb_alloc_urb(LINE6_ISO_PACKETS, GFP_KERNEL); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci if (urb == NULL) 27662306a36Sopenharmony_ci return -ENOMEM; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci urb->dev = line6->usbdev; 27962306a36Sopenharmony_ci urb->pipe = 28062306a36Sopenharmony_ci usb_rcvisocpipe(line6->usbdev, 28162306a36Sopenharmony_ci line6->properties->ep_audio_r & 28262306a36Sopenharmony_ci USB_ENDPOINT_NUMBER_MASK); 28362306a36Sopenharmony_ci urb->transfer_flags = URB_ISO_ASAP; 28462306a36Sopenharmony_ci urb->start_frame = -1; 28562306a36Sopenharmony_ci urb->number_of_packets = LINE6_ISO_PACKETS; 28662306a36Sopenharmony_ci urb->interval = LINE6_ISO_INTERVAL; 28762306a36Sopenharmony_ci urb->error_count = 0; 28862306a36Sopenharmony_ci urb->complete = audio_in_callback; 28962306a36Sopenharmony_ci if (usb_urb_ep_type_check(urb)) 29062306a36Sopenharmony_ci return -EINVAL; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return 0; 29462306a36Sopenharmony_ci} 295