Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
afad17c0
Commit
afad17c0
authored
Mar 24, 2009
by
Takashi Iwai
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/usb-audio' into for-linus
parents
d0807323
b1c86bb8
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
154 additions
and
105 deletions
+154
-105
sound/usb/usbaudio.c
sound/usb/usbaudio.c
+33
-46
sound/usb/usbmixer.c
sound/usb/usbmixer.c
+84
-58
sound/usb/usbmixer_maps.c
sound/usb/usbmixer_maps.c
+26
-0
sound/usb/usbquirks.h
sound/usb/usbquirks.h
+10
-0
sound/usb/usx2y/usb_stream.c
sound/usb/usx2y/usb_stream.c
+1
-1
No files found.
sound/usb/usbaudio.c
View file @
afad17c0
...
@@ -107,7 +107,7 @@ MODULE_PARM_DESC(ignore_ctl_error,
...
@@ -107,7 +107,7 @@ MODULE_PARM_DESC(ignore_ctl_error,
#define MAX_PACKS_HS (MAX_PACKS * 8)
/* in high speed mode */
#define MAX_PACKS_HS (MAX_PACKS * 8)
/* in high speed mode */
#define MAX_URBS 8
#define MAX_URBS 8
#define SYNC_URBS 4
/* always four urbs for sync */
#define SYNC_URBS 4
/* always four urbs for sync */
#define M
IN_PACKS_URB 1
/* minimum 1 packet per urb
*/
#define M
AX_QUEUE 24
/* try not to exceed this queue length, in ms
*/
struct
audioformat
{
struct
audioformat
{
struct
list_head
list
;
struct
list_head
list
;
...
@@ -525,7 +525,7 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
...
@@ -525,7 +525,7 @@ static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
/*
/*
* Prepare urb for streaming before playback starts or when paused.
* Prepare urb for streaming before playback starts or when paused.
*
*
* We don't have any data, so we send
a frame of
silence.
* We don't have any data, so we send silence.
*/
*/
static
int
prepare_nodata_playback_urb
(
struct
snd_usb_substream
*
subs
,
static
int
prepare_nodata_playback_urb
(
struct
snd_usb_substream
*
subs
,
struct
snd_pcm_runtime
*
runtime
,
struct
snd_pcm_runtime
*
runtime
,
...
@@ -537,13 +537,13 @@ static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
...
@@ -537,13 +537,13 @@ static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
offs
=
0
;
offs
=
0
;
urb
->
dev
=
ctx
->
subs
->
dev
;
urb
->
dev
=
ctx
->
subs
->
dev
;
urb
->
number_of_packets
=
subs
->
packs_per_ms
;
for
(
i
=
0
;
i
<
ctx
->
packets
;
++
i
)
{
for
(
i
=
0
;
i
<
subs
->
packs_per_ms
;
++
i
)
{
counts
=
snd_usb_audio_next_packet_size
(
subs
);
counts
=
snd_usb_audio_next_packet_size
(
subs
);
urb
->
iso_frame_desc
[
i
].
offset
=
offs
*
stride
;
urb
->
iso_frame_desc
[
i
].
offset
=
offs
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
urb
->
iso_frame_desc
[
i
].
length
=
counts
*
stride
;
offs
+=
counts
;
offs
+=
counts
;
}
}
urb
->
number_of_packets
=
ctx
->
packets
;
urb
->
transfer_buffer_length
=
offs
*
stride
;
urb
->
transfer_buffer_length
=
offs
*
stride
;
memset
(
urb
->
transfer_buffer
,
memset
(
urb
->
transfer_buffer
,
subs
->
cur_audiofmt
->
format
==
SNDRV_PCM_FORMAT_U8
?
0x80
:
0
,
subs
->
cur_audiofmt
->
format
==
SNDRV_PCM_FORMAT_U8
?
0x80
:
0
,
...
@@ -1034,9 +1034,9 @@ static void release_substream_urbs(struct snd_usb_substream *subs, int force)
...
@@ -1034,9 +1034,9 @@ static void release_substream_urbs(struct snd_usb_substream *subs, int force)
static
int
init_substream_urbs
(
struct
snd_usb_substream
*
subs
,
unsigned
int
period_bytes
,
static
int
init_substream_urbs
(
struct
snd_usb_substream
*
subs
,
unsigned
int
period_bytes
,
unsigned
int
rate
,
unsigned
int
frame_bits
)
unsigned
int
rate
,
unsigned
int
frame_bits
)
{
{
unsigned
int
maxsize
,
n
,
i
;
unsigned
int
maxsize
,
i
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
int
is_playback
=
subs
->
direction
==
SNDRV_PCM_STREAM_PLAYBACK
;
unsigned
int
npacks
[
MAX_URBS
],
urb_packs
,
total_packs
,
packs_per_ms
;
unsigned
int
urb_packs
,
total_packs
,
packs_per_ms
;
/* calculate the frequency in 16.16 format */
/* calculate the frequency in 16.16 format */
if
(
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
)
if
(
snd_usb_get_speed
(
subs
->
dev
)
==
USB_SPEED_FULL
)
...
@@ -1070,8 +1070,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
...
@@ -1070,8 +1070,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
subs
->
packs_per_ms
=
packs_per_ms
;
subs
->
packs_per_ms
=
packs_per_ms
;
if
(
is_playback
)
{
if
(
is_playback
)
{
urb_packs
=
nrpacks
;
urb_packs
=
max
(
nrpacks
,
1
);
urb_packs
=
max
(
urb_packs
,
(
unsigned
int
)
MIN_PACKS_URB
);
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
;
...
@@ -1079,7 +1078,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
...
@@ -1079,7 +1078,7 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
/* decide how many packets to be used */
/* decide how many packets to be used */
if
(
is_playback
)
{
if
(
is_playback
)
{
unsigned
int
minsize
;
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
=
(
subs
->
freqn
>>
(
16
-
subs
->
datainterval
))
*
(
frame_bits
>>
3
);
*
(
frame_bits
>>
3
);
...
@@ -1092,8 +1091,13 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
...
@@ -1092,8 +1091,13 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
total_packs
=
(
total_packs
+
packs_per_ms
-
1
)
total_packs
=
(
total_packs
+
packs_per_ms
-
1
)
&
~
(
packs_per_ms
-
1
);
&
~
(
packs_per_ms
-
1
);
/* we need at least two URBs for queueing */
/* we need at least two URBs for queueing */
if
(
total_packs
<
2
*
MIN_PACKS_URB
*
packs_per_ms
)
if
(
total_packs
<
2
*
packs_per_ms
)
{
total_packs
=
2
*
MIN_PACKS_URB
*
packs_per_ms
;
total_packs
=
2
*
packs_per_ms
;
}
else
{
/* and we don't want too long a queue either */
maxpacks
=
max
(
MAX_QUEUE
*
packs_per_ms
,
urb_packs
*
2
);
total_packs
=
min
(
total_packs
,
maxpacks
);
}
}
else
{
}
else
{
total_packs
=
MAX_URBS
*
urb_packs
;
total_packs
=
MAX_URBS
*
urb_packs
;
}
}
...
@@ -1102,31 +1106,11 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
...
@@ -1102,31 +1106,11 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
/* too much... */
/* too much... */
subs
->
nurbs
=
MAX_URBS
;
subs
->
nurbs
=
MAX_URBS
;
total_packs
=
MAX_URBS
*
urb_packs
;
total_packs
=
MAX_URBS
*
urb_packs
;
}
}
else
if
(
subs
->
nurbs
<
2
)
{
n
=
total_packs
;
for
(
i
=
0
;
i
<
subs
->
nurbs
;
i
++
)
{
npacks
[
i
]
=
n
>
urb_packs
?
urb_packs
:
n
;
n
-=
urb_packs
;
}
if
(
subs
->
nurbs
<=
1
)
{
/* 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
;
subs
->
nurbs
=
2
;
npacks
[
0
]
=
(
total_packs
+
1
)
/
2
;
npacks
[
1
]
=
total_packs
-
npacks
[
0
];
}
else
if
(
npacks
[
subs
->
nurbs
-
1
]
<
MIN_PACKS_URB
*
packs_per_ms
)
{
/* the last packet is too small.. */
if
(
subs
->
nurbs
>
2
)
{
/* merge to the first one */
npacks
[
0
]
+=
npacks
[
subs
->
nurbs
-
1
];
subs
->
nurbs
--
;
}
else
{
/* divide to two */
subs
->
nurbs
=
2
;
npacks
[
0
]
=
(
total_packs
+
1
)
/
2
;
npacks
[
1
]
=
total_packs
-
npacks
[
0
];
}
}
}
/* allocate and initialize data urbs */
/* allocate and initialize data urbs */
...
@@ -1134,7 +1118,8 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
...
@@ -1134,7 +1118,8 @@ static int init_substream_urbs(struct snd_usb_substream *subs, unsigned int peri
struct
snd_urb_ctx
*
u
=
&
subs
->
dataurb
[
i
];
struct
snd_urb_ctx
*
u
=
&
subs
->
dataurb
[
i
];
u
->
index
=
i
;
u
->
index
=
i
;
u
->
subs
=
subs
;
u
->
subs
=
subs
;
u
->
packets
=
npacks
[
i
];
u
->
packets
=
(
i
+
1
)
*
total_packs
/
subs
->
nurbs
-
i
*
total_packs
/
subs
->
nurbs
;
u
->
buffer_size
=
maxsize
*
u
->
packets
;
u
->
buffer_size
=
maxsize
*
u
->
packets
;
if
(
subs
->
fmt_type
==
USB_FORMAT_TYPE_II
)
if
(
subs
->
fmt_type
==
USB_FORMAT_TYPE_II
)
u
->
packets
++
;
/* for transfer delimiter */
u
->
packets
++
;
/* for transfer delimiter */
...
@@ -1292,14 +1277,14 @@ static int init_usb_sample_rate(struct usb_device *dev, int iface,
...
@@ -1292,14 +1277,14 @@ static int init_usb_sample_rate(struct usb_device *dev, int iface,
if
((
err
=
snd_usb_ctl_msg
(
dev
,
usb_sndctrlpipe
(
dev
,
0
),
SET_CUR
,
if
((
err
=
snd_usb_ctl_msg
(
dev
,
usb_sndctrlpipe
(
dev
,
0
),
SET_CUR
,
USB_TYPE_CLASS
|
USB_RECIP_ENDPOINT
|
USB_DIR_OUT
,
USB_TYPE_CLASS
|
USB_RECIP_ENDPOINT
|
USB_DIR_OUT
,
SAMPLING_FREQ_CONTROL
<<
8
,
ep
,
data
,
3
,
1000
))
<
0
)
{
SAMPLING_FREQ_CONTROL
<<
8
,
ep
,
data
,
3
,
1000
))
<
0
)
{
snd_printk
(
KERN_ERR
"%d:%d:%d: cannot set freq %d to ep
0x%
x
\n
"
,
snd_printk
(
KERN_ERR
"%d:%d:%d: cannot set freq %d to ep
%#
x
\n
"
,
dev
->
devnum
,
iface
,
fmt
->
altsetting
,
rate
,
ep
);
dev
->
devnum
,
iface
,
fmt
->
altsetting
,
rate
,
ep
);
return
err
;
return
err
;
}
}
if
((
err
=
snd_usb_ctl_msg
(
dev
,
usb_rcvctrlpipe
(
dev
,
0
),
GET_CUR
,
if
((
err
=
snd_usb_ctl_msg
(
dev
,
usb_rcvctrlpipe
(
dev
,
0
),
GET_CUR
,
USB_TYPE_CLASS
|
USB_RECIP_ENDPOINT
|
USB_DIR_IN
,
USB_TYPE_CLASS
|
USB_RECIP_ENDPOINT
|
USB_DIR_IN
,
SAMPLING_FREQ_CONTROL
<<
8
,
ep
,
data
,
3
,
1000
))
<
0
)
{
SAMPLING_FREQ_CONTROL
<<
8
,
ep
,
data
,
3
,
1000
))
<
0
)
{
snd_printk
(
KERN_WARNING
"%d:%d:%d: cannot get freq at ep
0x%
x
\n
"
,
snd_printk
(
KERN_WARNING
"%d:%d:%d: cannot get freq at ep
%#
x
\n
"
,
dev
->
devnum
,
iface
,
fmt
->
altsetting
,
ep
);
dev
->
devnum
,
iface
,
fmt
->
altsetting
,
ep
);
return
0
;
/* some devices don't support reading */
return
0
;
/* some devices don't support reading */
}
}
...
@@ -1431,9 +1416,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
...
@@ -1431,9 +1416,11 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs
->
cur_audiofmt
=
fmt
;
subs
->
cur_audiofmt
=
fmt
;
#if 0
#if 0
printk("setting done: format = %d, rate = %d..%d, channels = %d\n",
printk(KERN_DEBUG
"setting done: format = %d, rate = %d..%d, channels = %d\n",
fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
printk(" datapipe = 0x%0x, syncpipe = 0x%0x\n",
printk(KERN_DEBUG
" datapipe = 0x%0x, syncpipe = 0x%0x\n",
subs->datapipe, subs->syncpipe);
subs->datapipe, subs->syncpipe);
#endif
#endif
...
@@ -1468,7 +1455,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
...
@@ -1468,7 +1455,7 @@ static int snd_usb_hw_params(struct snd_pcm_substream *substream,
channels
=
params_channels
(
hw_params
);
channels
=
params_channels
(
hw_params
);
fmt
=
find_format
(
subs
,
format
,
rate
,
channels
);
fmt
=
find_format
(
subs
,
format
,
rate
,
channels
);
if
(
!
fmt
)
{
if
(
!
fmt
)
{
snd_printd
(
KERN_DEBUG
"cannot set format: format =
0x%
x, rate = %d, channels = %d
\n
"
,
snd_printd
(
KERN_DEBUG
"cannot set format: format =
%#
x, rate = %d, channels = %d
\n
"
,
format
,
rate
,
channels
);
format
,
rate
,
channels
);
return
-
EINVAL
;
return
-
EINVAL
;
}
}
...
@@ -1795,7 +1782,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
...
@@ -1795,7 +1782,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
if
(
rates
[
f
->
format
]
&&
rates
[
f
->
format
]
!=
f
->
rates
)
if
(
rates
[
f
->
format
]
&&
rates
[
f
->
format
]
!=
f
->
rates
)
goto
__out
;
goto
__out
;
}
}
channels
[
f
->
format
]
|=
(
1
<<
f
->
channels
);
channels
[
f
->
format
]
|=
1
<<
(
f
->
channels
-
1
);
rates
[
f
->
format
]
|=
f
->
rates
;
rates
[
f
->
format
]
|=
f
->
rates
;
/* needs knot? */
/* needs knot? */
if
(
f
->
rates
&
SNDRV_PCM_RATE_KNOT
)
if
(
f
->
rates
&
SNDRV_PCM_RATE_KNOT
)
...
@@ -1822,7 +1809,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
...
@@ -1822,7 +1809,7 @@ static int check_hw_params_convention(struct snd_usb_substream *subs)
continue
;
continue
;
for
(
i
=
0
;
i
<
32
;
i
++
)
{
for
(
i
=
0
;
i
<
32
;
i
++
)
{
if
(
f
->
rates
&
(
1
<<
i
))
if
(
f
->
rates
&
(
1
<<
i
))
channels
[
i
]
|=
(
1
<<
f
->
channels
);
channels
[
i
]
|=
1
<<
(
f
->
channels
-
1
);
}
}
}
}
cmaster
=
0
;
cmaster
=
0
;
...
@@ -1919,7 +1906,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
...
@@ -1919,7 +1906,7 @@ static int setup_hw_info(struct snd_pcm_runtime *runtime, struct snd_usb_substre
* in the current code assume the 1ms period.
* in the current code assume the 1ms period.
*/
*/
snd_pcm_hw_constraint_minmax
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIOD_TIME
,
snd_pcm_hw_constraint_minmax
(
runtime
,
SNDRV_PCM_HW_PARAM_PERIOD_TIME
,
1000
*
MIN_PACKS_URB
,
1000
,
/*(nrpacks * MAX_URBS) * 1000*/
UINT_MAX
);
/*(nrpacks * MAX_URBS) * 1000*/
UINT_MAX
);
err
=
check_hw_params_convention
(
subs
);
err
=
check_hw_params_convention
(
subs
);
...
@@ -2160,7 +2147,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
...
@@ -2160,7 +2147,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
fp
=
list_entry
(
p
,
struct
audioformat
,
list
);
fp
=
list_entry
(
p
,
struct
audioformat
,
list
);
snd_iprintf
(
buffer
,
" Interface %d
\n
"
,
fp
->
iface
);
snd_iprintf
(
buffer
,
" Interface %d
\n
"
,
fp
->
iface
);
snd_iprintf
(
buffer
,
" Altset %d
\n
"
,
fp
->
altsetting
);
snd_iprintf
(
buffer
,
" Altset %d
\n
"
,
fp
->
altsetting
);
snd_iprintf
(
buffer
,
" Format:
0x%
x
\n
"
,
fp
->
format
);
snd_iprintf
(
buffer
,
" Format:
%#
x
\n
"
,
fp
->
format
);
snd_iprintf
(
buffer
,
" Channels: %d
\n
"
,
fp
->
channels
);
snd_iprintf
(
buffer
,
" Channels: %d
\n
"
,
fp
->
channels
);
snd_iprintf
(
buffer
,
" Endpoint: %d %s (%s)
\n
"
,
snd_iprintf
(
buffer
,
" Endpoint: %d %s (%s)
\n
"
,
fp
->
endpoint
&
USB_ENDPOINT_NUMBER_MASK
,
fp
->
endpoint
&
USB_ENDPOINT_NUMBER_MASK
,
...
@@ -2180,7 +2167,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
...
@@ -2180,7 +2167,7 @@ static void proc_dump_substream_formats(struct snd_usb_substream *subs, struct s
snd_iprintf
(
buffer
,
"
\n
"
);
snd_iprintf
(
buffer
,
"
\n
"
);
}
}
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
// snd_iprintf(buffer, " Max Packet Size = %d\n", fp->maxpacksize);
// snd_iprintf(buffer, " EP Attribute =
0x%
x\n", fp->attributes);
// snd_iprintf(buffer, " EP Attribute =
%#
x\n", fp->attributes);
}
}
}
}
...
@@ -2621,7 +2608,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat
...
@@ -2621,7 +2608,7 @@ static int parse_audio_format_ii(struct snd_usb_audio *chip, struct audioformat
fp
->
format
=
SNDRV_PCM_FORMAT_MPEG
;
fp
->
format
=
SNDRV_PCM_FORMAT_MPEG
;
break
;
break
;
default:
default:
snd_printd
(
KERN_INFO
"%d:%u:%d : unknown format tag
0x%
x is detected. processed as MPEG.
\n
"
,
snd_printd
(
KERN_INFO
"%d:%u:%d : unknown format tag
%#
x is detected. processed as MPEG.
\n
"
,
chip
->
dev
->
devnum
,
fp
->
iface
,
fp
->
altsetting
,
format
);
chip
->
dev
->
devnum
,
fp
->
iface
,
fp
->
altsetting
,
format
);
fp
->
format
=
SNDRV_PCM_FORMAT_MPEG
;
fp
->
format
=
SNDRV_PCM_FORMAT_MPEG
;
break
;
break
;
...
@@ -2819,7 +2806,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
...
@@ -2819,7 +2806,7 @@ static int parse_audio_endpoints(struct snd_usb_audio *chip, int iface_no)
continue
;
continue
;
}
}
snd_printdd
(
KERN_INFO
"%d:%u:%d: add audio endpoint
0x%
x
\n
"
,
dev
->
devnum
,
iface_no
,
altno
,
fp
->
endpoint
);
snd_printdd
(
KERN_INFO
"%d:%u:%d: add audio endpoint
%#
x
\n
"
,
dev
->
devnum
,
iface_no
,
altno
,
fp
->
endpoint
);
err
=
add_audio_endpoint
(
chip
,
stream
,
fp
);
err
=
add_audio_endpoint
(
chip
,
stream
,
fp
);
if
(
err
<
0
)
{
if
(
err
<
0
)
{
kfree
(
fp
->
rate_table
);
kfree
(
fp
->
rate_table
);
...
@@ -3766,7 +3753,7 @@ static int usb_audio_resume(struct usb_interface *intf)
...
@@ -3766,7 +3753,7 @@ static int usb_audio_resume(struct usb_interface *intf)
static
int
__init
snd_usb_audio_init
(
void
)
static
int
__init
snd_usb_audio_init
(
void
)
{
{
if
(
nrpacks
<
MIN_PACKS_URB
||
nrpacks
>
MAX_PACKS
)
{
if
(
nrpacks
<
1
||
nrpacks
>
MAX_PACKS
)
{
printk
(
KERN_WARNING
"invalid nrpacks value.
\n
"
);
printk
(
KERN_WARNING
"invalid nrpacks value.
\n
"
);
return
-
EINVAL
;
return
-
EINVAL
;
}
}
...
...
sound/usb/usbmixer.c
View file @
afad17c0
...
@@ -66,6 +66,7 @@ static const struct rc_config {
...
@@ -66,6 +66,7 @@ static const struct rc_config {
{
USB_ID
(
0x041e
,
0x3000
),
0
,
1
,
2
,
1
,
18
,
0x0013
},
/* Extigy */
{
USB_ID
(
0x041e
,
0x3000
),
0
,
1
,
2
,
1
,
18
,
0x0013
},
/* Extigy */
{
USB_ID
(
0x041e
,
0x3020
),
2
,
1
,
6
,
6
,
18
,
0x0013
},
/* Audigy 2 NX */
{
USB_ID
(
0x041e
,
0x3020
),
2
,
1
,
6
,
6
,
18
,
0x0013
},
/* Audigy 2 NX */
{
USB_ID
(
0x041e
,
0x3040
),
2
,
2
,
6
,
6
,
2
,
0x6e91
},
/* Live! 24-bit */
{
USB_ID
(
0x041e
,
0x3040
),
2
,
2
,
6
,
6
,
2
,
0x6e91
},
/* Live! 24-bit */
{
USB_ID
(
0x041e
,
0x3048
),
2
,
2
,
6
,
6
,
2
,
0x6e91
},
/* Toshiba SB0500 */
};
};
struct
usb_mixer_interface
{
struct
usb_mixer_interface
{
...
@@ -109,6 +110,8 @@ struct mixer_build {
...
@@ -109,6 +110,8 @@ struct mixer_build {
const
struct
usbmix_selector_map
*
selector_map
;
const
struct
usbmix_selector_map
*
selector_map
;
};
};
#define MAX_CHANNELS 10
/* max logical channels */
struct
usb_mixer_elem_info
{
struct
usb_mixer_elem_info
{
struct
usb_mixer_interface
*
mixer
;
struct
usb_mixer_interface
*
mixer
;
struct
usb_mixer_elem_info
*
next_id_elem
;
/* list of controls with same id */
struct
usb_mixer_elem_info
*
next_id_elem
;
/* list of controls with same id */
...
@@ -119,6 +122,8 @@ struct usb_mixer_elem_info {
...
@@ -119,6 +122,8 @@ struct usb_mixer_elem_info {
int
channels
;
int
channels
;
int
val_type
;
int
val_type
;
int
min
,
max
,
res
;
int
min
,
max
,
res
;
int
cached
;
int
cache_val
[
MAX_CHANNELS
];
u8
initialized
;
u8
initialized
;
};
};
...
@@ -180,8 +185,6 @@ enum {
...
@@ -180,8 +185,6 @@ enum {
USB_PROC_DCR_RELEASE
=
6
,
USB_PROC_DCR_RELEASE
=
6
,
};
};
#define MAX_CHANNELS 10
/* max logical channels */
/*
/*
* manual mapping of mixer names
* manual mapping of mixer names
...
@@ -218,7 +221,10 @@ static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
...
@@ -218,7 +221,10 @@ static int check_ignored_ctl(struct mixer_build *state, int unitid, int control)
for
(
p
=
state
->
map
;
p
->
id
;
p
++
)
{
for
(
p
=
state
->
map
;
p
->
id
;
p
++
)
{
if
(
p
->
id
==
unitid
&&
!
p
->
name
&&
if
(
p
->
id
==
unitid
&&
!
p
->
name
&&
(
!
control
||
!
p
->
control
||
control
==
p
->
control
))
{
(
!
control
||
!
p
->
control
||
control
==
p
->
control
))
{
// printk("ignored control %d:%d\n", unitid, control);
/*
printk(KERN_DEBUG "ignored control %d:%d\n",
unitid, control);
*/
return
1
;
return
1
;
}
}
}
}
...
@@ -375,11 +381,35 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
...
@@ -375,11 +381,35 @@ static int get_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int *
}
}
/* channel = 0: master, 1 = first channel */
/* channel = 0: master, 1 = first channel */
static
inline
int
get_cur_mix_value
(
struct
usb_mixer_elem_info
*
cval
,
int
channel
,
int
*
value
)
static
inline
int
get_cur_mix_raw
(
struct
usb_mixer_elem_info
*
cval
,
int
channel
,
int
*
value
)
{
{
return
get_ctl_value
(
cval
,
GET_CUR
,
(
cval
->
control
<<
8
)
|
channel
,
value
);
return
get_ctl_value
(
cval
,
GET_CUR
,
(
cval
->
control
<<
8
)
|
channel
,
value
);
}
}
static
int
get_cur_mix_value
(
struct
usb_mixer_elem_info
*
cval
,
int
channel
,
int
index
,
int
*
value
)
{
int
err
;
if
(
cval
->
cached
&
(
1
<<
channel
))
{
*
value
=
cval
->
cache_val
[
index
];
return
0
;
}
err
=
get_cur_mix_raw
(
cval
,
channel
,
value
);
if
(
err
<
0
)
{
if
(
!
cval
->
mixer
->
ignore_ctl_error
)
snd_printd
(
KERN_ERR
"cannot get current value for "
"control %d ch %d: err = %d
\n
"
,
cval
->
control
,
channel
,
err
);
return
err
;
}
cval
->
cached
|=
1
<<
channel
;
cval
->
cache_val
[
index
]
=
*
value
;
return
0
;
}
/*
/*
* set a mixer value
* set a mixer value
*/
*/
...
@@ -411,9 +441,17 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int v
...
@@ -411,9 +441,17 @@ static int set_cur_ctl_value(struct usb_mixer_elem_info *cval, int validx, int v
return
set_ctl_value
(
cval
,
SET_CUR
,
validx
,
value
);
return
set_ctl_value
(
cval
,
SET_CUR
,
validx
,
value
);
}
}
static
inline
int
set_cur_mix_value
(
struct
usb_mixer_elem_info
*
cval
,
int
channel
,
int
value
)
static
int
set_cur_mix_value
(
struct
usb_mixer_elem_info
*
cval
,
int
channel
,
int
index
,
int
value
)
{
{
return
set_ctl_value
(
cval
,
SET_CUR
,
(
cval
->
control
<<
8
)
|
channel
,
value
);
int
err
;
err
=
set_ctl_value
(
cval
,
SET_CUR
,
(
cval
->
control
<<
8
)
|
channel
,
value
);
if
(
err
<
0
)
return
err
;
cval
->
cached
|=
1
<<
channel
;
cval
->
cache_val
[
index
]
=
value
;
return
0
;
}
}
/*
/*
...
@@ -717,7 +755,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
...
@@ -717,7 +755,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
if
(
cval
->
min
+
cval
->
res
<
cval
->
max
)
{
if
(
cval
->
min
+
cval
->
res
<
cval
->
max
)
{
int
last_valid_res
=
cval
->
res
;
int
last_valid_res
=
cval
->
res
;
int
saved
,
test
,
check
;
int
saved
,
test
,
check
;
get_cur_mix_
value
(
cval
,
minchn
,
&
saved
);
get_cur_mix_
raw
(
cval
,
minchn
,
&
saved
);
for
(;;)
{
for
(;;)
{
test
=
saved
;
test
=
saved
;
if
(
test
<
cval
->
max
)
if
(
test
<
cval
->
max
)
...
@@ -725,8 +763,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
...
@@ -725,8 +763,8 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
else
else
test
-=
cval
->
res
;
test
-=
cval
->
res
;
if
(
test
<
cval
->
min
||
test
>
cval
->
max
||
if
(
test
<
cval
->
min
||
test
>
cval
->
max
||
set_cur_mix_value
(
cval
,
minchn
,
test
)
||
set_cur_mix_value
(
cval
,
minchn
,
0
,
test
)
||
get_cur_mix_
value
(
cval
,
minchn
,
&
check
))
{
get_cur_mix_
raw
(
cval
,
minchn
,
&
check
))
{
cval
->
res
=
last_valid_res
;
cval
->
res
=
last_valid_res
;
break
;
break
;
}
}
...
@@ -734,7 +772,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
...
@@ -734,7 +772,7 @@ static int get_min_max(struct usb_mixer_elem_info *cval, int default_min)
break
;
break
;
cval
->
res
*=
2
;
cval
->
res
*=
2
;
}
}
set_cur_mix_value
(
cval
,
minchn
,
saved
);
set_cur_mix_value
(
cval
,
minchn
,
0
,
saved
);
}
}
cval
->
initialized
=
1
;
cval
->
initialized
=
1
;
...
@@ -774,35 +812,25 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
...
@@ -774,35 +812,25 @@ static int mixer_ctl_feature_get(struct snd_kcontrol *kcontrol, struct snd_ctl_e
struct
usb_mixer_elem_info
*
cval
=
kcontrol
->
private_data
;
struct
usb_mixer_elem_info
*
cval
=
kcontrol
->
private_data
;
int
c
,
cnt
,
val
,
err
;
int
c
,
cnt
,
val
,
err
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
cval
->
min
;
if
(
cval
->
cmask
)
{
if
(
cval
->
cmask
)
{
cnt
=
0
;
cnt
=
0
;
for
(
c
=
0
;
c
<
MAX_CHANNELS
;
c
++
)
{
for
(
c
=
0
;
c
<
MAX_CHANNELS
;
c
++
)
{
if
(
cval
->
cmask
&
(
1
<<
c
))
{
if
(
!
(
cval
->
cmask
&
(
1
<<
c
)))
err
=
get_cur_mix_value
(
cval
,
c
+
1
,
&
val
);
continue
;
if
(
err
<
0
)
{
err
=
get_cur_mix_value
(
cval
,
c
+
1
,
cnt
,
&
val
);
if
(
cval
->
mixer
->
ignore_ctl_error
)
{
if
(
err
<
0
)
ucontrol
->
value
.
integer
.
value
[
0
]
=
cval
->
min
;
return
cval
->
mixer
->
ignore_ctl_error
?
0
:
err
;
return
0
;
val
=
get_relative_value
(
cval
,
val
);
}
ucontrol
->
value
.
integer
.
value
[
cnt
]
=
val
;
snd_printd
(
KERN_ERR
"cannot get current value for control %d ch %d: err = %d
\n
"
,
cval
->
control
,
c
+
1
,
err
);
cnt
++
;
return
err
;
}
val
=
get_relative_value
(
cval
,
val
);
ucontrol
->
value
.
integer
.
value
[
cnt
]
=
val
;
cnt
++
;
}
}
}
return
0
;
}
else
{
}
else
{
/* master channel */
/* master channel */
err
=
get_cur_mix_value
(
cval
,
0
,
&
val
);
err
=
get_cur_mix_value
(
cval
,
0
,
0
,
&
val
);
if
(
err
<
0
)
{
if
(
err
<
0
)
if
(
cval
->
mixer
->
ignore_ctl_error
)
{
return
cval
->
mixer
->
ignore_ctl_error
?
0
:
err
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
cval
->
min
;
return
0
;
}
snd_printd
(
KERN_ERR
"cannot get current value for control %d master ch: err = %d
\n
"
,
cval
->
control
,
err
);
return
err
;
}
val
=
get_relative_value
(
cval
,
val
);
val
=
get_relative_value
(
cval
,
val
);
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
;
ucontrol
->
value
.
integer
.
value
[
0
]
=
val
;
}
}
...
@@ -819,34 +847,28 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
...
@@ -819,34 +847,28 @@ static int mixer_ctl_feature_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
if
(
cval
->
cmask
)
{
if
(
cval
->
cmask
)
{
cnt
=
0
;
cnt
=
0
;
for
(
c
=
0
;
c
<
MAX_CHANNELS
;
c
++
)
{
for
(
c
=
0
;
c
<
MAX_CHANNELS
;
c
++
)
{
if
(
cval
->
cmask
&
(
1
<<
c
))
{
if
(
!
(
cval
->
cmask
&
(
1
<<
c
)))
err
=
get_cur_mix_value
(
cval
,
c
+
1
,
&
oval
);
continue
;
if
(
err
<
0
)
{
err
=
get_cur_mix_value
(
cval
,
c
+
1
,
cnt
,
&
oval
);
if
(
cval
->
mixer
->
ignore_ctl_error
)
if
(
err
<
0
)
return
0
;
return
cval
->
mixer
->
ignore_ctl_error
?
0
:
err
;
return
err
;
val
=
ucontrol
->
value
.
integer
.
value
[
cnt
];
}
val
=
get_abs_value
(
cval
,
val
);
val
=
ucontrol
->
value
.
integer
.
value
[
cnt
];
if
(
oval
!=
val
)
{
val
=
get_abs_value
(
cval
,
val
);
set_cur_mix_value
(
cval
,
c
+
1
,
cnt
,
val
);
if
(
oval
!=
val
)
{
changed
=
1
;
set_cur_mix_value
(
cval
,
c
+
1
,
val
);
changed
=
1
;
}
get_cur_mix_value
(
cval
,
c
+
1
,
&
val
);
cnt
++
;
}
}
cnt
++
;
}
}
}
else
{
}
else
{
/* master channel */
/* master channel */
err
=
get_cur_mix_value
(
cval
,
0
,
&
oval
);
err
=
get_cur_mix_value
(
cval
,
0
,
0
,
&
oval
);
if
(
err
<
0
&&
cval
->
mixer
->
ignore_ctl_error
)
return
0
;
if
(
err
<
0
)
if
(
err
<
0
)
return
err
;
return
cval
->
mixer
->
ignore_ctl_error
?
0
:
err
;
val
=
ucontrol
->
value
.
integer
.
value
[
0
];
val
=
ucontrol
->
value
.
integer
.
value
[
0
];
val
=
get_abs_value
(
cval
,
val
);
val
=
get_abs_value
(
cval
,
val
);
if
(
val
!=
oval
)
{
if
(
val
!=
oval
)
{
set_cur_mix_value
(
cval
,
0
,
val
);
set_cur_mix_value
(
cval
,
0
,
0
,
val
);
changed
=
1
;
changed
=
1
;
}
}
}
}
...
@@ -1705,7 +1727,8 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
...
@@ -1705,7 +1727,8 @@ static void snd_usb_mixer_memory_change(struct usb_mixer_interface *mixer,
break
;
break
;
/* live24ext: 4 = line-in jack */
/* live24ext: 4 = line-in jack */
case
3
:
/* hp-out jack (may actuate Mute) */
case
3
:
/* hp-out jack (may actuate Mute) */
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
))
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
)
||
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3048
))
snd_usb_mixer_notify_id
(
mixer
,
mixer
->
rc_cfg
->
mute_mixer_id
);
snd_usb_mixer_notify_id
(
mixer
,
mixer
->
rc_cfg
->
mute_mixer_id
);
break
;
break
;
default:
default:
...
@@ -1936,8 +1959,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
...
@@ -1936,8 +1959,9 @@ static int snd_audigy2nx_controls_create(struct usb_mixer_interface *mixer)
int
i
,
err
;
int
i
,
err
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
snd_audigy2nx_controls
);
++
i
)
{
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
snd_audigy2nx_controls
);
++
i
)
{
if
(
i
>
1
&&
/* Live24ext has 2 LEDs only */
if
(
i
>
1
&&
/* Live24ext has 2 LEDs only */
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
))
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
)
||
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3048
)))
break
;
break
;
err
=
snd_ctl_add
(
mixer
->
chip
->
card
,
err
=
snd_ctl_add
(
mixer
->
chip
->
card
,
snd_ctl_new1
(
&
snd_audigy2nx_controls
[
i
],
mixer
));
snd_ctl_new1
(
&
snd_audigy2nx_controls
[
i
],
mixer
));
...
@@ -1974,7 +1998,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
...
@@ -1974,7 +1998,8 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
snd_iprintf
(
buffer
,
"%s jacks
\n\n
"
,
mixer
->
chip
->
card
->
shortname
);
snd_iprintf
(
buffer
,
"%s jacks
\n\n
"
,
mixer
->
chip
->
card
->
shortname
);
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3020
))
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3020
))
jacks
=
jacks_audigy2nx
;
jacks
=
jacks_audigy2nx
;
else
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
))
else
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
)
||
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3048
))
jacks
=
jacks_live24ext
;
jacks
=
jacks_live24ext
;
else
else
return
;
return
;
...
@@ -2024,7 +2049,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
...
@@ -2024,7 +2049,8 @@ int snd_usb_create_mixer(struct snd_usb_audio *chip, int ctrlif,
goto
_error
;
goto
_error
;
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3020
)
||
if
(
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3020
)
||
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
))
{
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3040
)
||
mixer
->
chip
->
usb_id
==
USB_ID
(
0x041e
,
0x3048
))
{
struct
snd_info_entry
*
entry
;
struct
snd_info_entry
*
entry
;
if
((
err
=
snd_audigy2nx_controls_create
(
mixer
))
<
0
)
if
((
err
=
snd_audigy2nx_controls_create
(
mixer
))
<
0
)
...
...
sound/usb/usbmixer_maps.c
View file @
afad17c0
...
@@ -261,6 +261,22 @@ static struct usbmix_name_map aureon_51_2_map[] = {
...
@@ -261,6 +261,22 @@ static struct usbmix_name_map aureon_51_2_map[] = {
{}
/* terminator */
{}
/* terminator */
};
};
static
struct
usbmix_name_map
scratch_live_map
[]
=
{
/* 1: IT Line 1 (USB streaming) */
/* 2: OT Line 1 (Speaker) */
/* 3: IT Line 1 (Line connector) */
{
4
,
"Line 1 In"
},
/* FU */
/* 5: OT Line 1 (USB streaming) */
/* 6: IT Line 2 (USB streaming) */
/* 7: OT Line 2 (Speaker) */
/* 8: IT Line 2 (Line connector) */
{
9
,
"Line 2 In"
},
/* FU */
/* 10: OT Line 2 (USB streaming) */
/* 11: IT Mic (Line connector) */
/* 12: OT Mic (USB streaming) */
{
0
}
/* terminator */
};
/*
/*
* Control map entries
* Control map entries
*/
*/
...
@@ -284,6 +300,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
...
@@ -284,6 +300,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.
id
=
USB_ID
(
0x041e
,
0x3040
),
.
id
=
USB_ID
(
0x041e
,
0x3040
),
.
map
=
live24ext_map
,
.
map
=
live24ext_map
,
},
},
{
.
id
=
USB_ID
(
0x041e
,
0x3048
),
.
map
=
audigy2nx_map
,
.
selector_map
=
audigy2nx_selectors
,
},
{
{
/* Hercules DJ Console (Windows Edition) */
/* Hercules DJ Console (Windows Edition) */
.
id
=
USB_ID
(
0x06f8
,
0xb000
),
.
id
=
USB_ID
(
0x06f8
,
0xb000
),
...
@@ -311,6 +332,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
...
@@ -311,6 +332,11 @@ static struct usbmix_ctl_map usbmix_ctl_maps[] = {
.
id
=
USB_ID
(
0x0ccd
,
0x0028
),
.
id
=
USB_ID
(
0x0ccd
,
0x0028
),
.
map
=
aureon_51_2_map
,
.
map
=
aureon_51_2_map
,
},
},
{
.
id
=
USB_ID
(
0x13e5
,
0x0001
),
.
map
=
scratch_live_map
,
.
ignore_ctl_error
=
1
,
},
{
0
}
/* terminator */
{
0
}
/* terminator */
};
};
sound/usb/usbquirks.h
View file @
afad17c0
...
@@ -39,6 +39,16 @@
...
@@ -39,6 +39,16 @@
.idProduct = prod, \
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
/* Creative/Toshiba Multimedia Center SB-0500 */
{
USB_DEVICE
(
0x041e
,
0x3048
),
.
driver_info
=
(
unsigned
long
)
&
(
const
struct
snd_usb_audio_quirk
)
{
.
vendor_name
=
"Toshiba"
,
.
product_name
=
"SB-0500"
,
.
ifnum
=
QUIRK_NO_INTERFACE
}
},
/* Creative/E-Mu devices */
/* Creative/E-Mu devices */
{
{
USB_DEVICE
(
0x041e
,
0x3010
),
USB_DEVICE
(
0x041e
,
0x3010
),
...
...
sound/usb/usx2y/usb_stream.c
View file @
afad17c0
...
@@ -557,7 +557,7 @@ static void stream_start(struct usb_stream_kernel *sk,
...
@@ -557,7 +557,7 @@ static void stream_start(struct usb_stream_kernel *sk,
s
->
idle_insize
-=
max_diff
-
max_diff_0
;
s
->
idle_insize
-=
max_diff
-
max_diff_0
;
s
->
idle_insize
+=
urb_size
-
s
->
period_size
;
s
->
idle_insize
+=
urb_size
-
s
->
period_size
;
if
(
s
->
idle_insize
<
0
)
{
if
(
s
->
idle_insize
<
0
)
{
snd_printk
(
"%i %i %i
\n
"
,
snd_printk
(
KERN_WARNING
"%i %i %i
\n
"
,
s
->
idle_insize
,
urb_size
,
s
->
period_size
);
s
->
idle_insize
,
urb_size
,
s
->
period_size
);
return
;
return
;
}
else
if
(
s
->
idle_insize
==
0
)
{
}
else
if
(
s
->
idle_insize
==
0
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment