Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
56599bb0
Commit
56599bb0
authored
Apr 18, 2012
by
Takashi Iwai
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/usb-endpoint' into topic/misc
parents
7536c301
22026c1a
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
1401 additions
and
825 deletions
+1401
-825
sound/usb/card.c
sound/usb/card.c
+9
-1
sound/usb/card.h
sound/usb/card.h
+62
-15
sound/usb/endpoint.c
sound/usb/endpoint.c
+894
-707
sound/usb/endpoint.h
sound/usb/endpoint.h
+20
-12
sound/usb/pcm.c
sound/usb/pcm.c
+365
-76
sound/usb/proc.c
sound/usb/proc.c
+21
-11
sound/usb/stream.c
sound/usb/stream.c
+28
-3
sound/usb/usbaudio.h
sound/usb/usbaudio.h
+2
-0
No files found.
sound/usb/card.c
View file @
56599bb0
...
...
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
subs
=
&
as
->
substream
[
idx
];
if
(
!
subs
->
num_formats
)
continue
;
snd_usb_release_substream_urbs
(
subs
,
1
);
subs
->
interface
=
-
1
;
subs
->
data_endpoint
=
NULL
;
subs
->
sync_endpoint
=
NULL
;
}
}
...
...
@@ -276,6 +277,7 @@ static int snd_usb_create_streams(struct snd_usb_audio *chip, int ctrlif)
static
int
snd_usb_audio_free
(
struct
snd_usb_audio
*
chip
)
{
mutex_destroy
(
&
chip
->
mutex
);
kfree
(
chip
);
return
0
;
}
...
...
@@ -336,6 +338,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
return
-
ENOMEM
;
}
mutex_init
(
&
chip
->
mutex
);
mutex_init
(
&
chip
->
shutdown_mutex
);
chip
->
index
=
idx
;
chip
->
dev
=
dev
;
...
...
@@ -348,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
chip
->
usb_id
=
USB_ID
(
le16_to_cpu
(
dev
->
descriptor
.
idVendor
),
le16_to_cpu
(
dev
->
descriptor
.
idProduct
));
INIT_LIST_HEAD
(
&
chip
->
pcm_list
);
INIT_LIST_HEAD
(
&
chip
->
ep_list
);
INIT_LIST_HEAD
(
&
chip
->
midi_list
);
INIT_LIST_HEAD
(
&
chip
->
mixer_list
);
...
...
@@ -565,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
list_for_each
(
p
,
&
chip
->
pcm_list
)
{
snd_usb_stream_disconnect
(
p
);
}
/* release the endpoint resources */
list_for_each
(
p
,
&
chip
->
ep_list
)
{
snd_usb_endpoint_free
(
p
);
}
/* release the midi resources */
list_for_each
(
p
,
&
chip
->
midi_list
)
{
snd_usbmidi_disconnect
(
p
);
...
...
sound/usb/card.h
View file @
56599bb0
...
...
@@ -30,13 +30,17 @@ struct audioformat {
};
struct
snd_usb_substream
;
struct
snd_usb_endpoint
;
struct
snd_urb_ctx
{
struct
urb
*
urb
;
unsigned
int
buffer_size
;
/* size of data buffer, if data URB */
struct
snd_usb_substream
*
subs
;
struct
snd_usb_endpoint
*
ep
;
int
index
;
/* index for urb array */
int
packets
;
/* number of packets per urb */
int
packet_size
[
MAX_PACKS_HS
];
/* size of packets for next submission */
struct
list_head
ready_list
;
};
struct
snd_urb_ops
{
...
...
@@ -46,6 +50,60 @@ struct snd_urb_ops {
int
(
*
retire_sync
)(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
u
);
};
struct
snd_usb_endpoint
{
struct
snd_usb_audio
*
chip
;
int
use_count
;
int
ep_num
;
/* the referenced endpoint number */
int
type
;
/* SND_USB_ENDPOINT_TYPE_* */
unsigned
long
flags
;
void
(
*
prepare_data_urb
)
(
struct
snd_usb_substream
*
subs
,
struct
urb
*
urb
);
void
(
*
retire_data_urb
)
(
struct
snd_usb_substream
*
subs
,
struct
urb
*
urb
);
struct
snd_usb_substream
*
data_subs
;
struct
snd_usb_endpoint
*
sync_master
;
struct
snd_usb_endpoint
*
sync_slave
;
struct
snd_urb_ctx
urb
[
MAX_URBS
];
struct
snd_usb_packet_info
{
uint32_t
packet_size
[
MAX_PACKS_HS
];
int
packets
;
}
next_packet
[
MAX_URBS
];
int
next_packet_read_pos
,
next_packet_write_pos
;
struct
list_head
ready_playback_urbs
;
unsigned
int
nurbs
;
/* # urbs */
unsigned
long
active_mask
;
/* bitmask of active urbs */
unsigned
long
unlink_mask
;
/* bitmask of unlinked urbs */
char
*
syncbuf
;
/* sync buffer for all sync URBs */
dma_addr_t
sync_dma
;
/* DMA address of syncbuf */
unsigned
int
pipe
;
/* the data i/o pipe */
unsigned
int
freqn
;
/* nominal sampling rate in fs/fps in Q16.16 format */
unsigned
int
freqm
;
/* momentary sampling rate in fs/fps in Q16.16 format */
int
freqshift
;
/* how much to shift the feedback value to get Q16.16 */
unsigned
int
freqmax
;
/* maximum sampling rate, used for buffer management */
unsigned
int
phase
;
/* phase accumulator */
unsigned
int
maxpacksize
;
/* max packet size in bytes */
unsigned
int
maxframesize
;
/* max packet size in frames */
unsigned
int
curpacksize
;
/* current packet size in bytes (for capture) */
unsigned
int
curframesize
;
/* current packet size in frames (for capture) */
unsigned
int
syncmaxsize
;
/* sync endpoint packet size */
unsigned
int
fill_max
:
1
;
/* fill max packet size always */
unsigned
int
datainterval
;
/* log_2 of data packet interval */
unsigned
int
syncinterval
;
/* P for adaptive mode, 0 otherwise */
unsigned
char
silence_value
;
unsigned
int
stride
;
int
iface
,
alt_idx
;
spinlock_t
lock
;
struct
list_head
list
;
};
struct
snd_usb_substream
{
struct
snd_usb_stream
*
stream
;
struct
usb_device
*
dev
;
...
...
@@ -57,21 +115,6 @@ struct snd_usb_substream {
unsigned
int
cur_rate
;
/* current rate (for hw_params callback) */
unsigned
int
period_bytes
;
/* current period bytes (for hw_params callback) */
unsigned
int
altset_idx
;
/* USB data format: index of alternate setting */
unsigned
int
datapipe
;
/* the data i/o pipe */
unsigned
int
syncpipe
;
/* 1 - async out or adaptive in */
unsigned
int
datainterval
;
/* log_2 of data packet interval */
unsigned
int
syncinterval
;
/* P for adaptive mode, 0 otherwise */
unsigned
int
freqn
;
/* nominal sampling rate in fs/fps in Q16.16 format */
unsigned
int
freqm
;
/* momentary sampling rate in fs/fps in Q16.16 format */
int
freqshift
;
/* how much to shift the feedback value to get Q16.16 */
unsigned
int
freqmax
;
/* maximum sampling rate, used for buffer management */
unsigned
int
phase
;
/* phase accumulator */
unsigned
int
maxpacksize
;
/* max packet size in bytes */
unsigned
int
maxframesize
;
/* max packet size in frames */
unsigned
int
curpacksize
;
/* current packet size in bytes (for capture) */
unsigned
int
curframesize
;
/* current packet size in frames (for capture) */
unsigned
int
syncmaxsize
;
/* sync endpoint packet size */
unsigned
int
fill_max
:
1
;
/* fill max packet size always */
unsigned
int
txfr_quirk
:
1
;
/* allow sub-frame alignment */
unsigned
int
fmt_type
;
/* USB audio format type (1-3) */
...
...
@@ -87,6 +130,10 @@ struct snd_usb_substream {
struct
snd_urb_ctx
syncurb
[
SYNC_URBS
];
/* sync urb table */
char
*
syncbuf
;
/* sync buffer for all sync URBs */
dma_addr_t
sync_dma
;
/* DMA address of syncbuf */
/* data and sync endpoints for this stream */
struct
snd_usb_endpoint
*
data_endpoint
;
struct
snd_usb_endpoint
*
sync_endpoint
;
unsigned
long
flags
;
u64
formats
;
/* format bitmasks (all or'ed) */
unsigned
int
num_formats
;
/* number of supported audio formats (list) */
...
...
sound/usb/endpoint.c
View file @
56599bb0
...
...
@@ -20,9 +20,11 @@
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "usbaudio.h"
#include "helper.h"
...
...
@@ -30,6 +32,35 @@
#include "endpoint.h"
#include "pcm.h"
#define EP_FLAG_ACTIVATED 0
#define EP_FLAG_RUNNING 1
/*
* snd_usb_endpoint is a model that abstracts everything related to an
* USB endpoint and its streaming.
*
* There are functions to activate and deactivate the streaming URBs and
* optinal callbacks to let the pcm logic handle the actual content of the
* packets for playback and record. Thus, the bus streaming and the audio
* handlers are fully decoupled.
*
* There are two different types of endpoints in for audio applications.
*
* SND_USB_ENDPOINT_TYPE_DATA handles full audio data payload for both
* inbound and outbound traffic.
*
* SND_USB_ENDPOINT_TYPE_SYNC are for inbound traffic only and expect the
* payload to carry Q16.16 formatted sync information (3 or 4 bytes).
*
* Each endpoint has to be configured (by calling
* snd_usb_endpoint_set_params()) before it can be used.
*
* The model incorporates a reference counting, so that multiple users
* can call snd_usb_endpoint_start() and snd_usb_endpoint_stop(), and
* only the first user will effectively start the URBs, and only the last
* one will tear them down again.
*/
/*
* convert a sampling rate into our full speed format (fs/1000 in Q16.16)
* this will overflow at approx 524 kHz
...
...
@@ -49,71 +80,414 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
}
/*
*
unlink active urbs.
*
release a urb data
*/
static
int
deactivate_urbs
(
struct
snd_usb_substream
*
subs
,
int
force
,
int
can_sleep
)
static
void
release_urb_ctx
(
struct
snd_urb_ctx
*
u
)
{
struct
snd_usb_audio
*
chip
=
subs
->
stream
->
chip
;
unsigned
int
i
;
int
async
;
if
(
u
->
buffer_size
)
usb_free_coherent
(
u
->
ep
->
chip
->
dev
,
u
->
buffer_size
,
u
->
urb
->
transfer_buffer
,
u
->
urb
->
transfer_dma
);
usb_free_urb
(
u
->
urb
);
u
->
urb
=
NULL
;
}
static
const
char
*
usb_error_string
(
int
err
)
{
switch
(
err
)
{
case
-
ENODEV
:
return
"no device"
;
case
-
ENOENT
:
return
"endpoint not enabled"
;
case
-
EPIPE
:
return
"endpoint stalled"
;
case
-
ENOSPC
:
return
"not enough bandwidth"
;
case
-
ESHUTDOWN
:
return
"device disabled"
;
case
-
EHOSTUNREACH
:
return
"device suspended"
;
case
-
EINVAL
:
case
-
EAGAIN
:
case
-
EFBIG
:
case
-
EMSGSIZE
:
return
"internal error"
;
default:
return
"unknown error"
;
}
}
/**
* snd_usb_endpoint_implicit_feedback_sink: Report endpoint usage type
*
* @ep: The endpoint
*
* Determine whether an endpoint is driven by an implicit feedback
* data endpoint source.
*/
int
snd_usb_endpoint_implict_feedback_sink
(
struct
snd_usb_endpoint
*
ep
)
{
return
ep
->
sync_master
&&
ep
->
sync_master
->
type
==
SND_USB_ENDPOINT_TYPE_DATA
&&
ep
->
type
==
SND_USB_ENDPOINT_TYPE_DATA
&&
usb_pipeout
(
ep
->
pipe
);
}
subs
->
running
=
0
;
/*
* For streaming based on information derived from sync endpoints,
* prepare_outbound_urb_sizes() will call next_packet_size() to
* determine the number of samples to be sent in the next packet.
*
* For implicit feedback, next_packet_size() is unused.
*/
static
int
next_packet_size
(
struct
snd_usb_endpoint
*
ep
)
{
unsigned
long
flags
;
int
ret
;
if
(
!
force
&&
subs
->
stream
->
chip
->
shutdown
)
/* to be sure... */
return
-
EBADFD
;
if
(
ep
->
fill_max
)
return
ep
->
maxframesize
;
async
=
!
can_sleep
&&
chip
->
async_unlink
;
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
ep
->
phase
=
(
ep
->
phase
&
0xffff
)
+
(
ep
->
freqm
<<
ep
->
datainterval
);
ret
=
min
(
ep
->
phase
>>
16
,
ep
->
maxframesize
);
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
if
(
!
async
&&
in_interrupt
())
return
0
;
return
ret
;
}
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
if
(
test_bit
(
i
,
&
subs
->
active_mask
))
{
if
(
!
test_and_set_bit
(
i
,
&
subs
->
unlink_mask
))
{
struct
urb
*
u
=
subs
->
dataurb
[
i
].
urb
;
if
(
async
)
usb_unlink_urb
(
u
);
else
usb_kill_urb
(
u
);
static
void
retire_outbound_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_urb_ctx
*
urb_ctx
)
{
if
(
ep
->
retire_data_urb
)
ep
->
retire_data_urb
(
ep
->
data_subs
,
urb_ctx
->
urb
);
}
static
void
retire_inbound_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_urb_ctx
*
urb_ctx
)
{
struct
urb
*
urb
=
urb_ctx
->
urb
;
if
(
ep
->
sync_slave
)
snd_usb_handle_sync_urb
(
ep
->
sync_slave
,
ep
,
urb
);
if
(
ep
->
retire_data_urb
)
ep
->
retire_data_urb
(
ep
->
data_subs
,
urb
);
}
static
void
prepare_outbound_urb_sizes
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_urb_ctx
*
ctx
)
{
int
i
;
for
(
i
=
0
;
i
<
ctx
->
packets
;
++
i
)
ctx
->
packet_size
[
i
]
=
next_packet_size
(
ep
);
}
/*
* Prepare a PLAYBACK urb for submission to the bus.
*/
static
void
prepare_outbound_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_urb_ctx
*
ctx
)
{
int
i
;
struct
urb
*
urb
=
ctx
->
urb
;
unsigned
char
*
cp
=
urb
->
transfer_buffer
;
urb
->
dev
=
ep
->
chip
->
dev
;
/* we need to set this at each time */
switch
(
ep
->
type
)
{
case
SND_USB_ENDPOINT_TYPE_DATA
:
if
(
ep
->
prepare_data_urb
)
{
ep
->
prepare_data_urb
(
ep
->
data_subs
,
urb
);
}
else
{
/* no data provider, so send silence */
unsigned
int
offs
=
0
;
for
(
i
=
0
;
i
<
ctx
->
packets
;
++
i
)
{
int
counts
=
ctx
->
packet_size
[
i
];
urb
->
iso_frame_desc
[
i
].
offset
=
offs
*
ep
->
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
ep
->
stride
;
offs
+=
counts
;
}
urb
->
number_of_packets
=
ctx
->
packets
;
urb
->
transfer_buffer_length
=
offs
*
ep
->
stride
;
memset
(
urb
->
transfer_buffer
,
ep
->
silence_value
,
offs
*
ep
->
stride
);
}
break
;
case
SND_USB_ENDPOINT_TYPE_SYNC
:
if
(
snd_usb_get_speed
(
ep
->
chip
->
dev
)
>=
USB_SPEED_HIGH
)
{
/*
* fill the length and offset of each urb descriptor.
* the fixed 12.13 frequency is passed as 16.16 through the pipe.
*/
urb
->
iso_frame_desc
[
0
].
length
=
4
;
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
cp
[
0
]
=
ep
->
freqn
;
cp
[
1
]
=
ep
->
freqn
>>
8
;
cp
[
2
]
=
ep
->
freqn
>>
16
;
cp
[
3
]
=
ep
->
freqn
>>
24
;
}
else
{
/*
* fill the length and offset of each urb descriptor.
* the fixed 10.14 frequency is passed through the pipe.
*/
urb
->
iso_frame_desc
[
0
].
length
=
3
;
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
cp
[
0
]
=
ep
->
freqn
>>
2
;
cp
[
1
]
=
ep
->
freqn
>>
10
;
cp
[
2
]
=
ep
->
freqn
>>
18
;
}
break
;
}
if
(
subs
->
syncpipe
)
{
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
if
(
test_bit
(
i
+
16
,
&
subs
->
active_mask
))
{
if
(
!
test_and_set_bit
(
i
+
16
,
&
subs
->
unlink_mask
))
{
struct
urb
*
u
=
subs
->
syncurb
[
i
].
urb
;
if
(
async
)
usb_unlink_urb
(
u
);
else
usb_kill_urb
(
u
);
}
}
}
/*
* Prepare a CAPTURE or SYNC urb for submission to the bus.
*/
static
inline
void
prepare_inbound_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_urb_ctx
*
urb_ctx
)
{
int
i
,
offs
;
struct
urb
*
urb
=
urb_ctx
->
urb
;
urb
->
dev
=
ep
->
chip
->
dev
;
/* we need to set this at each time */
switch
(
ep
->
type
)
{
case
SND_USB_ENDPOINT_TYPE_DATA
:
offs
=
0
;
for
(
i
=
0
;
i
<
urb_ctx
->
packets
;
i
++
)
{
urb
->
iso_frame_desc
[
i
].
offset
=
offs
;
urb
->
iso_frame_desc
[
i
].
length
=
ep
->
curpacksize
;
offs
+=
ep
->
curpacksize
;
}
urb
->
transfer_buffer_length
=
offs
;
urb
->
number_of_packets
=
urb_ctx
->
packets
;
break
;
case
SND_USB_ENDPOINT_TYPE_SYNC
:
urb
->
iso_frame_desc
[
0
].
length
=
min
(
4u
,
ep
->
syncmaxsize
);
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
break
;
}
return
0
;
}
/*
* Send output urbs that have been prepared previously. Urbs are dequeued
* from ep->ready_playback_urbs and in case there there aren't any available
* or there are no packets that have been prepared, this function does
* nothing.
*
* The reason why the functionality of sending and preparing urbs is separated
* is that host controllers don't guarantee an ordering in returing inbound
* and outbound packets to their submitters.
*
* This function is only used for implicit feedback endpoints. For endpoints
* driven by sync endpoints, urbs are submitted from their completion handler.
*/
static
void
queue_pending_output_urbs
(
struct
snd_usb_endpoint
*
ep
)
{
while
(
test_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
))
{
unsigned
long
flags
;
struct
snd_usb_packet_info
*
packet
;
struct
snd_urb_ctx
*
ctx
=
NULL
;
struct
urb
*
urb
;
int
err
,
i
;
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
if
(
ep
->
next_packet_read_pos
!=
ep
->
next_packet_write_pos
)
{
packet
=
ep
->
next_packet
+
ep
->
next_packet_read_pos
;
ep
->
next_packet_read_pos
++
;
ep
->
next_packet_read_pos
%=
MAX_URBS
;
/* take URB out of FIFO */
if
(
!
list_empty
(
&
ep
->
ready_playback_urbs
))
ctx
=
list_first_entry
(
&
ep
->
ready_playback_urbs
,
struct
snd_urb_ctx
,
ready_list
);
}
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
if
(
ctx
==
NULL
)
return
;
list_del_init
(
&
ctx
->
ready_list
);
urb
=
ctx
->
urb
;
/* copy over the length information */
for
(
i
=
0
;
i
<
packet
->
packets
;
i
++
)
ctx
->
packet_size
[
i
]
=
packet
->
packet_size
[
i
];
/* call the data handler to fill in playback data */
prepare_outbound_urb
(
ep
,
ctx
);
err
=
usb_submit_urb
(
ctx
->
urb
,
GFP_ATOMIC
);
if
(
err
<
0
)
snd_printk
(
KERN_ERR
"Unable to submit urb #%d: %d (urb %p)
\n
"
,
ctx
->
index
,
err
,
ctx
->
urb
);
else
set_bit
(
ctx
->
index
,
&
ep
->
active_mask
);
}
}
/*
*
release a urb data
*
complete callback for urbs
*/
static
void
release_urb_ctx
(
struct
snd_urb_ctx
*
u
)
static
void
snd_complete_urb
(
struct
urb
*
urb
)
{
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
struct
snd_usb_endpoint
*
ep
=
ctx
->
ep
;
int
err
;
if
(
unlikely
(
urb
->
status
==
-
ENOENT
||
/* unlinked */
urb
->
status
==
-
ENODEV
||
/* device removed */
urb
->
status
==
-
ECONNRESET
||
/* unlinked */
urb
->
status
==
-
ESHUTDOWN
||
/* device disabled */
ep
->
chip
->
shutdown
))
/* device disconnected */
goto
exit_clear
;
if
(
usb_pipeout
(
ep
->
pipe
))
{
retire_outbound_urb
(
ep
,
ctx
);
/* can be stopped during retire callback */
if
(
unlikely
(
!
test_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
)))
goto
exit_clear
;
if
(
snd_usb_endpoint_implict_feedback_sink
(
ep
))
{
unsigned
long
flags
;
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
list_add_tail
(
&
ctx
->
ready_list
,
&
ep
->
ready_playback_urbs
);
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
queue_pending_output_urbs
(
ep
);
goto
exit_clear
;
}
prepare_outbound_urb_sizes
(
ep
,
ctx
);
prepare_outbound_urb
(
ep
,
ctx
);
}
else
{
retire_inbound_urb
(
ep
,
ctx
);
/* can be stopped during retire callback */
if
(
unlikely
(
!
test_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
)))
goto
exit_clear
;
prepare_inbound_urb
(
ep
,
ctx
);
}
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
==
0
)
return
;
snd_printk
(
KERN_ERR
"cannot submit urb (err = %d)
\n
"
,
err
);
//snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
exit_clear:
clear_bit
(
ctx
->
index
,
&
ep
->
active_mask
);
}
/**
* snd_usb_add_endpoint: Add an endpoint to an audio chip
*
* @chip: The chip
* @alts: The USB host interface
* @ep_num: The number of the endpoint to use
* @direction: SNDRV_PCM_STREAM_PLAYBACK or SNDRV_PCM_STREAM_CAPTURE
* @type: SND_USB_ENDPOINT_TYPE_DATA or SND_USB_ENDPOINT_TYPE_SYNC
*
* If the requested endpoint has not been added to the given chip before,
* a new instance is created. Otherwise, a pointer to the previoulsy
* created instance is returned. In case of any error, NULL is returned.
*
* New endpoints will be added to chip->ep_list and must be freed by
* calling snd_usb_endpoint_free().
*/
struct
snd_usb_endpoint
*
snd_usb_add_endpoint
(
struct
snd_usb_audio
*
chip
,
struct
usb_host_interface
*
alts
,
int
ep_num
,
int
direction
,
int
type
)
{
if
(
u
->
urb
)
{
if
(
u
->
buffer_size
)
usb_free_coherent
(
u
->
subs
->
dev
,
u
->
buffer_size
,
u
->
urb
->
transfer_buffer
,
u
->
urb
->
transfer_dma
);
usb_free_urb
(
u
->
urb
);
u
->
urb
=
NULL
;
struct
list_head
*
p
;
struct
snd_usb_endpoint
*
ep
;
int
ret
,
is_playback
=
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
mutex_lock
(
&
chip
->
mutex
);
list_for_each
(
p
,
&
chip
->
ep_list
)
{
ep
=
list_entry
(
p
,
struct
snd_usb_endpoint
,
list
);
if
(
ep
->
ep_num
==
ep_num
&&
ep
->
iface
==
alts
->
desc
.
bInterfaceNumber
&&
ep
->
alt_idx
==
alts
->
desc
.
bAlternateSetting
)
{
snd_printdd
(
KERN_DEBUG
"Re-using EP %x in iface %d,%d @%p
\n
"
,
ep_num
,
ep
->
iface
,
ep
->
alt_idx
,
ep
);
goto
__exit_unlock
;
}
}
snd_printdd
(
KERN_DEBUG
"Creating new %s %s endpoint #%x
\n
"
,
is_playback
?
"playback"
:
"capture"
,
type
==
SND_USB_ENDPOINT_TYPE_DATA
?
"data"
:
"sync"
,
ep_num
);
/* select the alt setting once so the endpoints become valid */
ret
=
usb_set_interface
(
chip
->
dev
,
alts
->
desc
.
bInterfaceNumber
,
alts
->
desc
.
bAlternateSetting
);
if
(
ret
<
0
)
{
snd_printk
(
KERN_ERR
"%s(): usb_set_interface() failed, ret = %d
\n
"
,
__func__
,
ret
);
ep
=
NULL
;
goto
__exit_unlock
;
}
ep
=
kzalloc
(
sizeof
(
*
ep
),
GFP_KERNEL
);
if
(
!
ep
)
goto
__exit_unlock
;
ep
->
chip
=
chip
;
spin_lock_init
(
&
ep
->
lock
);
ep
->
type
=
type
;
ep
->
ep_num
=
ep_num
;
ep
->
iface
=
alts
->
desc
.
bInterfaceNumber
;
ep
->
alt_idx
=
alts
->
desc
.
bAlternateSetting
;
INIT_LIST_HEAD
(
&
ep
->
ready_playback_urbs
);
ep_num
&=
USB_ENDPOINT_NUMBER_MASK
;
if
(
is_playback
)
ep
->
pipe
=
usb_sndisocpipe
(
chip
->
dev
,
ep_num
);
else
ep
->
pipe
=
usb_rcvisocpipe
(
chip
->
dev
,
ep_num
);
if
(
type
==
SND_USB_ENDPOINT_TYPE_SYNC
)
{
if
(
get_endpoint
(
alts
,
1
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
get_endpoint
(
alts
,
1
)
->
bRefresh
>=
1
&&
get_endpoint
(
alts
,
1
)
->
bRefresh
<=
9
)
ep
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bRefresh
;
else
if
(
snd_usb_get_speed
(
chip
->
dev
)
==
USB_SPEED_FULL
)
ep
->
syncinterval
=
1
;
else
if
(
get_endpoint
(
alts
,
1
)
->
bInterval
>=
1
&&
get_endpoint
(
alts
,
1
)
->
bInterval
<=
16
)
ep
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bInterval
-
1
;
else
ep
->
syncinterval
=
3
;
ep
->
syncmaxsize
=
le16_to_cpu
(
get_endpoint
(
alts
,
1
)
->
wMaxPacketSize
);
}
list_add_tail
(
&
ep
->
list
,
&
chip
->
ep_list
);
__exit_unlock:
mutex_unlock
(
&
chip
->
mutex
);
return
ep
;
}
/*
* wait until all urbs are processed.
*/
static
int
wait_clear_urbs
(
struct
snd_usb_
substream
*
subs
)
static
int
wait_clear_urbs
(
struct
snd_usb_
endpoint
*
ep
)
{
unsigned
long
end_time
=
jiffies
+
msecs_to_jiffies
(
1000
);
unsigned
int
i
;
...
...
@@ -121,153 +495,148 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
do
{
alive
=
0
;
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
if
(
test_bit
(
i
,
&
subs
->
active_mask
))
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
if
(
test_bit
(
i
,
&
ep
->
active_mask
))
alive
++
;
}
if
(
subs
->
syncpipe
)
{
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
if
(
test_bit
(
i
+
16
,
&
subs
->
active_mask
))
alive
++
;
}
}
if
(
!
alive
)
if
(
!
alive
)
break
;
schedule_timeout_uninterruptible
(
1
);
}
while
(
time_before
(
jiffies
,
end_time
));
if
(
alive
)
snd_printk
(
KERN_ERR
"timeout: still %d active urbs..
\n
"
,
alive
);
snd_printk
(
KERN_ERR
"timeout: still %d active urbs on EP #%x
\n
"
,
alive
,
ep
->
ep_num
);
return
0
;
}
/*
*
release a substream
*
unlink active urbs.
*/
void
snd_usb_release_substream_urbs
(
struct
snd_usb_substream
*
subs
,
int
force
)
static
int
deactivate_urbs
(
struct
snd_usb_endpoint
*
ep
,
int
force
,
int
can_sleep
)
{
int
i
;
unsigned
int
i
;
int
async
;
/* stop urbs (to be sure) */
deactivate_urbs
(
subs
,
force
,
1
);
wait_clear_urbs
(
subs
);
for
(
i
=
0
;
i
<
MAX_URBS
;
i
++
)
release_urb_ctx
(
&
subs
->
dataurb
[
i
]);
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
release_urb_ctx
(
&
subs
->
syncurb
[
i
]);
usb_free_coherent
(
subs
->
dev
,
SYNC_URBS
*
4
,
subs
->
syncbuf
,
subs
->
sync_dma
);
subs
->
syncbuf
=
NULL
;
subs
->
nurbs
=
0
;
}
if
(
!
force
&&
ep
->
chip
->
shutdown
)
/* to be sure... */
return
-
EBADFD
;
/*
* complete callback from data urb
*/
static
void
snd_complete_urb
(
struct
urb
*
urb
)
{
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
struct
snd_usb_substream
*
subs
=
ctx
->
subs
;
struct
snd_pcm_substream
*
substream
=
ctx
->
subs
->
pcm_substream
;
int
err
=
0
;
if
((
subs
->
running
&&
subs
->
ops
.
retire
(
subs
,
substream
->
runtime
,
urb
))
||
!
subs
->
running
||
/* can be stopped during retire callback */
(
err
=
subs
->
ops
.
prepare
(
subs
,
substream
->
runtime
,
urb
))
<
0
||
(
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
))
<
0
)
{
clear_bit
(
ctx
->
index
,
&
subs
->
active_mask
);
if
(
err
<
0
)
{
snd_printd
(
KERN_ERR
"cannot submit urb (err = %d)
\n
"
,
err
);
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_XRUN
);
async
=
!
can_sleep
&&
ep
->
chip
->
async_unlink
;
clear_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
);
INIT_LIST_HEAD
(
&
ep
->
ready_playback_urbs
);
ep
->
next_packet_read_pos
=
0
;
ep
->
next_packet_write_pos
=
0
;
if
(
!
async
&&
in_interrupt
())
return
0
;
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
if
(
test_bit
(
i
,
&
ep
->
active_mask
))
{
if
(
!
test_and_set_bit
(
i
,
&
ep
->
unlink_mask
))
{
struct
urb
*
u
=
ep
->
urb
[
i
].
urb
;
if
(
async
)
usb_unlink_urb
(
u
);
else
usb_kill_urb
(
u
);
}
}
}
}
return
0
;
}
/*
*
complete callback from sync urb
*
release an endpoint's urbs
*/
static
void
snd_complete_sync_urb
(
struct
urb
*
urb
)
static
void
release_urbs
(
struct
snd_usb_endpoint
*
ep
,
int
force
)
{
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
struct
snd_usb_substream
*
subs
=
ctx
->
subs
;
struct
snd_pcm_substream
*
substream
=
ctx
->
subs
->
pcm_substream
;
int
err
=
0
;
if
((
subs
->
running
&&
subs
->
ops
.
retire_sync
(
subs
,
substream
->
runtime
,
urb
))
||
!
subs
->
running
||
/* can be stopped during retire callback */
(
err
=
subs
->
ops
.
prepare_sync
(
subs
,
substream
->
runtime
,
urb
))
<
0
||
(
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
))
<
0
)
{
clear_bit
(
ctx
->
index
+
16
,
&
subs
->
active_mask
);
if
(
err
<
0
)
{
snd_printd
(
KERN_ERR
"cannot submit sync urb (err = %d)
\n
"
,
err
);
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_XRUN
);
}
}
}
int
i
;
/* route incoming urbs to nirvana */
ep
->
retire_data_urb
=
NULL
;
ep
->
prepare_data_urb
=
NULL
;
/* stop urbs */
deactivate_urbs
(
ep
,
force
,
1
);
wait_clear_urbs
(
ep
);
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
release_urb_ctx
(
&
ep
->
urb
[
i
]);
if
(
ep
->
syncbuf
)
usb_free_coherent
(
ep
->
chip
->
dev
,
SYNC_URBS
*
4
,
ep
->
syncbuf
,
ep
->
sync_dma
);
ep
->
syncbuf
=
NULL
;
ep
->
nurbs
=
0
;
}
/*
*
initialize a substream for plaback/capture
*
configure a data endpoint
*/
int
snd_usb_init_substream_urbs
(
struct
snd_usb_substream
*
subs
,
unsigned
int
period_byte
s
,
unsigned
int
rate
,
unsigned
int
frame_bits
)
static
int
data_ep_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_hw_params
*
hw_param
s
,
struct
audioformat
*
fmt
,
struct
snd_usb_endpoint
*
sync_ep
)
{
unsigned
int
maxsize
,
i
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
unsigned
int
urb_packs
,
total_packs
,
packs_per_ms
;
struct
snd_usb_audio
*
chip
=
subs
->
stream
->
chip
;
unsigned
int
maxsize
,
i
,
urb_packs
,
total_packs
,
packs_per_ms
;
int
period_bytes
=
params_period_bytes
(
hw_params
);
int
format
=
params_format
(
hw_params
);
int
is_playback
=
usb_pipeout
(
ep
->
pipe
);
int
frame_bits
=
snd_pcm_format_physical_width
(
params_format
(
hw_params
))
*
params_channels
(
hw_params
);
ep
->
datainterval
=
fmt
->
datainterval
;
ep
->
stride
=
frame_bits
>>
3
;
ep
->
silence_value
=
format
==
SNDRV_PCM_FORMAT_U8
?
0x80
:
0
;
/* calculate the frequency in 16.16 format */
if
(
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
)
subs
->
freqn
=
get_usb_full_speed_rate
(
rate
);
else
subs
->
freqn
=
get_usb_high_speed_rate
(
rate
);
subs
->
freqm
=
subs
->
freqn
;
subs
->
freqshift
=
INT_MIN
;
/* calculate max. frequency */
if
(
subs
->
maxpacksize
)
{
if
(
ep
->
maxpacksize
)
{
/* whatever fits into a max. size packet */
maxsize
=
subs
->
maxpacksize
;
subs
->
freqmax
=
(
maxsize
/
(
frame_bits
>>
3
))
<<
(
16
-
subs
->
datainterval
);
maxsize
=
ep
->
maxpacksize
;
ep
->
freqmax
=
(
maxsize
/
(
frame_bits
>>
3
))
<<
(
16
-
ep
->
datainterval
);
}
else
{
/* no max. packet size: just take 25% higher than nominal */
subs
->
freqmax
=
subs
->
freqn
+
(
subs
->
freqn
>>
2
);
maxsize
=
((
subs
->
freqmax
+
0xffff
)
*
(
frame_bits
>>
3
))
>>
(
16
-
subs
->
datainterval
);
ep
->
freqmax
=
ep
->
freqn
+
(
ep
->
freqn
>>
2
);
maxsize
=
((
ep
->
freqmax
+
0xffff
)
*
(
frame_bits
>>
3
))
>>
(
16
-
ep
->
datainterval
);
}
subs
->
phase
=
0
;
if
(
subs
->
fill_max
)
subs
->
curpacksize
=
subs
->
maxpacksize
;
if
(
ep
->
fill_max
)
ep
->
curpacksize
=
ep
->
maxpacksize
;
else
subs
->
curpacksize
=
maxsize
;
ep
->
curpacksize
=
maxsize
;
if
(
snd_usb_get_speed
(
subs
->
dev
)
!=
USB_SPEED_FULL
)
packs_per_ms
=
8
>>
subs
->
datainterval
;
if
(
snd_usb_get_speed
(
ep
->
chip
->
dev
)
!=
USB_SPEED_FULL
)
packs_per_ms
=
8
>>
ep
->
datainterval
;
else
packs_per_ms
=
1
;
if
(
is_playback
)
{
urb_packs
=
max
(
chip
->
nrpacks
,
1
);
urb_packs
=
min
(
urb_packs
,
(
unsigned
int
)
MAX_PACKS
);
}
else
if
(
is_playback
&&
!
snd_usb_endpoint_implict_feedback_sink
(
ep
)
)
{
urb_packs
=
max
(
ep
->
chip
->
nrpacks
,
1
);
urb_packs
=
min
(
urb_packs
,
(
unsigned
int
)
MAX_PACKS
);
}
else
{
urb_packs
=
1
;
}
urb_packs
*=
packs_per_ms
;
if
(
subs
->
syncpipe
)
urb_packs
=
min
(
urb_packs
,
1U
<<
subs
->
syncinterval
);
if
(
sync_ep
&&
!
snd_usb_endpoint_implict_feedback_sink
(
ep
))
urb_packs
=
min
(
urb_packs
,
1U
<<
sync_ep
->
syncinterval
);
/* decide how many packets to be used */
if
(
is_playback
)
{
if
(
is_playback
&&
!
snd_usb_endpoint_implict_feedback_sink
(
ep
)
)
{
unsigned
int
minsize
,
maxpacks
;
/* determine how small a packet can be */
minsize
=
(
subs
->
freqn
>>
(
16
-
subs
->
datainterval
))
minsize
=
(
ep
->
freqn
>>
(
16
-
ep
->
datainterval
))
*
(
frame_bits
>>
3
);
/* with sync from device, assume it can be 12% lower */
if
(
s
ubs
->
syncpipe
)
if
(
s
ync_ep
)
minsize
-=
minsize
>>
3
;
minsize
=
max
(
minsize
,
1u
);
total_packs
=
(
period_bytes
+
minsize
-
1
)
/
minsize
;
...
...
@@ -284,284 +653,466 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
urb_packs
>>=
1
;
total_packs
=
MAX_URBS
*
urb_packs
;
}
subs
->
nurbs
=
(
total_packs
+
urb_packs
-
1
)
/
urb_packs
;
if
(
subs
->
nurbs
>
MAX_URBS
)
{
ep
->
nurbs
=
(
total_packs
+
urb_packs
-
1
)
/
urb_packs
;
if
(
ep
->
nurbs
>
MAX_URBS
)
{
/* too much... */
subs
->
nurbs
=
MAX_URBS
;
ep
->
nurbs
=
MAX_URBS
;
total_packs
=
MAX_URBS
*
urb_packs
;
}
else
if
(
subs
->
nurbs
<
2
)
{
}
else
if
(
ep
->
nurbs
<
2
)
{
/* too little - we need at least two packets
* to ensure contiguous playback/capture
*/
subs
->
nurbs
=
2
;
ep
->
nurbs
=
2
;
}
/* allocate and initialize data urbs */
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
struct
snd_urb_ctx
*
u
=
&
subs
->
data
urb
[
i
];
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
struct
snd_urb_ctx
*
u
=
&
ep
->
urb
[
i
];
u
->
index
=
i
;
u
->
subs
=
subs
;
u
->
packets
=
(
i
+
1
)
*
total_packs
/
subs
->
nurbs
-
i
*
total_packs
/
subs
->
nurbs
;
u
->
ep
=
ep
;
u
->
packets
=
(
i
+
1
)
*
total_packs
/
ep
->
nurbs
-
i
*
total_packs
/
ep
->
nurbs
;
u
->
buffer_size
=
maxsize
*
u
->
packets
;
if
(
subs
->
fmt_type
==
UAC_FORMAT_TYPE_II
)
if
(
fmt
->
fmt_type
==
UAC_FORMAT_TYPE_II
)
u
->
packets
++
;
/* for transfer delimiter */
u
->
urb
=
usb_alloc_urb
(
u
->
packets
,
GFP_KERNEL
);
if
(
!
u
->
urb
)
goto
out_of_memory
;
u
->
urb
->
transfer_buffer
=
usb_alloc_coherent
(
subs
->
dev
,
u
->
buffer_size
,
usb_alloc_coherent
(
ep
->
chip
->
dev
,
u
->
buffer_size
,
GFP_KERNEL
,
&
u
->
urb
->
transfer_dma
);
if
(
!
u
->
urb
->
transfer_buffer
)
goto
out_of_memory
;
u
->
urb
->
pipe
=
subs
->
data
pipe
;
u
->
urb
->
pipe
=
ep
->
pipe
;
u
->
urb
->
transfer_flags
=
URB_ISO_ASAP
|
URB_NO_TRANSFER_DMA_MAP
;
u
->
urb
->
interval
=
1
<<
subs
->
datainterval
;
u
->
urb
->
interval
=
1
<<
ep
->
datainterval
;
u
->
urb
->
context
=
u
;
u
->
urb
->
complete
=
snd_complete_urb
;
INIT_LIST_HEAD
(
&
u
->
ready_list
);
}
if
(
subs
->
syncpipe
)
{
/* allocate and initialize sync urbs */
subs
->
syncbuf
=
usb_alloc_coherent
(
subs
->
dev
,
SYNC_URBS
*
4
,
GFP_KERNEL
,
&
subs
->
sync_dma
);
if
(
!
subs
->
syncbuf
)
goto
out_of_memory
;
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
struct
snd_urb_ctx
*
u
=
&
subs
->
syncurb
[
i
];
u
->
index
=
i
;
u
->
subs
=
subs
;
u
->
packets
=
1
;
u
->
urb
=
usb_alloc_urb
(
1
,
GFP_KERNEL
);
if
(
!
u
->
urb
)
goto
out_of_memory
;
u
->
urb
->
transfer_buffer
=
subs
->
syncbuf
+
i
*
4
;
u
->
urb
->
transfer_dma
=
subs
->
sync_dma
+
i
*
4
;
u
->
urb
->
transfer_buffer_length
=
4
;
u
->
urb
->
pipe
=
subs
->
syncpipe
;
u
->
urb
->
transfer_flags
=
URB_ISO_ASAP
|
URB_NO_TRANSFER_DMA_MAP
;
u
->
urb
->
number_of_packets
=
1
;
u
->
urb
->
interval
=
1
<<
subs
->
syncinterval
;
u
->
urb
->
context
=
u
;
u
->
urb
->
complete
=
snd_complete_sync_urb
;
}
}
return
0
;
out_of_memory:
snd_usb_release_substream_urbs
(
subs
,
0
);
release_urbs
(
ep
,
0
);
return
-
ENOMEM
;
}
/*
* prepare urb for full speed capture sync pipe
*
* fill the length and offset of each urb descriptor.
* the fixed 10.14 frequency is passed through the pipe.
* configure a sync endpoint
*/
static
int
prepare_capture_sync_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
static
int
sync_ep_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_hw_params
*
hw_params
,
struct
audioformat
*
fmt
)
{
unsigned
char
*
cp
=
urb
->
transfer_buffer
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
int
i
;
ep
->
syncbuf
=
usb_alloc_coherent
(
ep
->
chip
->
dev
,
SYNC_URBS
*
4
,
GFP_KERNEL
,
&
ep
->
sync_dma
);
if
(
!
ep
->
syncbuf
)
return
-
ENOMEM
;
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
struct
snd_urb_ctx
*
u
=
&
ep
->
urb
[
i
];
u
->
index
=
i
;
u
->
ep
=
ep
;
u
->
packets
=
1
;
u
->
urb
=
usb_alloc_urb
(
1
,
GFP_KERNEL
);
if
(
!
u
->
urb
)
goto
out_of_memory
;
u
->
urb
->
transfer_buffer
=
ep
->
syncbuf
+
i
*
4
;
u
->
urb
->
transfer_dma
=
ep
->
sync_dma
+
i
*
4
;
u
->
urb
->
transfer_buffer_length
=
4
;
u
->
urb
->
pipe
=
ep
->
pipe
;
u
->
urb
->
transfer_flags
=
URB_ISO_ASAP
|
URB_NO_TRANSFER_DMA_MAP
;
u
->
urb
->
number_of_packets
=
1
;
u
->
urb
->
interval
=
1
<<
ep
->
syncinterval
;
u
->
urb
->
context
=
u
;
u
->
urb
->
complete
=
snd_complete_urb
;
}
ep
->
nurbs
=
SYNC_URBS
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
urb
->
iso_frame_desc
[
0
].
length
=
3
;
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
cp
[
0
]
=
subs
->
freqn
>>
2
;
cp
[
1
]
=
subs
->
freqn
>>
10
;
cp
[
2
]
=
subs
->
freqn
>>
18
;
return
0
;
out_of_memory:
release_urbs
(
ep
,
0
);
return
-
ENOMEM
;
}
/*
* prepare urb for high speed capture sync pipe
/**
* snd_usb_endpoint_set_params: configure an snd_endpoint
*
* @ep: the endpoint to configure
*
* fill the length and offset of each urb descriptor.
* the fixed 12.13 frequency is passed as 16.16 through the pipe.
* Determine the number of of URBs to be used on this endpoint.
* An endpoint must be configured before it can be started.
* An endpoint that is already running can not be reconfigured.
*/
static
int
prepare_capture_sync_urb_hs
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
int
snd_usb_endpoint_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_hw_params
*
hw_params
,
struct
audioformat
*
fmt
,
struct
snd_usb_endpoint
*
sync_ep
)
{
unsigned
char
*
cp
=
urb
->
transfer_buffer
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
int
err
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
urb
->
iso_frame_desc
[
0
].
length
=
4
;
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
cp
[
0
]
=
subs
->
freqn
;
cp
[
1
]
=
subs
->
freqn
>>
8
;
cp
[
2
]
=
subs
->
freqn
>>
16
;
cp
[
3
]
=
subs
->
freqn
>>
24
;
return
0
;
if
(
ep
->
use_count
!=
0
)
{
snd_printk
(
KERN_WARNING
"Unable to change format on ep #%x: already in use
\n
"
,
ep
->
ep_num
);
return
-
EBUSY
;
}
/* release old buffers, if any */
release_urbs
(
ep
,
0
);
ep
->
datainterval
=
fmt
->
datainterval
;
ep
->
maxpacksize
=
fmt
->
maxpacksize
;
ep
->
fill_max
=
!!
(
fmt
->
attributes
&
UAC_EP_CS_ATTR_FILL_MAX
);
if
(
snd_usb_get_speed
(
ep
->
chip
->
dev
)
==
USB_SPEED_FULL
)
ep
->
freqn
=
get_usb_full_speed_rate
(
params_rate
(
hw_params
));
else
ep
->
freqn
=
get_usb_high_speed_rate
(
params_rate
(
hw_params
));
/* calculate the frequency in 16.16 format */
ep
->
freqm
=
ep
->
freqn
;
ep
->
freqshift
=
INT_MIN
;
ep
->
phase
=
0
;
switch
(
ep
->
type
)
{
case
SND_USB_ENDPOINT_TYPE_DATA
:
err
=
data_ep_set_params
(
ep
,
hw_params
,
fmt
,
sync_ep
);
break
;
case
SND_USB_ENDPOINT_TYPE_SYNC
:
err
=
sync_ep_set_params
(
ep
,
hw_params
,
fmt
);
break
;
default:
err
=
-
EINVAL
;
}
snd_printdd
(
KERN_DEBUG
"Setting params for ep #%x (type %d, %d urbs), ret=%d
\n
"
,
ep
->
ep_num
,
ep
->
type
,
ep
->
nurbs
,
err
);
return
err
;
}
/*
* process after capture sync complete
* - nothing to do
/**
* snd_usb_endpoint_start: start an snd_usb_endpoint
*
* @ep: the endpoint to start
*
* A call to this function will increment the use count of the endpoint.
* In case this not already running, the URBs for this endpoint will be
* submitted. Otherwise, this function does nothing.
*
* Must be balanced to calls of snd_usb_endpoint_stop().
*
* Returns an error if the URB submission failed, 0 in all other cases.
*/
static
int
retire_capture_sync_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
int
snd_usb_endpoint_start
(
struct
snd_usb_endpoint
*
ep
)
{
int
err
;
unsigned
int
i
;
if
(
ep
->
chip
->
shutdown
)
return
-
EBADFD
;
/* already running? */
if
(
++
ep
->
use_count
!=
1
)
return
0
;
if
(
snd_BUG_ON
(
!
test_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
)))
return
-
EINVAL
;
/* just to be sure */
deactivate_urbs
(
ep
,
0
,
1
);
wait_clear_urbs
(
ep
);
ep
->
active_mask
=
0
;
ep
->
unlink_mask
=
0
;
ep
->
phase
=
0
;
/*
* If this endpoint has a data endpoint as implicit feedback source,
* don't start the urbs here. Instead, mark them all as available,
* wait for the record urbs to arrive and queue from that context.
*/
set_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
);
if
(
snd_usb_endpoint_implict_feedback_sink
(
ep
))
{
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
struct
snd_urb_ctx
*
ctx
=
ep
->
urb
+
i
;
list_add_tail
(
&
ctx
->
ready_list
,
&
ep
->
ready_playback_urbs
);
}
return
0
;
}
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
struct
urb
*
urb
=
ep
->
urb
[
i
].
urb
;
if
(
snd_BUG_ON
(
!
urb
))
goto
__error
;
if
(
usb_pipeout
(
ep
->
pipe
))
{
prepare_outbound_urb_sizes
(
ep
,
urb
->
context
);
prepare_outbound_urb
(
ep
,
urb
->
context
);
}
else
{
prepare_inbound_urb
(
ep
,
urb
->
context
);
}
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
);
if
(
err
<
0
)
{
snd_printk
(
KERN_ERR
"cannot submit urb %d, error %d: %s
\n
"
,
i
,
err
,
usb_error_string
(
err
));
goto
__error
;
}
set_bit
(
i
,
&
ep
->
active_mask
);
}
return
0
;
__error:
clear_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
);
ep
->
use_count
--
;
deactivate_urbs
(
ep
,
0
,
0
);
return
-
EPIPE
;
}
/*
* prepare urb for capture data pipe
/**
* snd_usb_endpoint_stop: stop an snd_usb_endpoint
*
* @ep: the endpoint to stop (may be NULL)
*
* fill the offset and length of each descriptor.
* A call to this function will decrement the use count of the endpoint.
* In case the last user has requested the endpoint stop, the URBs will
* actually deactivated.
*
* we use a temporary buffer to write the captured data.
* since the length of written data is determined by host, we cannot
* write onto the pcm buffer directly... the data is thus copied
* later at complete callback to the global buffer.
* Must be balanced to calls of snd_usb_endpoint_start().
*/
static
int
prepare_capture_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
void
snd_usb_endpoint_stop
(
struct
snd_usb_endpoint
*
ep
,
int
force
,
int
can_sleep
,
int
wait
)
{
i
nt
i
,
offs
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
i
f
(
!
ep
)
return
;
offs
=
0
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
for
(
i
=
0
;
i
<
ctx
->
packets
;
i
++
)
{
urb
->
iso_frame_desc
[
i
].
offset
=
offs
;
urb
->
iso_frame_desc
[
i
].
length
=
subs
->
curpacksize
;
offs
+=
subs
->
curpacksize
;
if
(
snd_BUG_ON
(
ep
->
use_count
==
0
))
return
;
if
(
snd_BUG_ON
(
!
test_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
)))
return
;
if
(
--
ep
->
use_count
==
0
)
{
deactivate_urbs
(
ep
,
force
,
can_sleep
);
ep
->
data_subs
=
NULL
;
ep
->
sync_slave
=
NULL
;
ep
->
retire_data_urb
=
NULL
;
ep
->
prepare_data_urb
=
NULL
;
if
(
wait
)
wait_clear_urbs
(
ep
);
}
urb
->
transfer_buffer_length
=
offs
;
urb
->
number_of_packets
=
ctx
->
packets
;
return
0
;
}
/*
* process after capture complete
/**
* snd_usb_endpoint_activate: activate an snd_usb_endpoint
*
* @ep: the endpoint to activate
*
* If the endpoint is not currently in use, this functions will select the
* correct alternate interface setting for the interface of this endpoint.
*
* copy the data from each desctiptor to the pcm buffer, and
* update the current position.
* In case of any active users, this functions does nothing.
*
* Returns an error if usb_set_interface() failed, 0 in all other
* cases.
*/
static
int
retire_capture_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
int
snd_usb_endpoint_activate
(
struct
snd_usb_endpoint
*
ep
)
{
unsigned
long
flags
;
unsigned
char
*
cp
;
int
i
;
unsigned
int
stride
,
frames
,
bytes
,
oldptr
;
int
period_elapsed
=
0
;
if
(
ep
->
use_count
!=
0
)
return
0
;
stride
=
runtime
->
frame_bits
>>
3
;
if
(
!
ep
->
chip
->
shutdown
&&
!
test_and_set_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
))
{
int
ret
;
for
(
i
=
0
;
i
<
urb
->
number_of_packets
;
i
++
)
{
cp
=
(
unsigned
char
*
)
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
i
].
offset
;
if
(
urb
->
iso_frame_desc
[
i
].
status
&&
printk_ratelimit
())
{
snd_printdd
(
"frame %d active: %d
\n
"
,
i
,
urb
->
iso_frame_desc
[
i
].
status
);
// continue;
}
bytes
=
urb
->
iso_frame_desc
[
i
].
actual_length
;
frames
=
bytes
/
stride
;
if
(
!
subs
->
txfr_quirk
)
bytes
=
frames
*
stride
;
if
(
bytes
%
(
runtime
->
sample_bits
>>
3
)
!=
0
)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int
oldbytes
=
bytes
;
#endif
bytes
=
frames
*
stride
;
snd_printdd
(
KERN_ERR
"Corrected urb data len. %d->%d
\n
"
,
oldbytes
,
bytes
);
}
/* update the current pointer */
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
oldptr
=
subs
->
hwptr_done
;
subs
->
hwptr_done
+=
bytes
;
if
(
subs
->
hwptr_done
>=
runtime
->
buffer_size
*
stride
)
subs
->
hwptr_done
-=
runtime
->
buffer_size
*
stride
;
frames
=
(
bytes
+
(
oldptr
%
stride
))
/
stride
;
subs
->
transfer_done
+=
frames
;
if
(
subs
->
transfer_done
>=
runtime
->
period_size
)
{
subs
->
transfer_done
-=
runtime
->
period_size
;
period_elapsed
=
1
;
}
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
/* copy a data chunk */
if
(
oldptr
+
bytes
>
runtime
->
buffer_size
*
stride
)
{
unsigned
int
bytes1
=
runtime
->
buffer_size
*
stride
-
oldptr
;
memcpy
(
runtime
->
dma_area
+
oldptr
,
cp
,
bytes1
);
memcpy
(
runtime
->
dma_area
,
cp
+
bytes1
,
bytes
-
bytes1
);
}
else
{
memcpy
(
runtime
->
dma_area
+
oldptr
,
cp
,
bytes
);
ret
=
usb_set_interface
(
ep
->
chip
->
dev
,
ep
->
iface
,
ep
->
alt_idx
);
if
(
ret
<
0
)
{
snd_printk
(
KERN_ERR
"%s() usb_set_interface() failed, ret = %d
\n
"
,
__func__
,
ret
);
clear_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
);
return
ret
;
}
return
0
;
}
if
(
period_elapsed
)
snd_pcm_period_elapsed
(
subs
->
pcm_substream
);
return
0
;
return
-
EBUSY
;
}
/*
* Process after capture complete when paused. Nothing to do.
/**
* snd_usb_endpoint_deactivate: deactivate an snd_usb_endpoint
*
* @ep: the endpoint to deactivate
*
* If the endpoint is not currently in use, this functions will select the
* alternate interface setting 0 for the interface of this endpoint.
*
* In case of any active users, this functions does nothing.
*
* Returns an error if usb_set_interface() failed, 0 in all other
* cases.
*/
static
int
retire_paused_capture_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
int
snd_usb_endpoint_deactivate
(
struct
snd_usb_endpoint
*
ep
)
{
return
0
;
}
if
(
!
ep
)
return
-
EINVAL
;
if
(
ep
->
use_count
!=
0
)
return
0
;
/*
* prepare urb for playback sync pipe
if
(
!
ep
->
chip
->
shutdown
&&
test_and_clear_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
))
{
int
ret
;
ret
=
usb_set_interface
(
ep
->
chip
->
dev
,
ep
->
iface
,
0
);
if
(
ret
<
0
)
{
snd_printk
(
KERN_ERR
"%s(): usb_set_interface() failed, ret = %d
\n
"
,
__func__
,
ret
);
return
ret
;
}
return
0
;
}
return
-
EBUSY
;
}
/** snd_usb_endpoint_free: Free the resources of an snd_usb_endpoint
*
* @ep: the list header of the endpoint to free
*
* set up the offset and length to receive the current frequency.
* This function does not care for the endpoint's use count but will tear
* down all the streaming URBs immediately and free all resources.
*/
static
int
prepare_playback_sync_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
void
snd_usb_endpoint_free
(
struct
list_head
*
head
)
{
struct
snd_u
rb_ctx
*
ctx
=
urb
->
context
;
struct
snd_u
sb_endpoint
*
ep
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
urb
->
iso_frame_desc
[
0
].
length
=
min
(
4u
,
ctx
->
subs
->
syncmaxsize
);
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
return
0
;
ep
=
list_entry
(
head
,
struct
snd_usb_endpoint
,
list
);
release_urbs
(
ep
,
1
);
kfree
(
ep
);
}
/*
*
process after playback sync complete
*
*
Full speed devices report feedback values in 10.14 format as samples per
*
frame, high speed devices in 16.16 format as samples per microframe.
*
Because the Audio Class 1 spec was written before USB 2.0, many high speed
*
devices use a wrong interpretation, some others use an entirely different
*
format. Therefore, we cannot predict what format any particular device uses
*
and must detect it automatically
.
/*
*
*
snd_usb_handle_sync_urb: parse an USB sync packet
*
*
@ep: the endpoint to handle the packet
*
@sender: the sending endpoint
*
@urb: the received packet
*
*
This function is called from the context of an endpoint that received
*
the packet and is used to let another endpoint object handle the payload
.
*/
static
int
retire_playback_sync_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
void
snd_usb_handle_sync_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_usb_endpoint
*
sender
,
const
struct
urb
*
urb
)
{
unsigned
int
f
;
int
shift
;
unsigned
int
f
;
unsigned
long
flags
;
snd_BUG_ON
(
ep
==
sender
);
/*
* In case the endpoint is operating in implicit feedback mode, prepare
* and a new outbound URB that has the same layout as the received
* packet and add it to the list of pending urbs.
*/
if
(
snd_usb_endpoint_implict_feedback_sink
(
ep
)
&&
ep
->
use_count
!=
0
)
{
/* implicit feedback case */
int
i
,
bytes
=
0
;
struct
snd_urb_ctx
*
in_ctx
;
struct
snd_usb_packet_info
*
out_packet
;
in_ctx
=
urb
->
context
;
/* Count overall packet size */
for
(
i
=
0
;
i
<
in_ctx
->
packets
;
i
++
)
if
(
urb
->
iso_frame_desc
[
i
].
status
==
0
)
bytes
+=
urb
->
iso_frame_desc
[
i
].
actual_length
;
/*
* skip empty packets. At least M-Audio's Fast Track Ultra stops
* streaming once it received a 0-byte OUT URB
*/
if
(
bytes
==
0
)
return
;
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
out_packet
=
ep
->
next_packet
+
ep
->
next_packet_write_pos
;
/*
* Iterate through the inbound packet and prepare the lengths
* for the output packet. The OUT packet we are about to send
* will have the same amount of payload than the IN packet we
* just received.
*/
out_packet
->
packets
=
in_ctx
->
packets
;
for
(
i
=
0
;
i
<
in_ctx
->
packets
;
i
++
)
{
if
(
urb
->
iso_frame_desc
[
i
].
status
==
0
)
out_packet
->
packet_size
[
i
]
=
urb
->
iso_frame_desc
[
i
].
actual_length
/
ep
->
stride
;
else
out_packet
->
packet_size
[
i
]
=
0
;
}
ep
->
next_packet_write_pos
++
;
ep
->
next_packet_write_pos
%=
MAX_URBS
;
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
queue_pending_output_urbs
(
ep
);
return
;
}
/*
* process after playback sync complete
*
* Full speed devices report feedback values in 10.14 format as samples
* per frame, high speed devices in 16.16 format as samples per
* microframe.
*
* Because the Audio Class 1 spec was written before USB 2.0, many high
* speed devices use a wrong interpretation, some others use an
* entirely different format.
*
* Therefore, we cannot predict what format any particular device uses
* and must detect it automatically.
*/
if
(
urb
->
iso_frame_desc
[
0
].
status
!=
0
||
urb
->
iso_frame_desc
[
0
].
actual_length
<
3
)
return
0
;
return
;
f
=
le32_to_cpup
(
urb
->
transfer_buffer
);
if
(
urb
->
iso_frame_desc
[
0
].
actual_length
==
3
)
f
&=
0x00ffffff
;
else
f
&=
0x0fffffff
;
if
(
f
==
0
)
return
0
;
return
;
if
(
unlikely
(
subs
->
freqshift
==
INT_MIN
))
{
if
(
unlikely
(
ep
->
freqshift
==
INT_MIN
))
{
/*
* The first time we see a feedback value, determine its format
* by shifting it left or right until it matches the nominal
...
...
@@ -569,398 +1120,34 @@ static int retire_playback_sync_urb(struct snd_usb_substream *subs,
* differ from the nominal value more than +50% or -25%.
*/
shift
=
0
;
while
(
f
<
subs
->
freqn
-
subs
->
freqn
/
4
)
{
while
(
f
<
ep
->
freqn
-
ep
->
freqn
/
4
)
{
f
<<=
1
;
shift
++
;
}
while
(
f
>
subs
->
freqn
+
subs
->
freqn
/
2
)
{
while
(
f
>
ep
->
freqn
+
ep
->
freqn
/
2
)
{
f
>>=
1
;
shift
--
;
}
subs
->
freqshift
=
shift
;
}
else
if
(
subs
->
freqshift
>=
0
)
f
<<=
subs
->
freqshift
;
ep
->
freqshift
=
shift
;
}
else
if
(
ep
->
freqshift
>=
0
)
f
<<=
ep
->
freqshift
;
else
f
>>=
-
subs
->
freqshift
;
f
>>=
-
ep
->
freqshift
;
if
(
likely
(
f
>=
subs
->
freqn
-
subs
->
freqn
/
8
&&
f
<=
subs
->
freqmax
))
{
if
(
likely
(
f
>=
ep
->
freqn
-
ep
->
freqn
/
8
&&
f
<=
ep
->
freqmax
))
{
/*
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
*/
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
subs
->
freqm
=
f
;
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
ep
->
freqm
=
f
;
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
}
else
{
/*
* Out of range; maybe the shift value is wrong.
* Reset it so that we autodetect again the next time.
*/
subs
->
freqshift
=
INT_MIN
;
}
return
0
;
}
/* determine the number of frames in the next packet */
static
int
snd_usb_audio_next_packet_size
(
struct
snd_usb_substream
*
subs
)
{
if
(
subs
->
fill_max
)
return
subs
->
maxframesize
;
else
{
subs
->
phase
=
(
subs
->
phase
&
0xffff
)
+
(
subs
->
freqm
<<
subs
->
datainterval
);
return
min
(
subs
->
phase
>>
16
,
subs
->
maxframesize
);
ep
->
freqshift
=
INT_MIN
;
}
}
/*
* Prepare urb for streaming before playback starts or when paused.
*
* We don't have any data, so we send silence.
*/
static
int
prepare_nodata_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
unsigned
int
i
,
offs
,
counts
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
int
stride
=
runtime
->
frame_bits
>>
3
;
offs
=
0
;
urb
->
dev
=
ctx
->
subs
->
dev
;
for
(
i
=
0
;
i
<
ctx
->
packets
;
++
i
)
{
counts
=
snd_usb_audio_next_packet_size
(
subs
);
urb
->
iso_frame_desc
[
i
].
offset
=
offs
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
offs
+=
counts
;
}
urb
->
number_of_packets
=
ctx
->
packets
;
urb
->
transfer_buffer_length
=
offs
*
stride
;
memset
(
urb
->
transfer_buffer
,
runtime
->
format
==
SNDRV_PCM_FORMAT_U8
?
0x80
:
0
,
offs
*
stride
);
return
0
;
}
/*
* prepare urb for playback data pipe
*
* Since a URB can handle only a single linear buffer, we must use double
* buffering when the data to be transferred overflows the buffer boundary.
* To avoid inconsistencies when updating hwptr_done, we use double buffering
* for all URBs.
*/
static
int
prepare_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
int
i
,
stride
;
unsigned
int
counts
,
frames
,
bytes
;
unsigned
long
flags
;
int
period_elapsed
=
0
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
stride
=
runtime
->
frame_bits
>>
3
;
frames
=
0
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
urb
->
number_of_packets
=
0
;
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
for
(
i
=
0
;
i
<
ctx
->
packets
;
i
++
)
{
counts
=
snd_usb_audio_next_packet_size
(
subs
);
/* set up descriptor */
urb
->
iso_frame_desc
[
i
].
offset
=
frames
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
frames
+=
counts
;
urb
->
number_of_packets
++
;
subs
->
transfer_done
+=
counts
;
if
(
subs
->
transfer_done
>=
runtime
->
period_size
)
{
subs
->
transfer_done
-=
runtime
->
period_size
;
period_elapsed
=
1
;
if
(
subs
->
fmt_type
==
UAC_FORMAT_TYPE_II
)
{
if
(
subs
->
transfer_done
>
0
)
{
/* FIXME: fill-max mode is not
* supported yet */
frames
-=
subs
->
transfer_done
;
counts
-=
subs
->
transfer_done
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
subs
->
transfer_done
=
0
;
}
i
++
;
if
(
i
<
ctx
->
packets
)
{
/* add a transfer delimiter */
urb
->
iso_frame_desc
[
i
].
offset
=
frames
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
0
;
urb
->
number_of_packets
++
;
}
break
;
}
}
if
(
period_elapsed
)
/* finish at the period boundary */
break
;
}
bytes
=
frames
*
stride
;
if
(
subs
->
hwptr_done
+
bytes
>
runtime
->
buffer_size
*
stride
)
{
/* err, the transferred area goes over buffer boundary. */
unsigned
int
bytes1
=
runtime
->
buffer_size
*
stride
-
subs
->
hwptr_done
;
memcpy
(
urb
->
transfer_buffer
,
runtime
->
dma_area
+
subs
->
hwptr_done
,
bytes1
);
memcpy
(
urb
->
transfer_buffer
+
bytes1
,
runtime
->
dma_area
,
bytes
-
bytes1
);
}
else
{
memcpy
(
urb
->
transfer_buffer
,
runtime
->
dma_area
+
subs
->
hwptr_done
,
bytes
);
}
subs
->
hwptr_done
+=
bytes
;
if
(
subs
->
hwptr_done
>=
runtime
->
buffer_size
*
stride
)
subs
->
hwptr_done
-=
runtime
->
buffer_size
*
stride
;
/* update delay with exact number of samples queued */
runtime
->
delay
=
subs
->
last_delay
;
runtime
->
delay
+=
frames
;
subs
->
last_delay
=
runtime
->
delay
;
/* realign last_frame_number */
subs
->
last_frame_number
=
usb_get_current_frame_number
(
subs
->
dev
);
subs
->
last_frame_number
&=
0xFF
;
/* keep 8 LSBs */
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
urb
->
transfer_buffer_length
=
bytes
;
if
(
period_elapsed
)
snd_pcm_period_elapsed
(
subs
->
pcm_substream
);
return
0
;
}
/*
* process after playback data complete
* - decrease the delay count again
*/
static
int
retire_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
unsigned
long
flags
;
int
stride
=
runtime
->
frame_bits
>>
3
;
int
processed
=
urb
->
transfer_buffer_length
/
stride
;
int
est_delay
;
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
est_delay
=
snd_usb_pcm_delay
(
subs
,
runtime
->
rate
);
/* update delay with exact number of samples played */
if
(
processed
>
subs
->
last_delay
)
subs
->
last_delay
=
0
;
else
subs
->
last_delay
-=
processed
;
runtime
->
delay
=
subs
->
last_delay
;
/*
* Report when delay estimate is off by more than 2ms.
* The error should be lower than 2ms since the estimate relies
* on two reads of a counter updated every ms.
*/
if
(
abs
(
est_delay
-
subs
->
last_delay
)
*
1000
>
runtime
->
rate
*
2
)
snd_printk
(
KERN_DEBUG
"delay: estimated %d, actual %d
\n
"
,
est_delay
,
subs
->
last_delay
);
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
return
0
;
}
static
const
char
*
usb_error_string
(
int
err
)
{
switch
(
err
)
{
case
-
ENODEV
:
return
"no device"
;
case
-
ENOENT
:
return
"endpoint not enabled"
;
case
-
EPIPE
:
return
"endpoint stalled"
;
case
-
ENOSPC
:
return
"not enough bandwidth"
;
case
-
ESHUTDOWN
:
return
"device disabled"
;
case
-
EHOSTUNREACH
:
return
"device suspended"
;
case
-
EINVAL
:
case
-
EAGAIN
:
case
-
EFBIG
:
case
-
EMSGSIZE
:
return
"internal error"
;
default:
return
"unknown error"
;
}
}
/*
* set up and start data/sync urbs
*/
static
int
start_urbs
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
)
{
unsigned
int
i
;
int
err
;
if
(
subs
->
stream
->
chip
->
shutdown
)
return
-
EBADFD
;
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
if
(
snd_BUG_ON
(
!
subs
->
dataurb
[
i
].
urb
))
return
-
EINVAL
;
if
(
subs
->
ops
.
prepare
(
subs
,
runtime
,
subs
->
dataurb
[
i
].
urb
)
<
0
)
{
snd_printk
(
KERN_ERR
"cannot prepare datapipe for urb %d
\n
"
,
i
);
goto
__error
;
}
}
if
(
subs
->
syncpipe
)
{
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
if
(
snd_BUG_ON
(
!
subs
->
syncurb
[
i
].
urb
))
return
-
EINVAL
;
if
(
subs
->
ops
.
prepare_sync
(
subs
,
runtime
,
subs
->
syncurb
[
i
].
urb
)
<
0
)
{
snd_printk
(
KERN_ERR
"cannot prepare syncpipe for urb %d
\n
"
,
i
);
goto
__error
;
}
}
}
subs
->
active_mask
=
0
;
subs
->
unlink_mask
=
0
;
subs
->
running
=
1
;
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
err
=
usb_submit_urb
(
subs
->
dataurb
[
i
].
urb
,
GFP_ATOMIC
);
if
(
err
<
0
)
{
snd_printk
(
KERN_ERR
"cannot submit datapipe "
"for urb %d, error %d: %s
\n
"
,
i
,
err
,
usb_error_string
(
err
));
goto
__error
;
}
set_bit
(
i
,
&
subs
->
active_mask
);
}
if
(
subs
->
syncpipe
)
{
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
err
=
usb_submit_urb
(
subs
->
syncurb
[
i
].
urb
,
GFP_ATOMIC
);
if
(
err
<
0
)
{
snd_printk
(
KERN_ERR
"cannot submit syncpipe "
"for urb %d, error %d: %s
\n
"
,
i
,
err
,
usb_error_string
(
err
));
goto
__error
;
}
set_bit
(
i
+
16
,
&
subs
->
active_mask
);
}
}
return
0
;
__error:
// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
deactivate_urbs
(
subs
,
0
,
0
);
return
-
EPIPE
;
}
/*
*/
static
struct
snd_urb_ops
audio_urb_ops
[
2
]
=
{
{
.
prepare
=
prepare_nodata_playback_urb
,
.
retire
=
retire_playback_urb
,
.
prepare_sync
=
prepare_playback_sync_urb
,
.
retire_sync
=
retire_playback_sync_urb
,
},
{
.
prepare
=
prepare_capture_urb
,
.
retire
=
retire_capture_urb
,
.
prepare_sync
=
prepare_capture_sync_urb
,
.
retire_sync
=
retire_capture_sync_urb
,
},
};
/*
* initialize the substream instance.
*/
void
snd_usb_init_substream
(
struct
snd_usb_stream
*
as
,
int
stream
,
struct
audioformat
*
fp
)
{
struct
snd_usb_substream
*
subs
=
&
as
->
substream
[
stream
];
INIT_LIST_HEAD
(
&
subs
->
fmt_list
);
spin_lock_init
(
&
subs
->
lock
);
subs
->
stream
=
as
;
subs
->
direction
=
stream
;
subs
->
dev
=
as
->
chip
->
dev
;
subs
->
txfr_quirk
=
as
->
chip
->
txfr_quirk
;
subs
->
ops
=
audio_urb_ops
[
stream
];
if
(
snd_usb_get_speed
(
subs
->
dev
)
>=
USB_SPEED_HIGH
)
subs
->
ops
.
prepare_sync
=
prepare_capture_sync_urb_hs
;
snd_usb_set_pcm_ops
(
as
->
pcm
,
stream
);
list_add_tail
(
&
fp
->
list
,
&
subs
->
fmt_list
);
subs
->
formats
|=
fp
->
formats
;
subs
->
endpoint
=
fp
->
endpoint
;
subs
->
num_formats
++
;
subs
->
fmt_type
=
fp
->
fmt_type
;
}
int
snd_usb_substream_playback_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
snd_usb_substream
*
subs
=
substream
->
runtime
->
private_data
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
subs
->
ops
.
prepare
=
prepare_playback_urb
;
return
0
;
case
SNDRV_PCM_TRIGGER_STOP
:
return
deactivate_urbs
(
subs
,
0
,
0
);
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
subs
->
ops
.
prepare
=
prepare_nodata_playback_urb
;
return
0
;
}
return
-
EINVAL
;
}
int
snd_usb_substream_capture_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
snd_usb_substream
*
subs
=
substream
->
runtime
->
private_data
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
subs
->
ops
.
retire
=
retire_capture_urb
;
return
start_urbs
(
subs
,
substream
->
runtime
);
case
SNDRV_PCM_TRIGGER_STOP
:
return
deactivate_urbs
(
subs
,
0
,
0
);
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
subs
->
ops
.
retire
=
retire_paused_capture_urb
;
return
0
;
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
subs
->
ops
.
retire
=
retire_capture_urb
;
return
0
;
}
return
-
EINVAL
;
}
int
snd_usb_substream_prepare
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
)
{
/* clear urbs (to be sure) */
deactivate_urbs
(
subs
,
0
,
1
);
wait_clear_urbs
(
subs
);
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if
(
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
)
{
subs
->
ops
.
prepare
=
prepare_nodata_playback_urb
;
return
start_urbs
(
subs
,
runtime
);
}
return
0
;
}
sound/usb/endpoint.h
View file @
56599bb0
#ifndef __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
void
snd_usb_init_substream
(
struct
snd_usb_stream
*
as
,
int
stream
,
struct
audioformat
*
fp
);
#define SND_USB_ENDPOINT_TYPE_DATA 0
#define SND_USB_ENDPOINT_TYPE_SYNC 1
int
snd_usb_init_substream_urbs
(
struct
snd_usb_substream
*
subs
,
unsigned
int
period_bytes
,
unsigned
int
rate
,
unsigned
int
frame_bits
);
struct
snd_usb_endpoint
*
snd_usb_add_endpoint
(
struct
snd_usb_audio
*
chip
,
struct
usb_host_interface
*
alts
,
int
ep_num
,
int
direction
,
int
type
);
void
snd_usb_release_substream_urbs
(
struct
snd_usb_substream
*
subs
,
int
force
);
int
snd_usb_endpoint_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_hw_params
*
hw_params
,
struct
audioformat
*
fmt
,
struct
snd_usb_endpoint
*
sync_ep
);
int
snd_usb_substream_prepare
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
);
int
snd_usb_endpoint_start
(
struct
snd_usb_endpoint
*
ep
);
void
snd_usb_endpoint_stop
(
struct
snd_usb_endpoint
*
ep
,
int
force
,
int
can_sleep
,
int
wait
);
int
snd_usb_endpoint_activate
(
struct
snd_usb_endpoint
*
ep
);
int
snd_usb_endpoint_deactivate
(
struct
snd_usb_endpoint
*
ep
);
void
snd_usb_endpoint_free
(
struct
list_head
*
head
);
int
snd_usb_substream_playback_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
);
int
snd_usb_substream_capture_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
);
int
snd_usb_endpoint_implict_feedback_sink
(
struct
snd_usb_endpoint
*
ep
);
void
snd_usb_handle_sync_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_usb_endpoint
*
sender
,
const
struct
urb
*
urb
);
#endif
/* __USBAUDIO_ENDPOINT_H */
sound/usb/pcm.c
View file @
56599bb0
...
...
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
...
...
@@ -34,6 +35,9 @@
#include "clock.h"
#include "power.h"
#define SUBSTREAM_FLAG_DATA_EP_STARTED 0
#define SUBSTREAM_FLAG_SYNC_EP_STARTED 1
/* return the estimated delay based on USB frame counters */
snd_pcm_uframes_t
snd_usb_pcm_delay
(
struct
snd_usb_substream
*
subs
,
unsigned
int
rate
)
...
...
@@ -208,6 +212,84 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
}
}
static
int
start_endpoints
(
struct
snd_usb_substream
*
subs
)
{
int
err
;
if
(
!
subs
->
data_endpoint
)
return
-
EINVAL
;
if
(
!
test_and_set_bit
(
SUBSTREAM_FLAG_DATA_EP_STARTED
,
&
subs
->
flags
))
{
struct
snd_usb_endpoint
*
ep
=
subs
->
data_endpoint
;
snd_printdd
(
KERN_DEBUG
"Starting data EP @%p
\n
"
,
ep
);
ep
->
data_subs
=
subs
;
err
=
snd_usb_endpoint_start
(
ep
);
if
(
err
<
0
)
{
clear_bit
(
SUBSTREAM_FLAG_DATA_EP_STARTED
,
&
subs
->
flags
);
return
err
;
}
}
if
(
subs
->
sync_endpoint
&&
!
test_and_set_bit
(
SUBSTREAM_FLAG_SYNC_EP_STARTED
,
&
subs
->
flags
))
{
struct
snd_usb_endpoint
*
ep
=
subs
->
sync_endpoint
;
snd_printdd
(
KERN_DEBUG
"Starting sync EP @%p
\n
"
,
ep
);
ep
->
sync_slave
=
subs
->
data_endpoint
;
err
=
snd_usb_endpoint_start
(
ep
);
if
(
err
<
0
)
{
clear_bit
(
SUBSTREAM_FLAG_SYNC_EP_STARTED
,
&
subs
->
flags
);
return
err
;
}
}
return
0
;
}
static
void
stop_endpoints
(
struct
snd_usb_substream
*
subs
,
int
force
,
int
can_sleep
,
int
wait
)
{
if
(
test_and_clear_bit
(
SUBSTREAM_FLAG_SYNC_EP_STARTED
,
&
subs
->
flags
))
snd_usb_endpoint_stop
(
subs
->
sync_endpoint
,
force
,
can_sleep
,
wait
);
if
(
test_and_clear_bit
(
SUBSTREAM_FLAG_DATA_EP_STARTED
,
&
subs
->
flags
))
snd_usb_endpoint_stop
(
subs
->
data_endpoint
,
force
,
can_sleep
,
wait
);
}
static
int
activate_endpoints
(
struct
snd_usb_substream
*
subs
)
{
if
(
subs
->
sync_endpoint
)
{
int
ret
;
ret
=
snd_usb_endpoint_activate
(
subs
->
sync_endpoint
);
if
(
ret
<
0
)
return
ret
;
}
return
snd_usb_endpoint_activate
(
subs
->
data_endpoint
);
}
static
int
deactivate_endpoints
(
struct
snd_usb_substream
*
subs
)
{
int
reta
,
retb
;
reta
=
snd_usb_endpoint_deactivate
(
subs
->
sync_endpoint
);
retb
=
snd_usb_endpoint_deactivate
(
subs
->
data_endpoint
);
if
(
reta
<
0
)
return
reta
;
if
(
retb
<
0
)
return
retb
;
return
0
;
}
/*
* find a matching format and set up the interface
*/
...
...
@@ -219,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct
usb_interface
*
iface
;
unsigned
int
ep
,
attr
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
int
err
;
int
err
,
implicit_fb
=
0
;
iface
=
usb_ifnum_to_if
(
dev
,
fmt
->
iface
);
if
(
WARN_ON
(
!
iface
))
...
...
@@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
if
(
fmt
==
subs
->
cur_audiofmt
)
return
0
;
/* close the old interface */
if
(
subs
->
interface
>=
0
&&
subs
->
interface
!=
fmt
->
iface
)
{
if
(
usb_set_interface
(
subs
->
dev
,
subs
->
interface
,
0
)
<
0
)
{
snd_printk
(
KERN_ERR
"%d:%d:%d: return to setting 0 failed
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EIO
;
}
subs
->
interface
=
-
1
;
subs
->
altset_idx
=
0
;
}
/* set interface */
if
(
subs
->
interface
!=
fmt
->
iface
||
subs
->
altset_idx
!=
fmt
->
altset_idx
)
{
if
(
usb_set_interface
(
dev
,
fmt
->
iface
,
fmt
->
altsetting
)
<
0
)
{
snd_printk
(
KERN_ERR
"%d:%d:%d: usb_set_interface failed
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EIO
;
}
snd_printdd
(
KERN_INFO
"setting usb interface %d:%d
\n
"
,
fmt
->
iface
,
fmt
->
altsetting
);
subs
->
interface
=
fmt
->
iface
;
subs
->
altset_idx
=
fmt
->
altset_idx
;
}
/* create a data pipe */
ep
=
fmt
->
endpoint
&
USB_ENDPOINT_NUMBER_MASK
;
if
(
is_playback
)
subs
->
datapipe
=
usb_sndisocpipe
(
dev
,
ep
);
else
subs
->
datapipe
=
usb_rcvisocpipe
(
dev
,
ep
);
subs
->
datainterval
=
fmt
->
datainterval
;
subs
->
syncpipe
=
subs
->
syncinterval
=
0
;
subs
->
maxpacksize
=
fmt
->
maxpacksize
;
subs
->
syncmaxsize
=
0
;
subs
->
fill_max
=
0
;
subs
->
data_endpoint
=
snd_usb_add_endpoint
(
subs
->
stream
->
chip
,
alts
,
fmt
->
endpoint
,
subs
->
direction
,
SND_USB_ENDPOINT_TYPE_DATA
);
if
(
!
subs
->
data_endpoint
)
return
-
EINVAL
;
/* we need a sync pipe in async OUT or adaptive IN mode */
/* check the number of EP, since some devices have broken
...
...
@@ -273,8 +326,25 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
* assume it as adaptive-out or sync-in.
*/
attr
=
fmt
->
ep_attr
&
USB_ENDPOINT_SYNCTYPE
;
switch
(
subs
->
stream
->
chip
->
usb_id
)
{
case
USB_ID
(
0x0763
,
0x2080
):
/* M-Audio FastTrack Ultra */
case
USB_ID
(
0x0763
,
0x2081
):
if
(
is_playback
)
{
implicit_fb
=
1
;
ep
=
0x81
;
iface
=
usb_ifnum_to_if
(
dev
,
2
);
if
(
!
iface
||
iface
->
num_altsetting
==
0
)
return
-
EINVAL
;
alts
=
&
iface
->
altsetting
[
1
];
goto
add_sync_ep
;
}
}
if
(((
is_playback
&&
attr
==
USB_ENDPOINT_SYNC_ASYNC
)
||
(
!
is_playback
&&
attr
==
USB_ENDPOINT_SYNC_ADAPTIVE
))
&&
(
!
is_playback
&&
attr
==
USB_ENDPOINT_SYNC_ADAPTIVE
))
&&
altsd
->
bNumEndpoints
>=
2
)
{
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
...
...
@@ -282,7 +352,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
the audio fields in the endpoint descriptors */
if
((
get_endpoint
(
alts
,
1
)
->
bmAttributes
&
USB_ENDPOINT_XFERTYPE_MASK
)
!=
0x01
||
(
get_endpoint
(
alts
,
1
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
get_endpoint
(
alts
,
1
)
->
bSynchAddress
!=
0
))
{
get_endpoint
(
alts
,
1
)
->
bSynchAddress
!=
0
&&
!
implicit_fb
))
{
snd_printk
(
KERN_ERR
"%d:%d:%d : invalid synch pipe
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EINVAL
;
...
...
@@ -290,33 +361,27 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
ep
=
get_endpoint
(
alts
,
1
)
->
bEndpointAddress
;
if
(
get_endpoint
(
alts
,
0
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
((
is_playback
&&
ep
!=
(
unsigned
int
)(
get_endpoint
(
alts
,
0
)
->
bSynchAddress
|
USB_DIR_IN
))
||
(
!
is_playback
&&
ep
!=
(
unsigned
int
)(
get_endpoint
(
alts
,
0
)
->
bSynchAddress
&
~
USB_DIR_IN
))))
{
(
!
is_playback
&&
ep
!=
(
unsigned
int
)(
get_endpoint
(
alts
,
0
)
->
bSynchAddress
&
~
USB_DIR_IN
))
||
(
is_playback
&&
!
implicit_fb
)))
{
snd_printk
(
KERN_ERR
"%d:%d:%d : invalid synch pipe
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EINVAL
;
}
ep
&=
USB_ENDPOINT_NUMBER_MASK
;
if
(
is_playback
)
subs
->
syncpipe
=
usb_rcvisocpipe
(
dev
,
ep
);
else
subs
->
syncpipe
=
usb_sndisocpipe
(
dev
,
ep
);
if
(
get_endpoint
(
alts
,
1
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
get_endpoint
(
alts
,
1
)
->
bRefresh
>=
1
&&
get_endpoint
(
alts
,
1
)
->
bRefresh
<=
9
)
subs
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bRefresh
;
else
if
(
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
)
subs
->
syncinterval
=
1
;
else
if
(
get_endpoint
(
alts
,
1
)
->
bInterval
>=
1
&&
get_endpoint
(
alts
,
1
)
->
bInterval
<=
16
)
subs
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bInterval
-
1
;
else
subs
->
syncinterval
=
3
;
subs
->
syncmaxsize
=
le16_to_cpu
(
get_endpoint
(
alts
,
1
)
->
wMaxPacketSize
);
}
/* always fill max packet size */
if
(
fmt
->
attributes
&
UAC_EP_CS_ATTR_FILL_MAX
)
subs
->
fill_max
=
1
;
implicit_fb
=
(
get_endpoint
(
alts
,
1
)
->
bmAttributes
&
USB_ENDPOINT_USAGE_MASK
)
==
USB_ENDPOINT_USAGE_IMPLICIT_FB
;
add_sync_ep:
subs
->
sync_endpoint
=
snd_usb_add_endpoint
(
subs
->
stream
->
chip
,
alts
,
ep
,
!
subs
->
direction
,
implicit_fb
?
SND_USB_ENDPOINT_TYPE_DATA
:
SND_USB_ENDPOINT_TYPE_SYNC
);
if
(
!
subs
->
sync_endpoint
)
return
-
EINVAL
;
subs
->
data_endpoint
->
sync_master
=
subs
->
sync_endpoint
;
}
if
((
err
=
snd_usb_init_pitch
(
subs
->
stream
->
chip
,
subs
->
interface
,
alts
,
fmt
))
<
0
)
return
err
;
...
...
@@ -390,12 +455,22 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if
(
changed
)
{
mutex_lock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
/* format changed */
snd_usb_release_substream_urbs
(
subs
,
0
);
/* influenced: period_bytes, channels, rate, format, */
ret
=
snd_usb_init_substream_urbs
(
subs
,
params_period_bytes
(
hw_params
),
params_rate
(
hw_params
),
snd_pcm_format_physical_width
(
params_format
(
hw_params
))
*
params_channels
(
hw_params
));
stop_endpoints
(
subs
,
0
,
0
,
0
);
deactivate_endpoints
(
subs
);
ret
=
activate_endpoints
(
subs
);
if
(
ret
<
0
)
goto
unlock
;
ret
=
snd_usb_endpoint_set_params
(
subs
->
data_endpoint
,
hw_params
,
fmt
,
subs
->
sync_endpoint
);
if
(
ret
<
0
)
goto
unlock
;
if
(
subs
->
sync_endpoint
)
ret
=
snd_usb_endpoint_set_params
(
subs
->
sync_endpoint
,
hw_params
,
fmt
,
NULL
);
unlock:
mutex_unlock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
}
...
...
@@ -415,7 +490,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs
->
cur_rate
=
0
;
subs
->
period_bytes
=
0
;
mutex_lock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
s
nd_usb_release_substream_urbs
(
subs
,
0
);
s
top_endpoints
(
subs
,
0
,
1
,
1
);
mutex_unlock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
return
snd_pcm_lib_free_vmalloc_buffer
(
substream
);
}
...
...
@@ -435,19 +510,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
return
-
ENXIO
;
}
if
(
snd_BUG_ON
(
!
subs
->
data_endpoint
))
return
-
EIO
;
/* some unit conversions in runtime */
subs
->
maxframesize
=
bytes_to_frames
(
runtime
,
subs
->
maxpacksize
);
subs
->
curframesize
=
bytes_to_frames
(
runtime
,
subs
->
curpacksize
);
subs
->
data_endpoint
->
maxframesize
=
bytes_to_frames
(
runtime
,
subs
->
data_endpoint
->
maxpacksize
);
subs
->
data_endpoint
->
curframesize
=
bytes_to_frames
(
runtime
,
subs
->
data_endpoint
->
curpacksize
);
/* reset the pointer */
subs
->
hwptr_done
=
0
;
subs
->
transfer_done
=
0
;
subs
->
phase
=
0
;
subs
->
last_delay
=
0
;
subs
->
last_frame_number
=
0
;
runtime
->
delay
=
0
;
return
snd_usb_substream_prepare
(
subs
,
runtime
);
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if
(
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
)
return
start_endpoints
(
subs
);
return
0
;
}
static
struct
snd_pcm_hardware
snd_usb_hardware
=
...
...
@@ -842,16 +926,171 @@ static int snd_usb_pcm_open(struct snd_pcm_substream *substream, int direction)
static
int
snd_usb_pcm_close
(
struct
snd_pcm_substream
*
substream
,
int
direction
)
{
int
ret
;
struct
snd_usb_stream
*
as
=
snd_pcm_substream_chip
(
substream
);
struct
snd_usb_substream
*
subs
=
&
as
->
substream
[
direction
];
if
(
!
as
->
chip
->
shutdown
&&
subs
->
interface
>=
0
)
{
usb_set_interface
(
subs
->
dev
,
subs
->
interface
,
0
);
subs
->
interface
=
-
1
;
}
stop_endpoints
(
subs
,
0
,
0
,
0
);
ret
=
deactivate_endpoints
(
subs
);
subs
->
pcm_substream
=
NULL
;
snd_usb_autosuspend
(
subs
->
stream
->
chip
);
return
0
;
return
ret
;
}
/* Since a URB can handle only a single linear buffer, we must use double
* buffering when the data to be transferred overflows the buffer boundary.
* To avoid inconsistencies when updating hwptr_done, we use double buffering
* for all URBs.
*/
static
void
retire_capture_urb
(
struct
snd_usb_substream
*
subs
,
struct
urb
*
urb
)
{
struct
snd_pcm_runtime
*
runtime
=
subs
->
pcm_substream
->
runtime
;
unsigned
int
stride
,
frames
,
bytes
,
oldptr
;
int
i
,
period_elapsed
=
0
;
unsigned
long
flags
;
unsigned
char
*
cp
;
stride
=
runtime
->
frame_bits
>>
3
;
for
(
i
=
0
;
i
<
urb
->
number_of_packets
;
i
++
)
{
cp
=
(
unsigned
char
*
)
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
i
].
offset
;
if
(
urb
->
iso_frame_desc
[
i
].
status
&&
printk_ratelimit
())
{
snd_printdd
(
KERN_ERR
"frame %d active: %d
\n
"
,
i
,
urb
->
iso_frame_desc
[
i
].
status
);
// continue;
}
bytes
=
urb
->
iso_frame_desc
[
i
].
actual_length
;
frames
=
bytes
/
stride
;
if
(
!
subs
->
txfr_quirk
)
bytes
=
frames
*
stride
;
if
(
bytes
%
(
runtime
->
sample_bits
>>
3
)
!=
0
)
{
#ifdef CONFIG_SND_DEBUG_VERBOSE
int
oldbytes
=
bytes
;
#endif
bytes
=
frames
*
stride
;
snd_printdd
(
KERN_ERR
"Corrected urb data len. %d->%d
\n
"
,
oldbytes
,
bytes
);
}
/* update the current pointer */
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
oldptr
=
subs
->
hwptr_done
;
subs
->
hwptr_done
+=
bytes
;
if
(
subs
->
hwptr_done
>=
runtime
->
buffer_size
*
stride
)
subs
->
hwptr_done
-=
runtime
->
buffer_size
*
stride
;
frames
=
(
bytes
+
(
oldptr
%
stride
))
/
stride
;
subs
->
transfer_done
+=
frames
;
if
(
subs
->
transfer_done
>=
runtime
->
period_size
)
{
subs
->
transfer_done
-=
runtime
->
period_size
;
period_elapsed
=
1
;
}
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
/* copy a data chunk */
if
(
oldptr
+
bytes
>
runtime
->
buffer_size
*
stride
)
{
unsigned
int
bytes1
=
runtime
->
buffer_size
*
stride
-
oldptr
;
memcpy
(
runtime
->
dma_area
+
oldptr
,
cp
,
bytes1
);
memcpy
(
runtime
->
dma_area
,
cp
+
bytes1
,
bytes
-
bytes1
);
}
else
{
memcpy
(
runtime
->
dma_area
+
oldptr
,
cp
,
bytes
);
}
}
if
(
period_elapsed
)
snd_pcm_period_elapsed
(
subs
->
pcm_substream
);
}
static
void
prepare_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
urb
*
urb
)
{
struct
snd_pcm_runtime
*
runtime
=
subs
->
pcm_substream
->
runtime
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
unsigned
int
counts
,
frames
,
bytes
;
int
i
,
stride
,
period_elapsed
=
0
;
unsigned
long
flags
;
stride
=
runtime
->
frame_bits
>>
3
;
frames
=
0
;
urb
->
number_of_packets
=
0
;
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
for
(
i
=
0
;
i
<
ctx
->
packets
;
i
++
)
{
counts
=
ctx
->
packet_size
[
i
];
/* set up descriptor */
urb
->
iso_frame_desc
[
i
].
offset
=
frames
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
frames
+=
counts
;
urb
->
number_of_packets
++
;
subs
->
transfer_done
+=
counts
;
if
(
subs
->
transfer_done
>=
runtime
->
period_size
)
{
subs
->
transfer_done
-=
runtime
->
period_size
;
period_elapsed
=
1
;
if
(
subs
->
fmt_type
==
UAC_FORMAT_TYPE_II
)
{
if
(
subs
->
transfer_done
>
0
)
{
/* FIXME: fill-max mode is not
* supported yet */
frames
-=
subs
->
transfer_done
;
counts
-=
subs
->
transfer_done
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
subs
->
transfer_done
=
0
;
}
i
++
;
if
(
i
<
ctx
->
packets
)
{
/* add a transfer delimiter */
urb
->
iso_frame_desc
[
i
].
offset
=
frames
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
0
;
urb
->
number_of_packets
++
;
}
break
;
}
}
if
(
period_elapsed
&&
!
snd_usb_endpoint_implict_feedback_sink
(
subs
->
data_endpoint
))
/* finish at the period boundary */
break
;
}
bytes
=
frames
*
stride
;
if
(
subs
->
hwptr_done
+
bytes
>
runtime
->
buffer_size
*
stride
)
{
/* err, the transferred area goes over buffer boundary. */
unsigned
int
bytes1
=
runtime
->
buffer_size
*
stride
-
subs
->
hwptr_done
;
memcpy
(
urb
->
transfer_buffer
,
runtime
->
dma_area
+
subs
->
hwptr_done
,
bytes1
);
memcpy
(
urb
->
transfer_buffer
+
bytes1
,
runtime
->
dma_area
,
bytes
-
bytes1
);
}
else
{
memcpy
(
urb
->
transfer_buffer
,
runtime
->
dma_area
+
subs
->
hwptr_done
,
bytes
);
}
subs
->
hwptr_done
+=
bytes
;
if
(
subs
->
hwptr_done
>=
runtime
->
buffer_size
*
stride
)
subs
->
hwptr_done
-=
runtime
->
buffer_size
*
stride
;
runtime
->
delay
+=
frames
;
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
urb
->
transfer_buffer_length
=
bytes
;
if
(
period_elapsed
)
snd_pcm_period_elapsed
(
subs
->
pcm_substream
);
}
/*
* process after playback data complete
* - decrease the delay count again
*/
static
void
retire_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
urb
*
urb
)
{
unsigned
long
flags
;
struct
snd_pcm_runtime
*
runtime
=
subs
->
pcm_substream
->
runtime
;
int
stride
=
runtime
->
frame_bits
>>
3
;
int
processed
=
urb
->
transfer_buffer_length
/
stride
;
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
if
(
processed
>
runtime
->
delay
)
runtime
->
delay
=
0
;
else
runtime
->
delay
-=
processed
;
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
}
static
int
snd_usb_playback_open
(
struct
snd_pcm_substream
*
substream
)
...
...
@@ -874,6 +1113,56 @@ static int snd_usb_capture_close(struct snd_pcm_substream *substream)
return
snd_usb_pcm_close
(
substream
,
SNDRV_PCM_STREAM_CAPTURE
);
}
static
int
snd_usb_substream_playback_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
struct
snd_usb_substream
*
subs
=
substream
->
runtime
->
private_data
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
subs
->
data_endpoint
->
prepare_data_urb
=
prepare_playback_urb
;
subs
->
data_endpoint
->
retire_data_urb
=
retire_playback_urb
;
return
0
;
case
SNDRV_PCM_TRIGGER_STOP
:
stop_endpoints
(
subs
,
0
,
0
,
0
);
return
0
;
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
subs
->
data_endpoint
->
prepare_data_urb
=
NULL
;
subs
->
data_endpoint
->
retire_data_urb
=
NULL
;
return
0
;
}
return
-
EINVAL
;
}
int
snd_usb_substream_capture_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
)
{
int
err
;
struct
snd_usb_substream
*
subs
=
substream
->
runtime
->
private_data
;
switch
(
cmd
)
{
case
SNDRV_PCM_TRIGGER_START
:
err
=
start_endpoints
(
subs
);
if
(
err
<
0
)
return
err
;
subs
->
data_endpoint
->
retire_data_urb
=
retire_capture_urb
;
return
0
;
case
SNDRV_PCM_TRIGGER_STOP
:
stop_endpoints
(
subs
,
0
,
0
,
0
);
return
0
;
case
SNDRV_PCM_TRIGGER_PAUSE_PUSH
:
subs
->
data_endpoint
->
retire_data_urb
=
NULL
;
return
0
;
case
SNDRV_PCM_TRIGGER_PAUSE_RELEASE
:
subs
->
data_endpoint
->
retire_data_urb
=
retire_capture_urb
;
return
0
;
}
return
-
EINVAL
;
}
static
struct
snd_pcm_ops
snd_usb_playback_ops
=
{
.
open
=
snd_usb_playback_open
,
.
close
=
snd_usb_playback_close
,
...
...
sound/usb/proc.c
View file @
56599bb0
...
...
@@ -115,6 +115,25 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
}
}
static
void
proc_dump_ep_status
(
struct
snd_usb_substream
*
subs
,
struct
snd_usb_endpoint
*
ep
,
struct
snd_info_buffer
*
buffer
)
{
if
(
!
ep
)
return
;
snd_iprintf
(
buffer
,
" Packet Size = %d
\n
"
,
ep
->
curpacksize
);
snd_iprintf
(
buffer
,
" Momentary freq = %u Hz (%#x.%04x)
\n
"
,
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
?
get_full_speed_hz
(
ep
->
freqm
)
:
get_high_speed_hz
(
ep
->
freqm
),
ep
->
freqm
>>
16
,
ep
->
freqm
&
0xffff
);
if
(
ep
->
freqshift
!=
INT_MIN
)
{
int
res
=
16
-
ep
->
freqshift
;
snd_iprintf
(
buffer
,
" Feedback Format = %d.%d
\n
"
,
(
ep
->
syncmaxsize
>
3
?
32
:
24
)
-
res
,
res
);
}
}
static
void
proc_dump_substream_status
(
struct
snd_usb_substream
*
subs
,
struct
snd_info_buffer
*
buffer
)
{
if
(
subs
->
running
)
{
...
...
@@ -126,17 +145,8 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
snd_iprintf
(
buffer
,
"%d "
,
subs
->
dataurb
[
i
].
packets
);
snd_iprintf
(
buffer
,
"]
\n
"
);
snd_iprintf
(
buffer
,
" Packet Size = %d
\n
"
,
subs
->
curpacksize
);
snd_iprintf
(
buffer
,
" Momentary freq = %u Hz (%#x.%04x)
\n
"
,
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
?
get_full_speed_hz
(
subs
->
freqm
)
:
get_high_speed_hz
(
subs
->
freqm
),
subs
->
freqm
>>
16
,
subs
->
freqm
&
0xffff
);
if
(
subs
->
freqshift
!=
INT_MIN
)
snd_iprintf
(
buffer
,
" Feedback Format = %d.%d
\n
"
,
(
subs
->
syncmaxsize
>
3
?
32
:
24
)
-
(
16
-
subs
->
freqshift
),
16
-
subs
->
freqshift
);
proc_dump_ep_status
(
subs
,
subs
->
data_endpoint
,
buffer
);
proc_dump_ep_status
(
subs
,
subs
->
sync_endpoint
,
buffer
);
}
else
{
snd_iprintf
(
buffer
,
" Status: Stop
\n
"
);
}
...
...
sound/usb/stream.c
View file @
56599bb0
...
...
@@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
}
}
/*
* initialize the substream instance.
*/
static
void
snd_usb_init_substream
(
struct
snd_usb_stream
*
as
,
int
stream
,
struct
audioformat
*
fp
)
{
struct
snd_usb_substream
*
subs
=
&
as
->
substream
[
stream
];
INIT_LIST_HEAD
(
&
subs
->
fmt_list
);
spin_lock_init
(
&
subs
->
lock
);
subs
->
stream
=
as
;
subs
->
direction
=
stream
;
subs
->
dev
=
as
->
chip
->
dev
;
subs
->
txfr_quirk
=
as
->
chip
->
txfr_quirk
;
snd_usb_set_pcm_ops
(
as
->
pcm
,
stream
);
list_add_tail
(
&
fp
->
list
,
&
subs
->
fmt_list
);
subs
->
formats
|=
fp
->
formats
;
subs
->
num_formats
++
;
subs
->
fmt_type
=
fp
->
fmt_type
;
}
/*
* add this endpoint to the chip instance.
...
...
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
continue
;
subs
=
&
as
->
substream
[
stream
];
if
(
!
subs
->
endpoint
)
if
(
!
subs
->
data_
endpoint
)
continue
;
if
(
subs
->
endpoint
==
fp
->
endpoint
)
{
if
(
subs
->
data_endpoint
->
ep_num
==
fp
->
endpoint
)
{
list_add_tail
(
&
fp
->
list
,
&
subs
->
fmt_list
);
subs
->
num_formats
++
;
subs
->
formats
|=
fp
->
formats
;
...
...
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
continue
;
subs
=
&
as
->
substream
[
stream
];
if
(
subs
->
endpoint
)
if
(
subs
->
data_
endpoint
)
continue
;
err
=
snd_pcm_new_stream
(
as
->
pcm
,
stream
,
1
);
if
(
err
<
0
)
...
...
sound/usb/usbaudio.h
View file @
56599bb0
...
...
@@ -36,6 +36,7 @@ struct snd_usb_audio {
struct
snd_card
*
card
;
struct
usb_interface
*
pm_intf
;
u32
usb_id
;
struct
mutex
mutex
;
struct
mutex
shutdown_mutex
;
unsigned
int
shutdown
:
1
;
unsigned
int
probing
:
1
;
...
...
@@ -46,6 +47,7 @@ struct snd_usb_audio {
int
num_suspended_intf
;
struct
list_head
pcm_list
;
/* list of pcm streams */
struct
list_head
ep_list
;
/* list of audio-related endpoints */
int
pcm_devs
;
struct
list_head
midi_list
;
/* list of midi interfaces */
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment