Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
56599bb0
cloud-kernel
项目概览
openanolis
/
cloud-kernel
1 年多 前同步成功
通知
161
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
56599bb0
编写于
4月 18, 2012
作者:
T
Takashi Iwai
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'topic/usb-endpoint' into topic/misc
上级
7536c301
22026c1a
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
1401 addition
and
825 deletion
+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
未找到文件。
sound/usb/card.c
浏览文件 @
56599bb0
...
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
...
@@ -131,8 +131,9 @@ static void snd_usb_stream_disconnect(struct list_head *head)
subs
=
&
as
->
substream
[
idx
];
subs
=
&
as
->
substream
[
idx
];
if
(
!
subs
->
num_formats
)
if
(
!
subs
->
num_formats
)
continue
;
continue
;
snd_usb_release_substream_urbs
(
subs
,
1
);
subs
->
interface
=
-
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)
...
@@ -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
)
static
int
snd_usb_audio_free
(
struct
snd_usb_audio
*
chip
)
{
{
mutex_destroy
(
&
chip
->
mutex
);
kfree
(
chip
);
kfree
(
chip
);
return
0
;
return
0
;
}
}
...
@@ -336,6 +338,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
...
@@ -336,6 +338,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
return
-
ENOMEM
;
return
-
ENOMEM
;
}
}
mutex_init
(
&
chip
->
mutex
);
mutex_init
(
&
chip
->
shutdown_mutex
);
mutex_init
(
&
chip
->
shutdown_mutex
);
chip
->
index
=
idx
;
chip
->
index
=
idx
;
chip
->
dev
=
dev
;
chip
->
dev
=
dev
;
...
@@ -348,6 +351,7 @@ static int snd_usb_audio_create(struct usb_device *dev, int idx,
...
@@ -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
),
chip
->
usb_id
=
USB_ID
(
le16_to_cpu
(
dev
->
descriptor
.
idVendor
),
le16_to_cpu
(
dev
->
descriptor
.
idProduct
));
le16_to_cpu
(
dev
->
descriptor
.
idProduct
));
INIT_LIST_HEAD
(
&
chip
->
pcm_list
);
INIT_LIST_HEAD
(
&
chip
->
pcm_list
);
INIT_LIST_HEAD
(
&
chip
->
ep_list
);
INIT_LIST_HEAD
(
&
chip
->
midi_list
);
INIT_LIST_HEAD
(
&
chip
->
midi_list
);
INIT_LIST_HEAD
(
&
chip
->
mixer_list
);
INIT_LIST_HEAD
(
&
chip
->
mixer_list
);
...
@@ -565,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
...
@@ -565,6 +569,10 @@ static void snd_usb_audio_disconnect(struct usb_device *dev,
list_for_each
(
p
,
&
chip
->
pcm_list
)
{
list_for_each
(
p
,
&
chip
->
pcm_list
)
{
snd_usb_stream_disconnect
(
p
);
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 */
/* release the midi resources */
list_for_each
(
p
,
&
chip
->
midi_list
)
{
list_for_each
(
p
,
&
chip
->
midi_list
)
{
snd_usbmidi_disconnect
(
p
);
snd_usbmidi_disconnect
(
p
);
...
...
sound/usb/card.h
浏览文件 @
56599bb0
...
@@ -30,13 +30,17 @@ struct audioformat {
...
@@ -30,13 +30,17 @@ struct audioformat {
};
};
struct
snd_usb_substream
;
struct
snd_usb_substream
;
struct
snd_usb_endpoint
;
struct
snd_urb_ctx
{
struct
snd_urb_ctx
{
struct
urb
*
urb
;
struct
urb
*
urb
;
unsigned
int
buffer_size
;
/* size of data buffer, if data URB */
unsigned
int
buffer_size
;
/* size of data buffer, if data URB */
struct
snd_usb_substream
*
subs
;
struct
snd_usb_substream
*
subs
;
struct
snd_usb_endpoint
*
ep
;
int
index
;
/* index for urb array */
int
index
;
/* index for urb array */
int
packets
;
/* number of packets per urb */
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
{
struct
snd_urb_ops
{
...
@@ -46,6 +50,60 @@ 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
);
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_substream
{
struct
snd_usb_stream
*
stream
;
struct
snd_usb_stream
*
stream
;
struct
usb_device
*
dev
;
struct
usb_device
*
dev
;
...
@@ -57,21 +115,6 @@ struct snd_usb_substream {
...
@@ -57,21 +115,6 @@ struct snd_usb_substream {
unsigned
int
cur_rate
;
/* current rate (for hw_params callback) */
unsigned
int
cur_rate
;
/* current rate (for hw_params callback) */
unsigned
int
period_bytes
;
/* current period bytes (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
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
txfr_quirk
:
1
;
/* allow sub-frame alignment */
unsigned
int
fmt_type
;
/* USB audio format type (1-3) */
unsigned
int
fmt_type
;
/* USB audio format type (1-3) */
...
@@ -87,6 +130,10 @@ struct snd_usb_substream {
...
@@ -87,6 +130,10 @@ struct snd_usb_substream {
struct
snd_urb_ctx
syncurb
[
SYNC_URBS
];
/* sync urb table */
struct
snd_urb_ctx
syncurb
[
SYNC_URBS
];
/* sync urb table */
char
*
syncbuf
;
/* sync buffer for all sync URBs */
char
*
syncbuf
;
/* sync buffer for all sync URBs */
dma_addr_t
sync_dma
;
/* DMA address of syncbuf */
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) */
u64
formats
;
/* format bitmasks (all or'ed) */
unsigned
int
num_formats
;
/* number of supported audio formats (list) */
unsigned
int
num_formats
;
/* number of supported audio formats (list) */
...
...
sound/usb/endpoint.c
浏览文件 @
56599bb0
...
@@ -20,9 +20,11 @@
...
@@ -20,9 +20,11 @@
#include <linux/ratelimit.h>
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include "usbaudio.h"
#include "usbaudio.h"
#include "helper.h"
#include "helper.h"
...
@@ -30,6 +32,35 @@
...
@@ -30,6 +32,35 @@
#include "endpoint.h"
#include "endpoint.h"
#include "pcm.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)
* convert a sampling rate into our full speed format (fs/1000 in Q16.16)
* this will overflow at approx 524 kHz
* this will overflow at approx 524 kHz
...
@@ -49,71 +80,414 @@ static inline unsigned get_usb_high_speed_rate(unsigned int rate)
...
@@ -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
;
if
(
u
->
buffer_size
)
unsigned
int
i
;
usb_free_coherent
(
u
->
ep
->
chip
->
dev
,
u
->
buffer_size
,
int
async
;
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... */
if
(
ep
->
fill_max
)
return
-
EBADFD
;
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
ret
;
return
0
;
}
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
static
void
retire_outbound_urb
(
struct
snd_usb_endpoint
*
ep
,
if
(
test_bit
(
i
,
&
subs
->
active_mask
))
{
struct
snd_urb_ctx
*
urb_ctx
)
if
(
!
test_and_set_bit
(
i
,
&
subs
->
unlink_mask
))
{
{
struct
urb
*
u
=
subs
->
dataurb
[
i
].
urb
;
if
(
ep
->
retire_data_urb
)
if
(
async
)
ep
->
retire_data_urb
(
ep
->
data_subs
,
urb_ctx
->
urb
);
usb_unlink_urb
(
u
);
}
else
usb_kill_urb
(
u
);
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
))
{
* Prepare a CAPTURE or SYNC urb for submission to the bus.
struct
urb
*
u
=
subs
->
syncurb
[
i
].
urb
;
*/
if
(
async
)
static
inline
void
prepare_inbound_urb
(
struct
snd_usb_endpoint
*
ep
,
usb_unlink_urb
(
u
);
struct
snd_urb_ctx
*
urb_ctx
)
else
{
usb_kill_urb
(
u
);
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
)
{
struct
list_head
*
p
;
if
(
u
->
buffer_size
)
struct
snd_usb_endpoint
*
ep
;
usb_free_coherent
(
u
->
subs
->
dev
,
u
->
buffer_size
,
int
ret
,
is_playback
=
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
u
->
urb
->
transfer_buffer
,
u
->
urb
->
transfer_dma
);
mutex_lock
(
&
chip
->
mutex
);
usb_free_urb
(
u
->
urb
);
u
->
urb
=
NULL
;
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.
* 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
long
end_time
=
jiffies
+
msecs_to_jiffies
(
1000
);
unsigned
int
i
;
unsigned
int
i
;
...
@@ -121,153 +495,148 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
...
@@ -121,153 +495,148 @@ static int wait_clear_urbs(struct snd_usb_substream *subs)
do
{
do
{
alive
=
0
;
alive
=
0
;
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
if
(
test_bit
(
i
,
&
subs
->
active_mask
))
if
(
test_bit
(
i
,
&
ep
->
active_mask
))
alive
++
;
alive
++
;
}
if
(
subs
->
syncpipe
)
{
if
(
!
alive
)
for
(
i
=
0
;
i
<
SYNC_URBS
;
i
++
)
{
if
(
test_bit
(
i
+
16
,
&
subs
->
active_mask
))
alive
++
;
}
}
if
(
!
alive
)
break
;
break
;
schedule_timeout_uninterruptible
(
1
);
schedule_timeout_uninterruptible
(
1
);
}
while
(
time_before
(
jiffies
,
end_time
));
}
while
(
time_before
(
jiffies
,
end_time
));
if
(
alive
)
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
;
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) */
if
(
!
force
&&
ep
->
chip
->
shutdown
)
/* to be sure... */
deactivate_urbs
(
subs
,
force
,
1
);
return
-
EBADFD
;
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
;
}
/*
async
=
!
can_sleep
&&
ep
->
chip
->
async_unlink
;
* complete callback from data urb
*/
clear_bit
(
EP_FLAG_RUNNING
,
&
ep
->
flags
);
static
void
snd_complete_urb
(
struct
urb
*
urb
)
{
INIT_LIST_HEAD
(
&
ep
->
ready_playback_urbs
);
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
ep
->
next_packet_read_pos
=
0
;
struct
snd_usb_substream
*
subs
=
ctx
->
subs
;
ep
->
next_packet_write_pos
=
0
;
struct
snd_pcm_substream
*
substream
=
ctx
->
subs
->
pcm_substream
;
int
err
=
0
;
if
(
!
async
&&
in_interrupt
())
return
0
;
if
((
subs
->
running
&&
subs
->
ops
.
retire
(
subs
,
substream
->
runtime
,
urb
))
||
!
subs
->
running
||
/* can be stopped during retire callback */
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
(
err
=
subs
->
ops
.
prepare
(
subs
,
substream
->
runtime
,
urb
))
<
0
||
if
(
test_bit
(
i
,
&
ep
->
active_mask
))
{
(
err
=
usb_submit_urb
(
urb
,
GFP_ATOMIC
))
<
0
)
{
if
(
!
test_and_set_bit
(
i
,
&
ep
->
unlink_mask
))
{
clear_bit
(
ctx
->
index
,
&
subs
->
active_mask
);
struct
urb
*
u
=
ep
->
urb
[
i
].
urb
;
if
(
err
<
0
)
{
if
(
async
)
snd_printd
(
KERN_ERR
"cannot submit urb (err = %d)
\n
"
,
err
);
usb_unlink_urb
(
u
);
snd_pcm_stop
(
substream
,
SNDRV_PCM_STATE_XRUN
);
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
;
int
i
;
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
);
}
}
}
/* 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
,
static
int
data_ep_set_params
(
struct
snd_usb_endpoint
*
ep
,
unsigned
int
period_byte
s
,
struct
snd_pcm_hw_params
*
hw_param
s
,
unsigned
int
rate
,
struct
audioformat
*
fmt
,
unsigned
int
frame_bits
)
struct
snd_usb_endpoint
*
sync_ep
)
{
{
unsigned
int
maxsize
,
i
;
unsigned
int
maxsize
,
i
,
urb_packs
,
total_packs
,
packs_per_ms
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
int
period_bytes
=
params_period_bytes
(
hw_params
);
unsigned
int
urb_packs
,
total_packs
,
packs_per_ms
;
int
format
=
params_format
(
hw_params
);
struct
snd_usb_audio
*
chip
=
subs
->
stream
->
chip
;
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 */
/* calculate max. frequency */
if
(
subs
->
maxpacksize
)
{
if
(
ep
->
maxpacksize
)
{
/* whatever fits into a max. size packet */
/* whatever fits into a max. size packet */
maxsize
=
subs
->
maxpacksize
;
maxsize
=
ep
->
maxpacksize
;
subs
->
freqmax
=
(
maxsize
/
(
frame_bits
>>
3
))
ep
->
freqmax
=
(
maxsize
/
(
frame_bits
>>
3
))
<<
(
16
-
subs
->
datainterval
);
<<
(
16
-
ep
->
datainterval
);
}
else
{
}
else
{
/* no max. packet size: just take 25% higher than nominal */
/* no max. packet size: just take 25% higher than nominal */
subs
->
freqmax
=
subs
->
freqn
+
(
subs
->
freqn
>>
2
);
ep
->
freqmax
=
ep
->
freqn
+
(
ep
->
freqn
>>
2
);
maxsize
=
((
subs
->
freqmax
+
0xffff
)
*
(
frame_bits
>>
3
))
maxsize
=
((
ep
->
freqmax
+
0xffff
)
*
(
frame_bits
>>
3
))
>>
(
16
-
subs
->
datainterval
);
>>
(
16
-
ep
->
datainterval
);
}
}
subs
->
phase
=
0
;
if
(
subs
->
fill_max
)
if
(
ep
->
fill_max
)
subs
->
curpacksize
=
subs
->
maxpacksize
;
ep
->
curpacksize
=
ep
->
maxpacksize
;
else
else
subs
->
curpacksize
=
maxsize
;
ep
->
curpacksize
=
maxsize
;
if
(
snd_usb_get_speed
(
subs
->
dev
)
!=
USB_SPEED_FULL
)
if
(
snd_usb_get_speed
(
ep
->
chip
->
dev
)
!=
USB_SPEED_FULL
)
packs_per_ms
=
8
>>
subs
->
datainterval
;
packs_per_ms
=
8
>>
ep
->
datainterval
;
else
else
packs_per_ms
=
1
;
packs_per_ms
=
1
;
if
(
is_playback
)
{
if
(
is_playback
&&
!
snd_usb_endpoint_implict_feedback_sink
(
ep
)
)
{
urb_packs
=
max
(
chip
->
nrpacks
,
1
);
urb_packs
=
max
(
ep
->
chip
->
nrpacks
,
1
);
urb_packs
=
min
(
urb_packs
,
(
unsigned
int
)
MAX_PACKS
);
urb_packs
=
min
(
urb_packs
,
(
unsigned
int
)
MAX_PACKS
);
}
else
}
else
{
urb_packs
=
1
;
urb_packs
=
1
;
}
urb_packs
*=
packs_per_ms
;
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 */
/* decide how many packets to be used */
if
(
is_playback
)
{
if
(
is_playback
&&
!
snd_usb_endpoint_implict_feedback_sink
(
ep
)
)
{
unsigned
int
minsize
,
maxpacks
;
unsigned
int
minsize
,
maxpacks
;
/* determine how small a packet can be */
/* determine how small a packet can be */
minsize
=
(
subs
->
freqn
>>
(
16
-
subs
->
datainterval
))
minsize
=
(
ep
->
freqn
>>
(
16
-
ep
->
datainterval
))
*
(
frame_bits
>>
3
);
*
(
frame_bits
>>
3
);
/* with sync from device, assume it can be 12% lower */
/* with sync from device, assume it can be 12% lower */
if
(
s
ubs
->
syncpipe
)
if
(
s
ync_ep
)
minsize
-=
minsize
>>
3
;
minsize
-=
minsize
>>
3
;
minsize
=
max
(
minsize
,
1u
);
minsize
=
max
(
minsize
,
1u
);
total_packs
=
(
period_bytes
+
minsize
-
1
)
/
minsize
;
total_packs
=
(
period_bytes
+
minsize
-
1
)
/
minsize
;
...
@@ -284,284 +653,466 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
...
@@ -284,284 +653,466 @@ int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
urb_packs
>>=
1
;
urb_packs
>>=
1
;
total_packs
=
MAX_URBS
*
urb_packs
;
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... */
/* too much... */
subs
->
nurbs
=
MAX_URBS
;
ep
->
nurbs
=
MAX_URBS
;
total_packs
=
MAX_URBS
*
urb_packs
;
total_packs
=
MAX_URBS
*
urb_packs
;
}
else
if
(
subs
->
nurbs
<
2
)
{
}
else
if
(
ep
->
nurbs
<
2
)
{
/* too little - we need at least two packets
/* too little - we need at least two packets
* to ensure contiguous playback/capture
* to ensure contiguous playback/capture
*/
*/
subs
->
nurbs
=
2
;
ep
->
nurbs
=
2
;
}
}
/* allocate and initialize data urbs */
/* allocate and initialize data urbs */
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
for
(
i
=
0
;
i
<
ep
->
nurbs
;
i
++
)
{
struct
snd_urb_ctx
*
u
=
&
subs
->
data
urb
[
i
];
struct
snd_urb_ctx
*
u
=
&
ep
->
urb
[
i
];
u
->
index
=
i
;
u
->
index
=
i
;
u
->
subs
=
subs
;
u
->
ep
=
ep
;
u
->
packets
=
(
i
+
1
)
*
total_packs
/
subs
->
nurbs
u
->
packets
=
(
i
+
1
)
*
total_packs
/
ep
->
nurbs
-
i
*
total_packs
/
subs
->
nurbs
;
-
i
*
total_packs
/
ep
->
nurbs
;
u
->
buffer_size
=
maxsize
*
u
->
packets
;
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
->
packets
++
;
/* for transfer delimiter */
u
->
urb
=
usb_alloc_urb
(
u
->
packets
,
GFP_KERNEL
);
u
->
urb
=
usb_alloc_urb
(
u
->
packets
,
GFP_KERNEL
);
if
(
!
u
->
urb
)
if
(
!
u
->
urb
)
goto
out_of_memory
;
goto
out_of_memory
;
u
->
urb
->
transfer_buffer
=
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
);
GFP_KERNEL
,
&
u
->
urb
->
transfer_dma
);
if
(
!
u
->
urb
->
transfer_buffer
)
if
(
!
u
->
urb
->
transfer_buffer
)
goto
out_of_memory
;
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
->
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
->
context
=
u
;
u
->
urb
->
complete
=
snd_complete_urb
;
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
;
return
0
;
out_of_memory:
out_of_memory:
snd_usb_release_substream_urbs
(
subs
,
0
);
release_urbs
(
ep
,
0
);
return
-
ENOMEM
;
return
-
ENOMEM
;
}
}
/*
/*
* prepare urb for full speed capture sync pipe
* configure a sync endpoint
*
* fill the length and offset of each urb descriptor.
* the fixed 10.14 frequency is passed through the pipe.
*/
*/
static
int
prepare_capture_sync_urb
(
struct
snd_usb_substream
*
subs
,
static
int
sync_ep_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_runtime
*
runtime
,
struct
snd_pcm_hw_params
*
hw_params
,
struct
urb
*
urb
)
struct
audioformat
*
fmt
)
{
{
unsigned
char
*
cp
=
urb
->
transfer_buffer
;
int
i
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
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
;
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.
* Determine the number of of URBs to be used on this endpoint.
* the fixed 12.13 frequency is passed as 16.16 through the pipe.
* 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
,
int
snd_usb_endpoint_set_params
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_runtime
*
runtime
,
struct
snd_pcm_hw_params
*
hw_params
,
struct
urb
*
urb
)
struct
audioformat
*
fmt
,
struct
snd_usb_endpoint
*
sync_ep
)
{
{
unsigned
char
*
cp
=
urb
->
transfer_buffer
;
int
err
;
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
if
(
ep
->
use_count
!=
0
)
{
urb
->
iso_frame_desc
[
0
].
length
=
4
;
snd_printk
(
KERN_WARNING
"Unable to change format on ep #%x: already in use
\n
"
,
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
ep
->
ep_num
);
cp
[
0
]
=
subs
->
freqn
;
return
-
EBUSY
;
cp
[
1
]
=
subs
->
freqn
>>
8
;
}
cp
[
2
]
=
subs
->
freqn
>>
16
;
cp
[
3
]
=
subs
->
freqn
>>
24
;
/* release old buffers, if any */
return
0
;
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
* snd_usb_endpoint_start: start an snd_usb_endpoint
* - nothing to do
*
* @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
,
int
snd_usb_endpoint_start
(
struct
snd_usb_endpoint
*
ep
)
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
{
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
;
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.
* Must be balanced to calls of snd_usb_endpoint_start().
* 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.
*/
*/
static
int
prepare_capture_urb
(
struct
snd_usb_substream
*
subs
,
void
snd_usb_endpoint_stop
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_runtime
*
runtime
,
int
force
,
int
can_sleep
,
int
wait
)
struct
urb
*
urb
)
{
{
i
nt
i
,
offs
;
i
f
(
!
ep
)
struct
snd_urb_ctx
*
ctx
=
urb
->
context
;
return
;
offs
=
0
;
if
(
snd_BUG_ON
(
ep
->
use_count
==
0
))
urb
->
dev
=
ctx
->
subs
->
dev
;
/* we need to set this at each time */
return
;
for
(
i
=
0
;
i
<
ctx
->
packets
;
i
++
)
{
urb
->
iso_frame_desc
[
i
].
offset
=
offs
;
if
(
snd_BUG_ON
(
!
test_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
)))
urb
->
iso_frame_desc
[
i
].
length
=
subs
->
curpacksize
;
return
;
offs
+=
subs
->
curpacksize
;
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
* In case of any active users, this functions does nothing.
* update the current position.
*
* Returns an error if usb_set_interface() failed, 0 in all other
* cases.
*/
*/
static
int
retire_capture_urb
(
struct
snd_usb_substream
*
subs
,
int
snd_usb_endpoint_activate
(
struct
snd_usb_endpoint
*
ep
)
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
{
unsigned
long
flags
;
if
(
ep
->
use_count
!=
0
)
unsigned
char
*
cp
;
return
0
;
int
i
;
unsigned
int
stride
,
frames
,
bytes
,
oldptr
;
int
period_elapsed
=
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
++
)
{
ret
=
usb_set_interface
(
ep
->
chip
->
dev
,
ep
->
iface
,
ep
->
alt_idx
);
cp
=
(
unsigned
char
*
)
urb
->
transfer_buffer
+
urb
->
iso_frame_desc
[
i
].
offset
;
if
(
ret
<
0
)
{
if
(
urb
->
iso_frame_desc
[
i
].
status
&&
printk_ratelimit
())
{
snd_printk
(
KERN_ERR
"%s() usb_set_interface() failed, ret = %d
\n
"
,
snd_printdd
(
"frame %d active: %d
\n
"
,
i
,
urb
->
iso_frame_desc
[
i
].
status
);
__func__
,
ret
);
// continue;
clear_bit
(
EP_FLAG_ACTIVATED
,
&
ep
->
flags
);
}
return
ret
;
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
);
}
}
return
0
;
}
}
if
(
period_elapsed
)
snd_pcm_period_elapsed
(
subs
->
pcm_substream
);
return
-
EBUSY
;
return
0
;
}
}
/*
/**
* 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
,
int
snd_usb_endpoint_deactivate
(
struct
snd_usb_endpoint
*
ep
)
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
{
return
0
;
if
(
!
ep
)
}
return
-
EINVAL
;
if
(
ep
->
use_count
!=
0
)
return
0
;
/*
if
(
!
ep
->
chip
->
shutdown
&&
* prepare urb for playback sync pipe
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
,
void
snd_usb_endpoint_free
(
struct
list_head
*
head
)
struct
snd_pcm_runtime
*
runtime
,
struct
urb
*
urb
)
{
{
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 */
ep
=
list_entry
(
head
,
struct
snd_usb_endpoint
,
list
);
urb
->
iso_frame_desc
[
0
].
length
=
min
(
4u
,
ctx
->
subs
->
syncmaxsize
);
release_urbs
(
ep
,
1
);
urb
->
iso_frame_desc
[
0
].
offset
=
0
;
kfree
(
ep
);
return
0
;
}
}
/*
/*
*
*
process after playback sync complete
*
snd_usb_handle_sync_urb: parse an USB sync packet
*
*
*
Full speed devices report feedback values in 10.14 format as samples per
*
@ep: the endpoint to handle the packet
*
frame, high speed devices in 16.16 format as samples per microframe.
*
@sender: the sending endpoint
*
Because the Audio Class 1 spec was written before USB 2.0, many high speed
*
@urb: the received packet
*
devices use a wrong interpretation, some others use an entirely different
*
*
format. Therefore, we cannot predict what format any particular device uses
*
This function is called from the context of an endpoint that received
*
and must detect it automatically
.
*
the packet and is used to let another endpoint object handle the payload
.
*/
*/
static
int
retire_playback_sync_urb
(
struct
snd_usb_substream
*
subs
,
void
snd_usb_handle_sync_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_pcm_runtime
*
runtime
,
struct
snd_usb_endpoint
*
sender
,
struct
urb
*
urb
)
const
struct
urb
*
urb
)
{
{
unsigned
int
f
;
int
shift
;
int
shift
;
unsigned
int
f
;
unsigned
long
flags
;
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
||
if
(
urb
->
iso_frame_desc
[
0
].
status
!=
0
||
urb
->
iso_frame_desc
[
0
].
actual_length
<
3
)
urb
->
iso_frame_desc
[
0
].
actual_length
<
3
)
return
0
;
return
;
f
=
le32_to_cpup
(
urb
->
transfer_buffer
);
f
=
le32_to_cpup
(
urb
->
transfer_buffer
);
if
(
urb
->
iso_frame_desc
[
0
].
actual_length
==
3
)
if
(
urb
->
iso_frame_desc
[
0
].
actual_length
==
3
)
f
&=
0x00ffffff
;
f
&=
0x00ffffff
;
else
else
f
&=
0x0fffffff
;
f
&=
0x0fffffff
;
if
(
f
==
0
)
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
* The first time we see a feedback value, determine its format
* by shifting it left or right until it matches the nominal
* 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,
...
@@ -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%.
* differ from the nominal value more than +50% or -25%.
*/
*/
shift
=
0
;
shift
=
0
;
while
(
f
<
subs
->
freqn
-
subs
->
freqn
/
4
)
{
while
(
f
<
ep
->
freqn
-
ep
->
freqn
/
4
)
{
f
<<=
1
;
f
<<=
1
;
shift
++
;
shift
++
;
}
}
while
(
f
>
subs
->
freqn
+
subs
->
freqn
/
2
)
{
while
(
f
>
ep
->
freqn
+
ep
->
freqn
/
2
)
{
f
>>=
1
;
f
>>=
1
;
shift
--
;
shift
--
;
}
}
subs
->
freqshift
=
shift
;
ep
->
freqshift
=
shift
;
}
}
else
if
(
ep
->
freqshift
>=
0
)
else
if
(
subs
->
freqshift
>=
0
)
f
<<=
ep
->
freqshift
;
f
<<=
subs
->
freqshift
;
else
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.
* If the frequency looks valid, set it.
* This value is referred to in prepare_playback_urb().
* This value is referred to in prepare_playback_urb().
*/
*/
spin_lock_irqsave
(
&
subs
->
lock
,
flags
);
spin_lock_irqsave
(
&
ep
->
lock
,
flags
);
subs
->
freqm
=
f
;
ep
->
freqm
=
f
;
spin_unlock_irqrestore
(
&
subs
->
lock
,
flags
);
spin_unlock_irqrestore
(
&
ep
->
lock
,
flags
);
}
else
{
}
else
{
/*
/*
* Out of range; maybe the shift value is wrong.
* Out of range; maybe the shift value is wrong.
* Reset it so that we autodetect again the next time.
* Reset it so that we autodetect again the next time.
*/
*/
subs
->
freqshift
=
INT_MIN
;
ep
->
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
);
}
}
}
}
/*
* 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
浏览文件 @
56599bb0
#ifndef __USBAUDIO_ENDPOINT_H
#ifndef __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
#define __USBAUDIO_ENDPOINT_H
void
snd_usb_init_substream
(
struct
snd_usb_stream
*
as
,
#define SND_USB_ENDPOINT_TYPE_DATA 0
int
stream
,
#define SND_USB_ENDPOINT_TYPE_SYNC 1
struct
audioformat
*
fp
);
int
snd_usb_init_substream_urbs
(
struct
snd_usb_substream
*
subs
,
struct
snd_usb_endpoint
*
snd_usb_add_endpoint
(
struct
snd_usb_audio
*
chip
,
unsigned
int
period_bytes
,
struct
usb_host_interface
*
alts
,
unsigned
int
rate
,
int
ep_num
,
int
direction
,
int
type
);
unsigned
int
frame_bits
);
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
,
int
snd_usb_endpoint_start
(
struct
snd_usb_endpoint
*
ep
);
struct
snd_pcm_runtime
*
runtime
);
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_endpoint_implict_feedback_sink
(
struct
snd_usb_endpoint
*
ep
);
int
snd_usb_substream_capture_trigger
(
struct
snd_pcm_substream
*
substream
,
int
cmd
);
void
snd_usb_handle_sync_urb
(
struct
snd_usb_endpoint
*
ep
,
struct
snd_usb_endpoint
*
sender
,
const
struct
urb
*
urb
);
#endif
/* __USBAUDIO_ENDPOINT_H */
#endif
/* __USBAUDIO_ENDPOINT_H */
sound/usb/pcm.c
浏览文件 @
56599bb0
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
#include <linux/init.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/ratelimit.h>
#include <linux/usb.h>
#include <linux/usb.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio.h>
#include <linux/usb/audio-v2.h>
#include <linux/usb/audio-v2.h>
...
@@ -34,6 +35,9 @@
...
@@ -34,6 +35,9 @@
#include "clock.h"
#include "clock.h"
#include "power.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 */
/* return the estimated delay based on USB frame counters */
snd_pcm_uframes_t
snd_usb_pcm_delay
(
struct
snd_usb_substream
*
subs
,
snd_pcm_uframes_t
snd_usb_pcm_delay
(
struct
snd_usb_substream
*
subs
,
unsigned
int
rate
)
unsigned
int
rate
)
...
@@ -208,6 +212,84 @@ int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
...
@@ -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
* 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)
...
@@ -219,7 +301,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct
usb_interface
*
iface
;
struct
usb_interface
*
iface
;
unsigned
int
ep
,
attr
;
unsigned
int
ep
,
attr
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
int
err
;
int
err
,
implicit_fb
=
0
;
iface
=
usb_ifnum_to_if
(
dev
,
fmt
->
iface
);
iface
=
usb_ifnum_to_if
(
dev
,
fmt
->
iface
);
if
(
WARN_ON
(
!
iface
))
if
(
WARN_ON
(
!
iface
))
...
@@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
...
@@ -232,40 +314,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
if
(
fmt
==
subs
->
cur_audiofmt
)
if
(
fmt
==
subs
->
cur_audiofmt
)
return
0
;
return
0
;
/* close the old interface */
subs
->
data_endpoint
=
snd_usb_add_endpoint
(
subs
->
stream
->
chip
,
if
(
subs
->
interface
>=
0
&&
subs
->
interface
!=
fmt
->
iface
)
{
alts
,
fmt
->
endpoint
,
subs
->
direction
,
if
(
usb_set_interface
(
subs
->
dev
,
subs
->
interface
,
0
)
<
0
)
{
SND_USB_ENDPOINT_TYPE_DATA
);
snd_printk
(
KERN_ERR
"%d:%d:%d: return to setting 0 failed
\n
"
,
if
(
!
subs
->
data_endpoint
)
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EINVAL
;
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
;
/* we need a sync pipe in async OUT or adaptive IN mode */
/* we need a sync pipe in async OUT or adaptive IN mode */
/* check the number of EP, since some devices have broken
/* 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)
...
@@ -273,8 +326,25 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
* assume it as adaptive-out or sync-in.
* assume it as adaptive-out or sync-in.
*/
*/
attr
=
fmt
->
ep_attr
&
USB_ENDPOINT_SYNCTYPE
;
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
)
||
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
)
{
altsd
->
bNumEndpoints
>=
2
)
{
/* check sync-pipe endpoint */
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
/* ... and check descriptor size before accessing bSynchAddress
...
@@ -282,7 +352,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
...
@@ -282,7 +352,8 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
the audio fields in the endpoint descriptors */
the audio fields in the endpoint descriptors */
if
((
get_endpoint
(
alts
,
1
)
->
bmAttributes
&
USB_ENDPOINT_XFERTYPE_MASK
)
!=
0x01
||
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
)
->
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
"
,
snd_printk
(
KERN_ERR
"%d:%d:%d : invalid synch pipe
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EINVAL
;
return
-
EINVAL
;
...
@@ -290,33 +361,27 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
...
@@ -290,33 +361,27 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
ep
=
get_endpoint
(
alts
,
1
)
->
bEndpointAddress
;
ep
=
get_endpoint
(
alts
,
1
)
->
bEndpointAddress
;
if
(
get_endpoint
(
alts
,
0
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
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
&&
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
"
,
snd_printk
(
KERN_ERR
"%d:%d:%d : invalid synch pipe
\n
"
,
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
dev
->
devnum
,
fmt
->
iface
,
fmt
->
altsetting
);
return
-
EINVAL
;
return
-
EINVAL
;
}
}
ep
&=
USB_ENDPOINT_NUMBER_MASK
;
if
(
is_playback
)
implicit_fb
=
(
get_endpoint
(
alts
,
1
)
->
bmAttributes
&
USB_ENDPOINT_USAGE_MASK
)
subs
->
syncpipe
=
usb_rcvisocpipe
(
dev
,
ep
);
==
USB_ENDPOINT_USAGE_IMPLICIT_FB
;
else
subs
->
syncpipe
=
usb_sndisocpipe
(
dev
,
ep
);
add_sync_ep:
if
(
get_endpoint
(
alts
,
1
)
->
bLength
>=
USB_DT_ENDPOINT_AUDIO_SIZE
&&
subs
->
sync_endpoint
=
snd_usb_add_endpoint
(
subs
->
stream
->
chip
,
get_endpoint
(
alts
,
1
)
->
bRefresh
>=
1
&&
alts
,
ep
,
!
subs
->
direction
,
get_endpoint
(
alts
,
1
)
->
bRefresh
<=
9
)
implicit_fb
?
subs
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bRefresh
;
SND_USB_ENDPOINT_TYPE_DATA
:
else
if
(
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
)
SND_USB_ENDPOINT_TYPE_SYNC
);
subs
->
syncinterval
=
1
;
if
(
!
subs
->
sync_endpoint
)
else
if
(
get_endpoint
(
alts
,
1
)
->
bInterval
>=
1
&&
return
-
EINVAL
;
get_endpoint
(
alts
,
1
)
->
bInterval
<=
16
)
subs
->
syncinterval
=
get_endpoint
(
alts
,
1
)
->
bInterval
-
1
;
subs
->
data_endpoint
->
sync_master
=
subs
->
sync_endpoint
;
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
;
if
((
err
=
snd_usb_init_pitch
(
subs
->
stream
->
chip
,
subs
->
interface
,
alts
,
fmt
))
<
0
)
if
((
err
=
snd_usb_init_pitch
(
subs
->
stream
->
chip
,
subs
->
interface
,
alts
,
fmt
))
<
0
)
return
err
;
return
err
;
...
@@ -390,12 +455,22 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
...
@@ -390,12 +455,22 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
if
(
changed
)
{
if
(
changed
)
{
mutex_lock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
mutex_lock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
/* format changed */
/* format changed */
snd_usb_release_substream_urbs
(
subs
,
0
);
stop_endpoints
(
subs
,
0
,
0
,
0
);
/* influenced: period_bytes, channels, rate, format, */
deactivate_endpoints
(
subs
);
ret
=
snd_usb_init_substream_urbs
(
subs
,
params_period_bytes
(
hw_params
),
params_rate
(
hw_params
),
ret
=
activate_endpoints
(
subs
);
snd_pcm_format_physical_width
(
params_format
(
hw_params
))
*
if
(
ret
<
0
)
params_channels
(
hw_params
));
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
);
mutex_unlock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
}
}
...
@@ -415,7 +490,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
...
@@ -415,7 +490,7 @@ static int snd_usb_hw_free(struct snd_pcm_substream *substream)
subs
->
cur_rate
=
0
;
subs
->
cur_rate
=
0
;
subs
->
period_bytes
=
0
;
subs
->
period_bytes
=
0
;
mutex_lock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
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
);
mutex_unlock
(
&
subs
->
stream
->
chip
->
shutdown_mutex
);
return
snd_pcm_lib_free_vmalloc_buffer
(
substream
);
return
snd_pcm_lib_free_vmalloc_buffer
(
substream
);
}
}
...
@@ -435,19 +510,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
...
@@ -435,19 +510,28 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
return
-
ENXIO
;
return
-
ENXIO
;
}
}
if
(
snd_BUG_ON
(
!
subs
->
data_endpoint
))
return
-
EIO
;
/* some unit conversions in runtime */
/* some unit conversions in runtime */
subs
->
maxframesize
=
bytes_to_frames
(
runtime
,
subs
->
maxpacksize
);
subs
->
data_endpoint
->
maxframesize
=
subs
->
curframesize
=
bytes_to_frames
(
runtime
,
subs
->
curpacksize
);
bytes_to_frames
(
runtime
,
subs
->
data_endpoint
->
maxpacksize
);
subs
->
data_endpoint
->
curframesize
=
bytes_to_frames
(
runtime
,
subs
->
data_endpoint
->
curpacksize
);
/* reset the pointer */
/* reset the pointer */
subs
->
hwptr_done
=
0
;
subs
->
hwptr_done
=
0
;
subs
->
transfer_done
=
0
;
subs
->
transfer_done
=
0
;
subs
->
phase
=
0
;
subs
->
last_delay
=
0
;
subs
->
last_delay
=
0
;
subs
->
last_frame_number
=
0
;
subs
->
last_frame_number
=
0
;
runtime
->
delay
=
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
=
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)
...
@@ -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
)
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_stream
*
as
=
snd_pcm_substream_chip
(
substream
);
struct
snd_usb_substream
*
subs
=
&
as
->
substream
[
direction
];
struct
snd_usb_substream
*
subs
=
&
as
->
substream
[
direction
];
if
(
!
as
->
chip
->
shutdown
&&
subs
->
interface
>=
0
)
{
stop_endpoints
(
subs
,
0
,
0
,
0
);
usb_set_interface
(
subs
->
dev
,
subs
->
interface
,
0
);
ret
=
deactivate_endpoints
(
subs
);
subs
->
interface
=
-
1
;
}
subs
->
pcm_substream
=
NULL
;
subs
->
pcm_substream
=
NULL
;
snd_usb_autosuspend
(
subs
->
stream
->
chip
);
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
)
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)
...
@@ -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
);
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
=
{
static
struct
snd_pcm_ops
snd_usb_playback_ops
=
{
.
open
=
snd_usb_playback_open
,
.
open
=
snd_usb_playback_open
,
.
close
=
snd_usb_playback_close
,
.
close
=
snd_usb_playback_close
,
...
...
sound/usb/proc.c
浏览文件 @
56599bb0
...
@@ -115,6 +115,25 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
...
@@ -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
)
static
void
proc_dump_substream_status
(
struct
snd_usb_substream
*
subs
,
struct
snd_info_buffer
*
buffer
)
{
{
if
(
subs
->
running
)
{
if
(
subs
->
running
)
{
...
@@ -126,17 +145,8 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn
...
@@ -126,17 +145,8 @@ static void proc_dump_substream_status(struct snd_usb_substream *subs, struct sn
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
snd_iprintf
(
buffer
,
"%d "
,
subs
->
dataurb
[
i
].
packets
);
snd_iprintf
(
buffer
,
"%d "
,
subs
->
dataurb
[
i
].
packets
);
snd_iprintf
(
buffer
,
"]
\n
"
);
snd_iprintf
(
buffer
,
"]
\n
"
);
snd_iprintf
(
buffer
,
" Packet Size = %d
\n
"
,
subs
->
curpacksize
);
proc_dump_ep_status
(
subs
,
subs
->
data_endpoint
,
buffer
);
snd_iprintf
(
buffer
,
" Momentary freq = %u Hz (%#x.%04x)
\n
"
,
proc_dump_ep_status
(
subs
,
subs
->
sync_endpoint
,
buffer
);
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
);
}
else
{
}
else
{
snd_iprintf
(
buffer
,
" Status: Stop
\n
"
);
snd_iprintf
(
buffer
,
" Status: Stop
\n
"
);
}
}
...
...
sound/usb/stream.c
浏览文件 @
56599bb0
...
@@ -73,6 +73,31 @@ static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
...
@@ -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.
* add this endpoint to the chip instance.
...
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
...
@@ -94,9 +119,9 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
continue
;
continue
;
subs
=
&
as
->
substream
[
stream
];
subs
=
&
as
->
substream
[
stream
];
if
(
!
subs
->
endpoint
)
if
(
!
subs
->
data_
endpoint
)
continue
;
continue
;
if
(
subs
->
endpoint
==
fp
->
endpoint
)
{
if
(
subs
->
data_endpoint
->
ep_num
==
fp
->
endpoint
)
{
list_add_tail
(
&
fp
->
list
,
&
subs
->
fmt_list
);
list_add_tail
(
&
fp
->
list
,
&
subs
->
fmt_list
);
subs
->
num_formats
++
;
subs
->
num_formats
++
;
subs
->
formats
|=
fp
->
formats
;
subs
->
formats
|=
fp
->
formats
;
...
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
...
@@ -109,7 +134,7 @@ int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
if
(
as
->
fmt_type
!=
fp
->
fmt_type
)
continue
;
continue
;
subs
=
&
as
->
substream
[
stream
];
subs
=
&
as
->
substream
[
stream
];
if
(
subs
->
endpoint
)
if
(
subs
->
data_
endpoint
)
continue
;
continue
;
err
=
snd_pcm_new_stream
(
as
->
pcm
,
stream
,
1
);
err
=
snd_pcm_new_stream
(
as
->
pcm
,
stream
,
1
);
if
(
err
<
0
)
if
(
err
<
0
)
...
...
sound/usb/usbaudio.h
浏览文件 @
56599bb0
...
@@ -36,6 +36,7 @@ struct snd_usb_audio {
...
@@ -36,6 +36,7 @@ struct snd_usb_audio {
struct
snd_card
*
card
;
struct
snd_card
*
card
;
struct
usb_interface
*
pm_intf
;
struct
usb_interface
*
pm_intf
;
u32
usb_id
;
u32
usb_id
;
struct
mutex
mutex
;
struct
mutex
shutdown_mutex
;
struct
mutex
shutdown_mutex
;
unsigned
int
shutdown
:
1
;
unsigned
int
shutdown
:
1
;
unsigned
int
probing
:
1
;
unsigned
int
probing
:
1
;
...
@@ -46,6 +47,7 @@ struct snd_usb_audio {
...
@@ -46,6 +47,7 @@ struct snd_usb_audio {
int
num_suspended_intf
;
int
num_suspended_intf
;
struct
list_head
pcm_list
;
/* list of pcm streams */
struct
list_head
pcm_list
;
/* list of pcm streams */
struct
list_head
ep_list
;
/* list of audio-related endpoints */
int
pcm_devs
;
int
pcm_devs
;
struct
list_head
midi_list
;
/* list of midi interfaces */
struct
list_head
midi_list
;
/* list of midi interfaces */
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录