2009-02-27 19:43:04 -08:00
/*
2015-01-20 02:20:50 -06:00
* Line 6 Linux USB driver
2009-02-27 19:43:04 -08:00
*
2010-08-12 01:35:30 +02:00
* Copyright ( C ) 2004 - 2010 Markus Grabner ( grabner @ icg . tugraz . at )
2009-02-27 19:43:04 -08:00
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2.
*
*/
2011-11-23 08:20:45 +00:00
# include <linux/slab.h>
2009-02-27 19:43:04 -08:00
# include <sound/core.h>
# include <sound/pcm.h>
# include <sound/pcm_params.h>
2010-08-12 01:35:30 +02:00
# include "capture.h"
# include "driver.h"
2009-02-27 19:43:04 -08:00
# include "pcm.h"
2010-08-12 01:35:30 +02:00
2009-02-27 19:43:04 -08:00
/*
Find a free URB and submit it .
2015-01-27 12:50:44 +01:00
must be called in line6pcm - > in . lock context
2009-02-27 19:43:04 -08:00
*/
2010-08-12 01:35:30 +02:00
static int submit_audio_in_urb ( struct snd_line6_pcm * line6pcm )
2009-02-27 19:43:04 -08:00
{
2010-08-12 01:35:30 +02:00
int index ;
2009-02-27 19:43:04 -08:00
int i , urb_size ;
2010-08-23 01:08:25 +02:00
int ret ;
2009-02-27 19:43:04 -08:00
struct urb * urb_in ;
2016-09-18 20:59:21 +02:00
index = find_first_zero_bit ( & line6pcm - > in . active_urbs ,
line6pcm - > line6 - > iso_buffers ) ;
2009-02-27 19:43:04 -08:00
2016-09-18 20:59:21 +02:00
if ( index < 0 | | index > = line6pcm - > line6 - > iso_buffers ) {
2010-08-12 01:35:30 +02:00
dev_err ( line6pcm - > line6 - > ifcdev , " no free URB found \n " ) ;
2009-02-27 19:43:04 -08:00
return - EINVAL ;
}
2015-01-23 16:10:57 +01:00
urb_in = line6pcm - > in . urbs [ index ] ;
2009-02-27 19:43:04 -08:00
urb_size = 0 ;
2009-02-27 22:39:22 -08:00
for ( i = 0 ; i < LINE6_ISO_PACKETS ; + + i ) {
2009-11-15 22:17:52 -06:00
struct usb_iso_packet_descriptor * fin =
& urb_in - > iso_frame_desc [ i ] ;
2009-02-27 19:43:04 -08:00
fin - > offset = urb_size ;
2016-09-18 20:59:23 +02:00
fin - > length = line6pcm - > max_packet_size_in ;
urb_size + = line6pcm - > max_packet_size_in ;
2009-02-27 19:43:04 -08:00
}
2009-11-15 22:17:52 -06:00
urb_in - > transfer_buffer =
2015-01-23 16:10:57 +01:00
line6pcm - > in . buffer +
2016-09-18 20:59:23 +02:00
index * LINE6_ISO_PACKETS * line6pcm - > max_packet_size_in ;
2009-02-27 19:43:04 -08:00
urb_in - > transfer_buffer_length = urb_size ;
2010-08-12 01:35:30 +02:00
urb_in - > context = line6pcm ;
2009-02-27 19:43:04 -08:00
2010-08-23 01:08:25 +02:00
ret = usb_submit_urb ( urb_in , GFP_ATOMIC ) ;
if ( ret = = 0 )
2015-01-23 16:10:57 +01:00
set_bit ( index , & line6pcm - > in . active_urbs ) ;
2009-02-27 19:43:04 -08:00
else
2010-08-12 01:35:30 +02:00
dev_err ( line6pcm - > line6 - > ifcdev ,
2010-08-23 01:08:25 +02:00
" URB in #%d submission failed (%d) \n " , index , ret ) ;
2009-02-27 19:43:04 -08:00
return 0 ;
}
/*
Submit all currently available capture URBs .
2015-01-27 15:24:09 +01:00
must be called in line6pcm - > in . lock context
2009-02-27 19:43:04 -08:00
*/
2010-08-12 01:35:30 +02:00
int line6_submit_audio_in_all_urbs ( struct snd_line6_pcm * line6pcm )
2009-02-27 19:43:04 -08:00
{
2015-01-27 12:50:44 +01:00
int ret = 0 , i ;
2009-02-27 19:43:04 -08:00
2016-09-18 20:59:21 +02:00
for ( i = 0 ; i < line6pcm - > line6 - > iso_buffers ; + + i ) {
2010-08-12 01:35:30 +02:00
ret = submit_audio_in_urb ( line6pcm ) ;
2009-02-27 22:39:22 -08:00
if ( ret < 0 )
2015-01-27 12:50:44 +01:00
break ;
2009-02-27 22:39:22 -08:00
}
2009-02-27 19:43:04 -08:00
2015-01-27 12:50:44 +01:00
return ret ;
2009-02-27 19:43:04 -08:00
}
/*
2010-08-12 01:35:30 +02:00
Copy data into ALSA capture buffer .
*/
void line6_capture_copy ( struct snd_line6_pcm * line6pcm , char * fbuf , int fsize )
{
struct snd_pcm_substream * substream =
get_substream ( line6pcm , SNDRV_PCM_STREAM_CAPTURE ) ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
2016-09-18 20:59:24 +02:00
const int bytes_per_frame =
line6pcm - > properties - > bytes_per_channel *
line6pcm - > properties - > capture_hw . channels_max ;
2010-08-12 01:35:30 +02:00
int frames = fsize / bytes_per_frame ;
2010-12-07 23:38:02 +01:00
if ( runtime = = NULL )
2010-09-17 23:33:25 +02:00
return ;
2015-01-23 16:10:57 +01:00
if ( line6pcm - > in . pos_done + frames > runtime - > buffer_size ) {
2010-08-12 01:35:30 +02:00
/*
2010-08-23 01:08:25 +02:00
The transferred area goes over buffer boundary ,
copy two separate chunks .
*/
2010-08-12 01:35:30 +02:00
int len ;
2014-03-18 23:59:46 +02:00
2015-01-23 16:10:57 +01:00
len = runtime - > buffer_size - line6pcm - > in . pos_done ;
2010-08-12 01:35:30 +02:00
if ( len > 0 ) {
memcpy ( runtime - > dma_area +
2015-01-23 16:10:57 +01:00
line6pcm - > in . pos_done * bytes_per_frame , fbuf ,
2010-08-12 01:35:30 +02:00
len * bytes_per_frame ) ;
memcpy ( runtime - > dma_area , fbuf + len * bytes_per_frame ,
( frames - len ) * bytes_per_frame ) ;
2010-09-21 16:58:00 -07:00
} else {
/* this is somewhat paranoid */
dev_err ( line6pcm - > line6 - > ifcdev ,
" driver bug: len = %d \n " , len ) ;
}
2010-08-12 01:35:30 +02:00
} else {
/* copy single chunk */
memcpy ( runtime - > dma_area +
2015-01-23 16:10:57 +01:00
line6pcm - > in . pos_done * bytes_per_frame , fbuf , fsize ) ;
2010-08-12 01:35:30 +02:00
}
2015-01-23 16:10:57 +01:00
line6pcm - > in . pos_done + = frames ;
if ( line6pcm - > in . pos_done > = runtime - > buffer_size )
line6pcm - > in . pos_done - = runtime - > buffer_size ;
2010-08-12 01:35:30 +02:00
}
void line6_capture_check_period ( struct snd_line6_pcm * line6pcm , int length )
{
struct snd_pcm_substream * substream =
get_substream ( line6pcm , SNDRV_PCM_STREAM_CAPTURE ) ;
2015-01-23 16:10:57 +01:00
line6pcm - > in . bytes + = length ;
if ( line6pcm - > in . bytes > = line6pcm - > in . period ) {
line6pcm - > in . bytes % = line6pcm - > in . period ;
2015-01-27 12:50:44 +01:00
spin_unlock ( & line6pcm - > in . lock ) ;
2010-08-12 01:35:30 +02:00
snd_pcm_period_elapsed ( substream ) ;
2015-01-27 12:50:44 +01:00
spin_lock ( & line6pcm - > in . lock ) ;
2010-08-12 01:35:30 +02:00
}
}
/*
2010-09-21 16:58:00 -07:00
* Callback for completed capture URB .
*/
2009-02-27 20:28:04 -08:00
static void audio_in_callback ( struct urb * urb )
2009-02-27 19:43:04 -08:00
{
int i , index , length = 0 , shutdown = 0 ;
unsigned long flags ;
2010-08-12 01:35:30 +02:00
struct snd_line6_pcm * line6pcm = ( struct snd_line6_pcm * ) urb - > context ;
2015-01-23 16:10:57 +01:00
line6pcm - > in . last_frame = urb - > start_frame ;
2009-02-27 19:43:04 -08:00
/* find index of URB */
2016-09-18 20:59:21 +02:00
for ( index = 0 ; index < line6pcm - > line6 - > iso_buffers ; + + index )
2015-01-23 16:10:57 +01:00
if ( urb = = line6pcm - > in . urbs [ index ] )
2009-02-27 19:43:04 -08:00
break ;
2015-01-23 16:10:57 +01:00
spin_lock_irqsave ( & line6pcm - > in . lock , flags ) ;
2009-02-27 19:43:04 -08:00
2009-02-27 22:39:22 -08:00
for ( i = 0 ; i < LINE6_ISO_PACKETS ; + + i ) {
2009-02-27 19:43:04 -08:00
char * fbuf ;
int fsize ;
struct usb_iso_packet_descriptor * fin = & urb - > iso_frame_desc [ i ] ;
2010-08-23 01:08:25 +02:00
if ( fin - > status = = - EXDEV ) {
2009-02-27 19:43:04 -08:00
shutdown = 1 ;
break ;
}
fbuf = urb - > transfer_buffer + fin - > offset ;
fsize = fin - > actual_length ;
2010-08-12 01:35:30 +02:00
2016-09-18 20:59:23 +02:00
if ( fsize > line6pcm - > max_packet_size_in ) {
2010-08-12 01:35:30 +02:00
dev_err ( line6pcm - > line6 - > ifcdev ,
" driver and/or device bug: packet too large (%d > %d) \n " ,
2016-09-18 20:59:23 +02:00
fsize , line6pcm - > max_packet_size_in ) ;
2010-08-12 01:35:30 +02:00
}
2009-02-27 19:43:04 -08:00
length + = fsize ;
2016-09-18 20:59:22 +02:00
BUILD_BUG_ON_MSG ( LINE6_ISO_PACKETS ! = 1 ,
" The following code assumes LINE6_ISO_PACKETS == 1 " ) ;
/* TODO:
* Also , if iso_buffers ! = 2 , the prev frame is almost random at
* playback side .
* This needs to be redesigned . It should be " stable " , but we may
* experience sync problems on such high - speed configs .
*/
2010-08-12 01:35:30 +02:00
line6pcm - > prev_fbuf = fbuf ;
2016-09-18 20:59:24 +02:00
line6pcm - > prev_fsize = fsize /
( line6pcm - > properties - > bytes_per_channel *
line6pcm - > properties - > capture_hw . channels_max ) ;
2009-02-27 19:43:04 -08:00
2015-01-27 15:24:09 +01:00
if ( ! test_bit ( LINE6_STREAM_IMPULSE , & line6pcm - > in . running ) & &
test_bit ( LINE6_STREAM_PCM , & line6pcm - > in . running ) & &
fsize > 0 )
line6_capture_copy ( line6pcm , fbuf , fsize ) ;
2009-02-27 19:43:04 -08:00
}
2015-01-23 16:10:57 +01:00
clear_bit ( index , & line6pcm - > in . active_urbs ) ;
2009-02-27 19:43:04 -08:00
2015-01-23 16:10:57 +01:00
if ( test_and_clear_bit ( index , & line6pcm - > in . unlink_urbs ) )
2009-02-27 19:43:04 -08:00
shutdown = 1 ;
2009-02-27 22:39:22 -08:00
if ( ! shutdown ) {
2010-08-12 01:35:30 +02:00
submit_audio_in_urb ( line6pcm ) ;
2009-02-27 19:43:04 -08:00
2015-01-27 15:24:09 +01:00
if ( ! test_bit ( LINE6_STREAM_IMPULSE , & line6pcm - > in . running ) & &
test_bit ( LINE6_STREAM_PCM , & line6pcm - > in . running ) )
line6_capture_check_period ( line6pcm , length ) ;
2009-02-27 19:43:04 -08:00
}
2015-01-27 12:50:44 +01:00
spin_unlock_irqrestore ( & line6pcm - > in . lock , flags ) ;
2009-02-27 19:43:04 -08:00
}
/* open capture callback */
static int snd_line6_capture_open ( struct snd_pcm_substream * substream )
{
int err ;
struct snd_pcm_runtime * runtime = substream - > runtime ;
struct snd_line6_pcm * line6pcm = snd_pcm_substream_chip ( substream ) ;
2009-02-27 22:39:22 -08:00
err = snd_pcm_hw_constraint_ratdens ( runtime , 0 ,
SNDRV_PCM_HW_PARAM_RATE ,
2015-01-28 15:08:59 +01:00
& line6pcm - > properties - > rates ) ;
2009-02-27 22:39:22 -08:00
if ( err < 0 )
2009-02-27 19:43:04 -08:00
return err ;
2016-09-18 20:59:25 +02:00
line6_pcm_acquire ( line6pcm , LINE6_STREAM_CAPTURE_HELPER , false ) ;
2015-01-28 15:08:59 +01:00
runtime - > hw = line6pcm - > properties - > capture_hw ;
2009-02-27 19:43:04 -08:00
return 0 ;
}
/* close capture callback */
static int snd_line6_capture_close ( struct snd_pcm_substream * substream )
{
2016-09-18 20:59:25 +02:00
struct snd_line6_pcm * line6pcm = snd_pcm_substream_chip ( substream ) ;
line6_pcm_release ( line6pcm , LINE6_STREAM_CAPTURE_HELPER ) ;
2009-02-27 19:43:04 -08:00
return 0 ;
}
/* capture operators */
struct snd_pcm_ops snd_line6_capture_ops = {
2010-08-23 01:08:25 +02:00
. open = snd_line6_capture_open ,
. close = snd_line6_capture_close ,
. ioctl = snd_pcm_lib_ioctl ,
2015-01-27 15:24:09 +01:00
. hw_params = snd_line6_hw_params ,
. hw_free = snd_line6_hw_free ,
2010-08-23 01:08:25 +02:00
. prepare = snd_line6_prepare ,
. trigger = snd_line6_trigger ,
2015-01-27 15:41:27 +01:00
. pointer = snd_line6_pointer ,
2009-02-27 19:43:04 -08:00
} ;
2010-08-12 01:35:30 +02:00
int line6_create_audio_in_urbs ( struct snd_line6_pcm * line6pcm )
2009-02-27 19:43:04 -08:00
{
2015-01-12 12:42:55 -08:00
struct usb_line6 * line6 = line6pcm - > line6 ;
2009-02-27 19:43:04 -08:00
int i ;
2016-09-18 20:59:21 +02:00
line6pcm - > in . urbs = kzalloc (
sizeof ( struct urb * ) * line6 - > iso_buffers , GFP_KERNEL ) ;
if ( line6pcm - > in . urbs = = NULL )
return - ENOMEM ;
2009-02-27 19:43:04 -08:00
/* create audio URBs and fill in constant values: */
2016-09-18 20:59:21 +02:00
for ( i = 0 ; i < line6 - > iso_buffers ; + + i ) {
2009-02-27 19:43:04 -08:00
struct urb * urb ;
/* URB for audio in: */
2015-01-23 16:10:57 +01:00
urb = line6pcm - > in . urbs [ i ] =
2009-11-15 22:17:52 -06:00
usb_alloc_urb ( LINE6_ISO_PACKETS , GFP_KERNEL ) ;
2009-02-27 19:43:04 -08:00
2015-01-19 15:05:10 +01:00
if ( urb = = NULL )
2009-02-27 19:43:04 -08:00
return - ENOMEM ;
2015-01-12 12:42:55 -08:00
urb - > dev = line6 - > usbdev ;
2009-11-15 22:17:52 -06:00
urb - > pipe =
2015-01-12 12:42:55 -08:00
usb_rcvisocpipe ( line6 - > usbdev ,
line6 - > properties - > ep_audio_r &
2010-08-23 01:08:25 +02:00
USB_ENDPOINT_NUMBER_MASK ) ;
2009-02-27 19:43:04 -08:00
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > start_frame = - 1 ;
urb - > number_of_packets = LINE6_ISO_PACKETS ;
urb - > interval = LINE6_ISO_INTERVAL ;
urb - > error_count = 0 ;
urb - > complete = audio_in_callback ;
}
return 0 ;
}