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
Kirill Smelkov
linux
Commits
273639ad
Commit
273639ad
authored
Oct 07, 2002
by
Jaroslav Kysela
Browse files
Options
Browse Files
Download
Plain Diff
Merge
http://linux.bkbits.net/linux-2.5
into suse.cz:/home/perex/bk/linux-sound/linux-sound
parents
1815a7a3
b414e441
Changes
11
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
745 additions
and
325 deletions
+745
-325
include/sound/version.h
include/sound/version.h
+1
-1
sound/core/info.c
sound/core/info.c
+0
-6
sound/core/sound.c
sound/core/sound.c
+0
-9
sound/pci/Config.in
sound/pci/Config.in
+2
-2
sound/pci/cs46xx/dsp_spos.c
sound/pci/cs46xx/dsp_spos.c
+0
-1
sound/pci/cs46xx/dsp_spos_scb_lib.c
sound/pci/cs46xx/dsp_spos_scb_lib.c
+0
-1
sound/sound_core.c
sound/sound_core.c
+10
-5
sound/usb/usbaudio.c
sound/usb/usbaudio.c
+100
-57
sound/usb/usbaudio.h
sound/usb/usbaudio.h
+22
-7
sound/usb/usbmidi.c
sound/usb/usbmidi.c
+307
-88
sound/usb/usbquirks.h
sound/usb/usbquirks.h
+303
-148
No files found.
include/sound/version.h
View file @
273639ad
/* include/version.h. Generated automatically by configure. */
#define CONFIG_SND_VERSION "0.9.0rc3"
#define CONFIG_SND_DATE " (
Tue Oct 01 14:40:2
3 2002 UTC)"
#define CONFIG_SND_DATE " (
Fri Oct 04 13:09:1
3 2002 UTC)"
sound/core/info.c
View file @
273639ad
...
...
@@ -960,7 +960,6 @@ void snd_info_free_device(snd_info_entry_t * entry)
{
#ifdef CONFIG_DEVFS_FS
char
dname
[
32
];
devfs_handle_t
master
;
#endif
snd_runtime_check
(
entry
,
return
);
...
...
@@ -970,12 +969,7 @@ void snd_info_free_device(snd_info_entry_t * entry)
#ifdef CONFIG_DEVFS_FS
if
(
entry
->
p
&&
strncmp
(
entry
->
name
,
"controlC"
,
8
))
{
sprintf
(
dname
,
"snd/%s"
,
entry
->
name
);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
master
=
devfs_find_handle
(
NULL
,
dname
,
strlen
(
dname
),
0
,
0
,
DEVFS_SPECIAL_CHR
,
0
);
devfs_unregister
(
master
);
#else
devfs_find_and_unregister
(
NULL
,
dname
,
0
,
0
,
DEVFS_SPECIAL_CHR
,
0
);
#endif
}
#endif
snd_info_free_entry
(
entry
);
...
...
sound/core/sound.c
View file @
273639ad
...
...
@@ -358,21 +358,12 @@ static int __init alsa_sound_init(void)
static
void
__exit
alsa_sound_exit
(
void
)
{
#ifdef CONFIG_DEVFS_FS
devfs_handle_t
master
;
char
controlname
[
24
];
short
controlnum
;
for
(
controlnum
=
0
;
controlnum
<
snd_cards_limit
;
controlnum
++
)
{
sprintf
(
controlname
,
"snd/controlC%d"
,
controlnum
);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
master
=
devfs_find_handle
(
NULL
,
controlname
,
strlen
(
controlname
),
0
,
0
,
DEVFS_SPECIAL_CHR
,
0
);
devfs_unregister
(
master
);
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
master
=
devfs_find_handle
(
NULL
,
controlname
,
0
,
0
,
DEVFS_SPECIAL_CHR
,
0
);
devfs_unregister
(
master
);
#else
devfs_find_and_unregister
(
NULL
,
controlname
,
0
,
0
,
DEVFS_SPECIAL_CHR
,
0
);
#endif
}
#endif
...
...
sound/pci/Config.in
View file @
273639ad
...
...
@@ -7,7 +7,7 @@ dep_tristate 'ALi PCI Audio M5451' CONFIG_SND_ALI5451 $CONFIG_SND
dep_tristate 'Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x' CONFIG_SND_CS46XX $CONFIG_SND
dep_mbool ' Cirrus Logic (Sound Fusion) New DSP support (EXPERIMENTAL)' CONFIG_SND_CS46XX_NEW_DSP $CONFIG_SND_CS46XX $CONFIG_EXPERIMENTAL
dep_tristate 'Cirrus Logic (Sound Fusion) CS4281' CONFIG_SND_CS4281 $CONFIG_SND
dep_tristate 'EMU10K1 (SB Live!, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
dep_tristate 'EMU10K1 (SB Live!
& Audigy
, E-mu APS)' CONFIG_SND_EMU10K1 $CONFIG_SND
dep_tristate 'Korg 1212 IO' CONFIG_SND_KORG1212 $CONFIG_SND
dep_tristate 'NeoMagic NM256AV/ZX' CONFIG_SND_NM256 $CONFIG_SND
dep_tristate 'RME Digi32, 32/8, 32 PRO' CONFIG_SND_RME32 $CONFIG_SND
...
...
@@ -25,7 +25,7 @@ dep_tristate 'ESS ES1968/1978 (Maestro-1/2/2E)' CONFIG_SND_ES1968 $CONFIG_SND
dep_tristate 'ESS Allegro/Maestro3' CONFIG_SND_MAESTRO3 $CONFIG_SND
dep_tristate 'ForteMedia FM801' CONFIG_SND_FM801 $CONFIG_SND
dep_tristate 'ICEnsemble ICE1712 (Envy24)' CONFIG_SND_ICE1712 $CONFIG_SND
dep_tristate 'Intel i8
10/i820/i830/i840/MX440 integrated audio
' CONFIG_SND_INTEL8X0 $CONFIG_SND
dep_tristate 'Intel i8
x0/MX440, SiS 7012; Ali 5455; NForce Audio; AMD768/8111
' CONFIG_SND_INTEL8X0 $CONFIG_SND
dep_tristate 'S3 SonicVibes' CONFIG_SND_SONICVIBES $CONFIG_SND
dep_tristate 'VIA 82C686A/B, 8233 South Bridge' CONFIG_SND_VIA82XX $CONFIG_SND
...
...
sound/pci/cs46xx/dsp_spos.c
View file @
273639ad
...
...
@@ -1577,7 +1577,6 @@ int cs46xx_dsp_disable_spdif_out (cs46xx_t *chip)
int
cs46xx_dsp_enable_spdif_in
(
cs46xx_t
*
chip
)
{
dsp_spos_instance_t
*
ins
=
chip
->
dsp_spos_instance
;
unsigned
int
flags
;
/* turn on amplifier */
chip
->
active_ctrl
(
chip
,
1
);
...
...
sound/pci/cs46xx/dsp_spos_scb_lib.c
View file @
273639ad
...
...
@@ -1410,7 +1410,6 @@ int cs46xx_src_link(cs46xx_t *chip,dsp_scb_descriptor_t * src)
{
dsp_spos_instance_t
*
ins
=
chip
->
dsp_spos_instance
;
dsp_scb_descriptor_t
*
parent_scb
;
unsigned
int
flags
;
snd_assert
(
src
->
parent_scb_ptr
==
NULL
,
return
-
EINVAL
);
snd_assert
(
ins
->
master_mix_scb
!=
NULL
,
return
-
EINVAL
);
...
...
sound/sound_core.c
View file @
273639ad
...
...
@@ -122,7 +122,7 @@ static int __sound_insert_unit(struct sound_unit * s, struct sound_unit **list,
* Remove a node from the chain. Called with the lock asserted
*/
static
void
__sound_remove_unit
(
struct
sound_unit
**
list
,
int
unit
)
static
struct
sound_unit
*
__sound_remove_unit
(
struct
sound_unit
**
list
,
int
unit
)
{
while
(
*
list
)
{
...
...
@@ -130,13 +130,12 @@ static void __sound_remove_unit(struct sound_unit **list, int unit)
if
(
p
->
unit_minor
==
unit
)
{
*
list
=
p
->
next
;
devfs_unregister
(
p
->
de
);
kfree
(
p
);
return
;
return
p
;
}
list
=&
(
p
->
next
);
}
printk
(
KERN_ERR
"Sound device %d went missing!
\n
"
,
unit
);
return
NULL
;
}
/*
...
...
@@ -189,9 +188,15 @@ static int sound_insert_unit(struct sound_unit **list, struct file_operations *f
static
void
sound_remove_unit
(
struct
sound_unit
**
list
,
int
unit
)
{
struct
sound_unit
*
p
;
spin_lock
(
&
sound_loader_lock
);
__sound_remove_unit
(
list
,
unit
);
p
=
__sound_remove_unit
(
list
,
unit
);
spin_unlock
(
&
sound_loader_lock
);
if
(
p
)
{
devfs_unregister
(
p
->
de
);
kfree
(
p
);
}
}
/*
...
...
sound/usb/usbaudio.c
View file @
273639ad
...
...
@@ -284,6 +284,16 @@ static int prepare_capture_urb(snd_usb_substream_t *subs,
urb
->
transfer_buffer
=
ctx
->
buf
;
urb
->
transfer_buffer_length
=
offs
;
urb
->
interval
=
1
;
#if 0 // for check
if (! urb->bandwidth) {
int bustime;
bustime = usb_check_bandwidth(urb->dev, urb);
if (bustime < 0)
return bustime;
printk("urb %d: bandwidth = %d (packets = %d)\n", ctx->index, bustime, urb->number_of_packets);
usb_claim_bandwidth(urb->dev, urb, bustime, 1);
}
#endif // for check
return
0
;
}
...
...
@@ -305,8 +315,10 @@ static int retire_capture_urb(snd_usb_substream_t *subs,
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
)
/* active? hmm, skip this */
continue
;
if
(
urb
->
iso_frame_desc
[
i
].
status
)
{
snd_printd
(
KERN_ERR
"frame %d active: %d
\n
"
,
i
,
urb
->
iso_frame_desc
[
i
].
status
);
// continue;
}
len
=
urb
->
iso_frame_desc
[
i
].
actual_length
/
stride
;
if
(
!
len
)
continue
;
...
...
@@ -1009,6 +1021,7 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
}
/* if endpoint has sampling rate control, set it */
if
(
fmt
->
attributes
&
EP_CS_ATTR_SAMPLE_RATE
)
{
int
crate
;
data
[
0
]
=
runtime
->
rate
;
data
[
1
]
=
runtime
->
rate
>>
8
;
data
[
2
]
=
runtime
->
rate
>>
16
;
...
...
@@ -1026,8 +1039,11 @@ static int set_format(snd_usb_substream_t *subs, snd_pcm_runtime_t *runtime)
dev
->
devnum
,
subs
->
interface
,
fmt
->
altsetting
,
ep
);
return
err
;
}
runtime
->
rate
=
data
[
0
]
|
(
data
[
1
]
<<
8
)
|
(
data
[
2
]
<<
16
);
// printk("ok, getting back rate to %d\n", runtime->rate);
crate
=
data
[
0
]
|
(
data
[
1
]
<<
8
)
|
(
data
[
2
]
<<
16
);
if
(
crate
!=
runtime
->
rate
)
{
snd_printd
(
KERN_WARNING
"current rate %d is different from the runtime rate %d
\n
"
,
crate
,
runtime
->
rate
);
// runtime->rate = crate;
}
}
/* always fill max packet size */
if
(
fmt
->
attributes
&
EP_CS_ATTR_FILL_MAX
)
...
...
@@ -1292,14 +1308,14 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
* entry point for linux usb interface
*/
#if
n
def OLD_USB
static
int
usb_audio_probe
(
struct
usb_interface
*
intf
,
#ifdef OLD_USB
static
void
*
usb_audio_probe
(
struct
usb_device
*
dev
,
unsigned
int
ifnum
,
const
struct
usb_device_id
*
id
);
static
void
usb_audio_disconnect
(
struct
usb_
interface
*
intf
);
static
void
usb_audio_disconnect
(
struct
usb_
device
*
dev
,
void
*
ptr
);
#else
static
void
*
usb_audio_probe
(
usb_device
*
dev
,
unsigned
int
ifnum
,
static
int
usb_audio_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
);
static
void
usb_audio_disconnect
(
struct
usb_
device
*
dev
,
void
*
ptr
);
static
void
usb_audio_disconnect
(
struct
usb_
interface
*
intf
);
#endif
static
struct
usb_device_id
usb_audio_ids
[]
=
{
...
...
@@ -1810,7 +1826,8 @@ static int parse_audio_endpoints(snd_usb_audio_t *chip, unsigned char *buffer, i
* parse audio control descriptor and create pcm/midi streams
*/
static
int
snd_usb_create_midi_interface
(
snd_usb_audio_t
*
chip
,
int
ifnum
,
static
int
snd_usb_create_midi_interface
(
snd_usb_audio_t
*
chip
,
struct
usb_interface
*
iface
,
const
snd_usb_audio_quirk_t
*
quirk
);
static
int
snd_usb_create_streams
(
snd_usb_audio_t
*
chip
,
int
ctrlif
,
...
...
@@ -1850,7 +1867,7 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
}
if
(
iface
->
altsetting
[
0
].
bInterfaceClass
==
USB_CLASS_AUDIO
&&
iface
->
altsetting
[
0
].
bInterfaceSubClass
==
USB_SUBCLASS_MIDI_STREAMING
)
{
if
(
snd_usb_create_midi_interface
(
chip
,
j
,
NULL
)
<
0
)
{
if
(
snd_usb_create_midi_interface
(
chip
,
iface
,
NULL
)
<
0
)
{
snd_printk
(
KERN_ERR
"%d:%u:%d: cannot create sequencer device
\n
"
,
dev
->
devnum
,
ctrlif
,
j
);
continue
;
}
...
...
@@ -1871,7 +1888,8 @@ static int snd_usb_create_streams(snd_usb_audio_t *chip, int ctrlif,
return
0
;
}
static
int
snd_usb_create_midi_interface
(
snd_usb_audio_t
*
chip
,
int
ifnum
,
static
int
snd_usb_create_midi_interface
(
snd_usb_audio_t
*
chip
,
struct
usb_interface
*
iface
,
const
snd_usb_audio_quirk_t
*
quirk
)
{
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
...
...
@@ -1888,18 +1906,20 @@ static int snd_usb_create_midi_interface(snd_usb_audio_t *chip, int ifnum,
strcpy
(
seq_device
->
name
,
chip
->
card
->
shortname
);
umidi
=
(
snd_usb_midi_t
*
)
SNDRV_SEQ_DEVICE_ARGPTR
(
seq_device
);
umidi
->
chip
=
chip
;
umidi
->
ifnum
=
ifnum
;
umidi
->
iface
=
iface
;
umidi
->
ifnum
=
iface
->
altsetting
->
bInterfaceNumber
;
umidi
->
quirk
=
quirk
;
umidi
->
seq_client
=
-
1
;
#endif
return
0
;
}
static
inline
int
snd_usb_create_quirk
(
snd_usb_audio_t
*
chip
,
int
ifnum
,
static
inline
int
snd_usb_create_quirk
(
snd_usb_audio_t
*
chip
,
struct
usb_interface
*
iface
,
const
snd_usb_audio_quirk_t
*
quirk
)
{
/* in the future, there may be quirks for PCM devices */
return
snd_usb_create_midi_interface
(
chip
,
if
num
,
quirk
);
return
snd_usb_create_midi_interface
(
chip
,
if
ace
,
quirk
);
}
...
...
@@ -2050,27 +2070,18 @@ static int alloc_desc_buffer(struct usb_device *dev, int index, unsigned char **
* only at the first time. the successive calls of this function will
* append the pcm interface to the corresponding card.
*/
#ifndef OLD_USB
static
int
usb_audio_probe
(
struct
usb_interface
*
intf
,
static
void
*
snd_usb_audio_probe
(
struct
usb_device
*
dev
,
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
#else
static
void
*
usb_audio_probe
(
struct
usb_device
*
dev
,
unsigned
int
ifnum
,
const
struct
usb_device_id
*
id
)
#endif
{
#ifndef OLD_USB
struct
usb_device
*
dev
=
interface_to_usbdev
(
intf
);
int
ifnum
=
intf
->
altsetting
->
bInterfaceNumber
;
#endif
struct
usb_config_descriptor
*
config
=
dev
->
actconfig
;
const
snd_usb_audio_quirk_t
*
quirk
=
(
const
snd_usb_audio_quirk_t
*
)
id
->
driver_info
;
unsigned
char
*
buffer
;
unsigned
int
index
;
int
i
,
buflen
;
int
i
;
snd_card_t
*
card
;
snd_usb_audio_t
*
chip
;
int
ifnum
=
intf
->
altsetting
->
bInterfaceNumber
;
if
(
quirk
&&
ifnum
!=
quirk
->
ifnum
)
if
(
quirk
&&
quirk
->
ifnum
!=
QUIRK_ANY_INTERFACE
&&
ifnum
!=
quirk
->
ifnum
)
goto
__err_val
;
if
(
usb_set_configuration
(
dev
,
config
->
bConfigurationValue
)
<
0
)
{
...
...
@@ -2078,11 +2089,6 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
goto
__err_val
;
}
index
=
dev
->
actconfig
-
config
;
buflen
=
alloc_desc_buffer
(
dev
,
index
,
&
buffer
);
if
(
buflen
<=
0
)
goto
__err_val
;
/*
* found a config. now register to ALSA
*/
...
...
@@ -2124,12 +2130,24 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
}
if
(
!
quirk
)
{
if
(
snd_usb_create_streams
(
chip
,
ifnum
,
buffer
,
buflen
)
<
0
)
/* USB audio interface */
unsigned
char
*
buffer
;
unsigned
int
index
;
int
buflen
;
index
=
dev
->
actconfig
-
config
;
buflen
=
alloc_desc_buffer
(
dev
,
index
,
&
buffer
);
if
(
buflen
<=
0
)
goto
__error
;
if
(
snd_usb_create_mixer
(
chip
,
ifnum
,
buffer
,
buflen
)
<
0
)
if
(
snd_usb_create_streams
(
chip
,
ifnum
,
buffer
,
buflen
)
<
0
||
snd_usb_create_mixer
(
chip
,
ifnum
,
buffer
,
buflen
)
<
0
)
{
kfree
(
buffer
);
goto
__error
;
}
kfree
(
buffer
);
}
else
{
if
(
snd_usb_create_quirk
(
chip
,
ifnum
,
quirk
)
<
0
)
/* USB midi interface */
if
(
snd_usb_create_quirk
(
chip
,
intf
,
quirk
)
<
0
)
goto
__error
;
}
...
...
@@ -2142,38 +2160,20 @@ static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum,
chip
->
num_interfaces
++
;
up
(
&
register_mutex
);
kfree
(
buffer
);
#ifndef OLD_USB
return
0
;
#else
return
chip
;
#endif
__error:
up
(
&
register_mutex
);
kfree
(
buffer
);
__err_val:
#ifndef OLD_USB
return
-
EIO
;
#else
return
NULL
;
#endif
}
/*
* we need to take care of counter, since disconnection can be called also
* many times as well as usb_audio_probe().
*/
#ifndef OLD_USB
static
void
usb_audio_disconnect
(
struct
usb_interface
*
intf
)
#else
static
void
usb_audio_disconnect
(
struct
usb_device
*
dev
,
void
*
ptr
)
#endif
static
void
snd_usb_audio_disconnect
(
struct
usb_device
*
dev
,
void
*
ptr
)
{
#ifndef OLD_USB
void
*
ptr
=
dev_get_drvdata
(
&
intf
->
dev
);
#endif
snd_usb_audio_t
*
chip
;
if
(
ptr
==
(
void
*
)
-
1
)
...
...
@@ -2185,6 +2185,49 @@ static void usb_audio_disconnect(struct usb_device *dev, void *ptr)
snd_card_free
(
chip
->
card
);
}
#ifdef OLD_USB
/*
* 2.4 USB kernel API
*/
static
void
*
usb_audio_probe
(
struct
usb_device
*
dev
,
unsigned
int
ifnum
,
const
struct
usb_device_id
*
id
)
{
return
snd_usb_audio_probe
(
dev
,
usb_ifnum_to_if
(
dev
,
ifnum
),
id
);
}
static
void
usb_audio_disconnect
(
struct
usb_device
*
dev
,
void
*
ptr
)
{
snd_usb_audio_disconnect
(
dev
,
ptr
);
}
#else
/*
* new 2.5 USB kernel API
*/
static
int
usb_audio_probe
(
struct
usb_interface
*
intf
,
const
struct
usb_device_id
*
id
)
{
void
*
chip
;
chip
=
snd_usb_audio_probe
(
interface_to_usbdev
(
intf
),
intf
,
id
);
if
(
chip
)
{
dev_set_drvdata
(
&
intf
->
dev
,
chip
);
return
0
;
}
else
return
-
EIO
;
}
static
void
usb_audio_disconnect
(
struct
usb_interface
*
intf
)
{
snd_usb_audio_disconnect
(
interface_to_usbdev
(
intf
),
dev_get_drvdata
(
&
intf
->
dev
));
}
#endif
static
int
__init
snd_usb_audio_init
(
void
)
{
usb_register
(
&
usb_audio_driver
);
...
...
sound/usb/usbaudio.h
View file @
273639ad
...
...
@@ -58,6 +58,8 @@
#define EP_GENERAL 0x01
#define MS_GENERAL 0x01
#define MIDI_IN_JACK 0x02
#define MIDI_OUT_JACK 0x03
/* endpoint attributes */
#define EP_ATTR_MASK 0x0c
...
...
@@ -146,22 +148,34 @@ struct snd_usb_audio {
/*
* Information about devices with broken descriptors
*/
#define QUIRK_ANY_INTERFACE -1
#define QUIRK_MIDI_FIXED_ENDPOINT 0
#define QUIRK_MIDI_YAMAHA 1
#define QUIRK_MIDI_MIDIMAN 2
typedef
struct
snd_usb_audio_quirk
snd_usb_audio_quirk_t
;
typedef
struct
snd_usb_midi_endpoint_info
snd_usb_midi_endpoint_info_t
;
struct
snd_usb_audio_quirk
{
const
char
*
vendor_name
;
const
char
*
product_name
;
int
ifnum
;
int16_t
ifnum
;
int16_t
type
;
const
void
*
data
;
};
/* MIDI specific
*/
struct
snd_usb_midi_endpoint_info
{
/* data for QUIRK_MIDI_FIXED_ENDPOINT
*/
struct
snd_usb_midi_endpoint_info
{
int16_t
epnum
;
/* ep number, -1 autodetect */
uint16_t
out_cables
;
/* bitmask */
uint16_t
in_cables
;
/* bitmask */
}
endpoints
[
MIDI_MAX_ENDPOINTS
];
};
/* for QUIRK_MIDI_YAMAHA, data is NULL */
/* for QUIRK_MIDI_MIDIMAN, data is the number of ports */
/*
* USB MIDI sequencer device data
*/
...
...
@@ -173,6 +187,7 @@ typedef struct snd_usb_midi_in_endpoint snd_usb_midi_in_endpoint_t;
struct
snd_usb_midi
{
/* filled by usbaudio.c */
snd_usb_audio_t
*
chip
;
struct
usb_interface
*
iface
;
int
ifnum
;
const
snd_usb_audio_quirk_t
*
quirk
;
...
...
sound/usb/usbmidi.c
View file @
273639ad
...
...
@@ -4,6 +4,10 @@
* Copyright (c) 2002 Clemens Ladisch
* All rights reserved.
*
* Based on the OSS usb-midi driver by NAGANO Daisuke,
* NetBSD's umidi driver by Takuya SHIOZAKI,
* the "USB Device Class Definition for MIDI Devices" by Roland
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
...
...
@@ -120,6 +124,10 @@ struct snd_usb_midi_in_endpoint {
static
void
snd_usbmidi_do_output
(
snd_usb_midi_out_endpoint_t
*
ep
);
static
const
uint8_t
snd_usbmidi_cin_length
[]
=
{
0
,
0
,
2
,
3
,
3
,
1
,
2
,
3
,
3
,
3
,
3
,
3
,
2
,
2
,
3
,
1
};
/*
* Submits the URB, with error handling.
*/
...
...
@@ -152,9 +160,6 @@ static int snd_usbmidi_urb_error(int status)
static
void
snd_usbmidi_input_packet
(
snd_usb_midi_in_endpoint_t
*
ep
,
uint8_t
packet
[
4
])
{
static
const
uint8_t
cin_length
[]
=
{
0
,
0
,
2
,
3
,
3
,
1
,
2
,
3
,
3
,
3
,
3
,
3
,
2
,
2
,
3
,
1
};
int
cable
=
packet
[
0
]
>>
4
;
usbmidi_in_port_t
*
port
=
&
ep
->
ports
[
cable
];
snd_seq_event_t
ev
;
...
...
@@ -163,7 +168,7 @@ static void snd_usbmidi_input_packet(snd_usb_midi_in_endpoint_t* ep,
return
;
memset
(
&
ev
,
0
,
sizeof
(
ev
));
if
(
snd_midi_event_encode
(
port
->
midi_event
,
&
packet
[
1
],
cin_length
[
packet
[
0
]
&
0x0f
],
&
ev
)
>
0
snd_usbmidi_
cin_length
[
packet
[
0
]
&
0x0f
],
&
ev
)
>
0
&&
ev
.
type
!=
SNDRV_SEQ_EVENT_NONE
)
{
ev
.
source
.
port
=
port
->
seq_port
;
ev
.
dest
.
client
=
SNDRV_SEQ_ADDRESS_SUBSCRIBERS
;
...
...
@@ -199,6 +204,38 @@ static void snd_usbmidi_in_urb_complete(struct urb* urb)
}
}
/*
* Converts the data read from a Midiman device to standard USB MIDI packets.
*/
static
void
snd_usbmidi_in_midiman_complete
(
struct
urb
*
urb
)
{
if
(
urb
->
status
==
0
)
{
uint8_t
*
buffer
=
(
uint8_t
*
)
urb
->
transfer_buffer
;
int
i
;
for
(
i
=
0
;
i
+
4
<=
urb
->
actual_length
;
i
+=
4
)
{
if
(
buffer
[
i
+
3
]
!=
0
)
{
/*
* snd_usbmidi_input_packet() doesn't check the
* contents of the message, so we simply use
* some random CIN with the desired length.
*/
static
const
uint8_t
cin
[
4
]
=
{
0x0
,
0xf
,
0x2
,
0x3
};
uint8_t
ctl
=
buffer
[
i
+
3
];
buffer
[
i
+
3
]
=
buffer
[
i
+
2
];
buffer
[
i
+
2
]
=
buffer
[
i
+
1
];
buffer
[
i
+
1
]
=
buffer
[
i
+
0
];
buffer
[
i
+
0
]
=
(
ctl
&
0xf0
)
|
cin
[
ctl
&
3
];
}
else
{
buffer
[
i
+
0
]
=
0
;
}
}
}
snd_usbmidi_in_urb_complete
(
urb
);
}
static
void
snd_usbmidi_out_urb_complete
(
struct
urb
*
urb
)
{
snd_usb_midi_out_endpoint_t
*
ep
=
snd_magic_cast
(
snd_usb_midi_out_endpoint_t
,
urb
->
context
,
return
);
...
...
@@ -213,6 +250,23 @@ static void snd_usbmidi_out_urb_complete(struct urb* urb)
spin_unlock_irqrestore
(
&
ep
->
buffer_lock
,
flags
);
}
/*
* Converts standard USB MIDI packets to what Midman devices expect.
*/
static
void
snd_usbmidi_convert_to_midiman
(
struct
urb
*
urb
)
{
uint8_t
*
buffer
=
(
uint8_t
*
)
urb
->
transfer_buffer
;
int
i
;
for
(
i
=
0
;
i
+
4
<=
urb
->
transfer_buffer_length
;
i
+=
4
)
{
uint8_t
cin
=
buffer
[
i
];
buffer
[
i
+
0
]
=
buffer
[
i
+
1
];
buffer
[
i
+
1
]
=
buffer
[
i
+
2
];
buffer
[
i
+
2
]
=
buffer
[
i
+
3
];
buffer
[
i
+
3
]
=
(
cin
&
0xf0
)
|
snd_usbmidi_cin_length
[
cin
&
0x0f
];
}
}
/*
* This is called when some data should be transferred to the device
* (after the reception of one or more sequencer events, or after completion
...
...
@@ -255,6 +309,9 @@ static void snd_usbmidi_do_output(snd_usb_midi_out_endpoint_t* ep)
}
if
(
len
>
0
)
{
if
(
ep
->
umidi
->
quirk
&&
ep
->
umidi
->
quirk
->
type
==
QUIRK_MIDI_MIDIMAN
)
snd_usbmidi_convert_to_midiman
(
ep
->
urb
);
ep
->
urb
->
dev
=
ep
->
umidi
->
chip
->
dev
;
snd_usbmidi_submit_urb
(
ep
->
urb
,
GFP_ATOMIC
);
}
...
...
@@ -479,22 +536,6 @@ static void snd_usbmidi_in_endpoint_delete(snd_usb_midi_in_endpoint_t* ep)
snd_magic_kfree
(
ep
);
}
#ifndef OLD_USB
/* this code is not exported from USB core anymore */
struct
usb_interface
*
local_usb_ifnum_to_if
(
struct
usb_device
*
dev
,
unsigned
ifnum
)
{
int
i
;
for
(
i
=
0
;
i
<
dev
->
actconfig
->
bNumInterfaces
;
i
++
)
if
(
dev
->
actconfig
->
interface
[
i
].
altsetting
[
0
].
bInterfaceNumber
==
ifnum
)
return
&
dev
->
actconfig
->
interface
[
i
];
return
NULL
;
}
#else
#define local_usb_ifnum_to_if usb_ifnum_to_if
#endif
/*
* For Roland devices, use the alternate setting which uses interrupt
* transfers for input.
...
...
@@ -507,7 +548,7 @@ static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
if
(
umidi
->
chip
->
dev
->
descriptor
.
idVendor
!=
0x0582
)
return
NULL
;
intf
=
local_usb_ifnum_to_if
(
umidi
->
chip
->
dev
,
umidi
->
ifnum
)
;
intf
=
umidi
->
iface
;
if
(
!
intf
||
intf
->
num_altsetting
!=
2
)
return
NULL
;
...
...
@@ -528,6 +569,14 @@ static usb_endpoint_descriptor_t* snd_usbmidi_get_int_epd(snd_usb_midi_t* umidi,
return
&
intfd
->
endpoint
[
1
];
}
static
usb_endpoint_descriptor_t
*
snd_usbmidi_get_midiman_int_epd
(
snd_usb_midi_t
*
umidi
)
{
usb_interface_t
*
intf
=
umidi
->
iface
;
if
(
!
intf
)
return
NULL
;
return
&
intf
->
altsetting
[
0
].
endpoint
[
0
];
}
/*
* Creates an input endpoint, and initalizes input ports.
* ALSA ports are created later.
...
...
@@ -551,6 +600,9 @@ static int snd_usbmidi_in_endpoint_create(snd_usb_midi_t* umidi,
for
(
i
=
0
;
i
<
0x10
;
++
i
)
ep
->
ports
[
i
].
seq_port
=
-
1
;
if
(
umidi
->
quirk
&&
umidi
->
quirk
->
type
==
QUIRK_MIDI_MIDIMAN
)
int_epd
=
snd_usbmidi_get_midiman_int_epd
(
umidi
);
else
int_epd
=
snd_usbmidi_get_int_epd
(
umidi
,
ep_info
->
epnum
);
ep
->
urb
=
usb_alloc_urb
(
0
,
GFP_KERNEL
);
...
...
@@ -697,63 +749,63 @@ static int snd_usbmidi_seq_device_delete(snd_seq_device_t* seq_device)
}
/*
* After input and output endpoints have been initialized, create
* the ALSA port for each input/output port pair in the endpoint.
* *port_idx is the port number, which must be unique over all endpoints.
* Creates a sequencer port for an input/output cable pair.
*/
static
int
snd_usbmidi_create_endpoint_ports
(
snd_usb_midi_t
*
umidi
,
int
ep
,
int
*
port_idx
,
snd_usb_midi_endpoint_info_t
*
ep_info
)
static
int
snd_usbmidi_create_port
(
snd_usb_midi_t
*
umidi
,
snd_usb_midi_out_endpoint_t
*
out_ep
,
snd_usb_midi_in_endpoint_t
*
in_ep
,
int
cable
,
int
port_idx
)
{
int
c
,
err
;
int
cap
,
type
,
port
;
int
out
,
in
;
snd_seq_port_callback_t
port_callback
;
char
port_name
[
48
];
for
(
c
=
0
;
c
<
0x10
;
++
c
)
{
out
=
ep_info
->
out_cables
&
(
1
<<
c
);
in
=
ep_info
->
in_cables
&
(
1
<<
c
);
if
(
!
(
in
||
out
))
continue
;
cap
=
0
;
memset
(
&
port_callback
,
0
,
sizeof
(
port_callback
));
port_callback
.
owner
=
THIS_MODULE
;
if
(
out
)
{
if
(
out_ep
)
{
port_callback
.
event_input
=
snd_usbmidi_event_input
;
port_callback
.
private_data
=
&
umidi
->
endpoints
[
ep
].
out
->
ports
[
c
];
cap
|=
SNDRV_SEQ_PORT_CAP_WRITE
|
SNDRV_SEQ_PORT_CAP_SUBS_WRITE
;
port_callback
.
private_data
=
&
out_ep
->
ports
[
cable
];
cap
|=
SNDRV_SEQ_PORT_CAP_WRITE
|
SNDRV_SEQ_PORT_CAP_SUBS_WRITE
;
}
if
(
in
)
{
cap
|=
SNDRV_SEQ_PORT_CAP_READ
|
SNDRV_SEQ_PORT_CAP_SUBS_READ
;
if
(
in_ep
)
{
cap
|=
SNDRV_SEQ_PORT_CAP_READ
|
SNDRV_SEQ_PORT_CAP_SUBS_READ
;
}
if
(
out
&&
in
)
{
if
(
out_ep
&&
in_ep
)
{
cap
|=
SNDRV_SEQ_PORT_CAP_DUPLEX
;
}
/* TODO: read type bits from element descriptor */
type
=
SNDRV_SEQ_PORT_TYPE_MIDI_GENERIC
;
/* TODO: read port name from jack descriptor */
snprintf
(
port_name
,
sizeof
(
port_name
),
"%s Port %d"
,
umidi
->
chip
->
card
->
shortname
,
*
port_idx
);
port
=
snd_seq_event_port_attach
(
umidi
->
seq_client
,
&
port_callback
,
umidi
->
chip
->
card
->
shortname
,
port_idx
);
port
=
snd_seq_event_port_attach
(
umidi
->
seq_client
,
&
port_callback
,
cap
,
type
,
port_name
);
if
(
port
<
0
)
{
snd_printk
(
KERN_ERR
"cannot create port (error code %d)
\n
"
,
port
);
return
port
;
}
if
(
in
)
umidi
->
endpoints
[
ep
].
in
->
ports
[
c
].
seq_port
=
port
;
if
(
in_ep
)
in_ep
->
ports
[
cable
].
seq_port
=
port
;
return
port
;
}
if
(
*
port_idx
<
SNDRV_MINOR_RAWMIDIS
)
{
/*
* Creates a virmidi port emulating rawmidi for the sequencer port.
*/
static
int
snd_usbmidi_create_virmidi
(
snd_usb_midi_t
*
umidi
,
int
port
,
int
port_idx
,
snd_rawmidi_t
**
rrmidi
)
{
snd_rawmidi_t
*
rmidi
;
snd_virmidi_dev_t
*
rdev
;
err
=
snd_virmidi_new
(
umidi
->
chip
->
card
,
*
port_idx
,
&
rmidi
);
int
err
;
*
rrmidi
=
NULL
;
err
=
snd_virmidi_new
(
umidi
->
chip
->
card
,
port_idx
,
&
rmidi
);
if
(
err
<
0
)
return
err
;
sprintf
(
rmidi
->
name
,
"%s MIDI %d"
,
umidi
->
chip
->
card
->
shortname
,
port_idx
);
rdev
=
snd_magic_cast
(
snd_virmidi_dev_t
,
rmidi
->
private_data
,
return
-
ENXIO
);
strcpy
(
rmidi
->
name
,
port_name
);
rdev
->
seq_mode
=
SNDRV_VIRMIDI_SEQ_ATTACH
;
rdev
->
client
=
umidi
->
seq_client
;
rdev
->
port
=
port
;
...
...
@@ -762,7 +814,41 @@ static int snd_usbmidi_create_endpoint_ports(snd_usb_midi_t* umidi, int ep, int*
snd_device_free
(
umidi
->
chip
->
card
,
rmidi
);
return
err
;
}
umidi
->
endpoints
[
ep
].
rmidi
[
c
]
=
rmidi
;
*
rrmidi
=
rmidi
;
return
0
;
}
/*
* After input and output endpoints have been initialized, create
* the ALSA port for each input/output port pair in the endpoint.
* *port_idx is the port number, which must be unique over all endpoints.
*/
static
int
snd_usbmidi_create_endpoint_ports
(
snd_usb_midi_t
*
umidi
,
snd_usb_midi_endpoint_t
*
endpoint
,
int
*
port_idx
,
snd_usb_midi_endpoint_info_t
*
ep_info
)
{
int
cable
;
for
(
cable
=
0
;
cable
<
0x10
;
++
cable
)
{
int
port
,
err
;
int
out
=
ep_info
->
out_cables
&
(
1
<<
cable
);
int
in
=
ep_info
->
in_cables
&
(
1
<<
cable
);
if
(
!
(
in
||
out
))
continue
;
port
=
snd_usbmidi_create_port
(
umidi
,
out
?
endpoint
->
out
:
NULL
,
in
?
endpoint
->
in
:
NULL
,
cable
,
*
port_idx
);
if
(
port
<
0
)
return
port
;
if
(
*
port_idx
<
SNDRV_MINOR_RAWMIDIS
)
{
err
=
snd_usbmidi_create_virmidi
(
umidi
,
port
,
*
port_idx
,
&
endpoint
->
rmidi
[
cable
]);
if
(
err
<
0
)
return
err
;
}
++*
port_idx
;
}
...
...
@@ -792,8 +878,8 @@ static int snd_usbmidi_create_endpoints(snd_usb_midi_t* umidi,
if
(
err
<
0
)
return
err
;
}
err
=
snd_usbmidi_create_endpoint_ports
(
umidi
,
i
,
&
port_idx
,
&
endpoints
[
i
]);
err
=
snd_usbmidi_create_endpoint_ports
(
umidi
,
&
umidi
->
endpoints
[
i
]
,
&
port_idx
,
&
endpoints
[
i
]);
if
(
err
<
0
)
return
err
;
printk
(
KERN_INFO
"snd-usb-midi: endpoint %d: created %d output and %d input ports
\n
"
,
...
...
@@ -817,9 +903,7 @@ static int snd_usbmidi_get_ms_info(snd_usb_midi_t* umidi,
usb_ms_endpoint_descriptor_t
*
ms_ep
;
int
i
,
epidx
;
memset
(
endpoints
,
0
,
sizeof
(
*
endpoints
)
*
MIDI_MAX_ENDPOINTS
);
intf
=
local_usb_ifnum_to_if
(
umidi
->
chip
->
dev
,
umidi
->
ifnum
);
intf
=
umidi
->
iface
;
if
(
!
intf
)
return
-
ENXIO
;
intfd
=
&
intf
->
altsetting
[
0
];
...
...
@@ -878,7 +962,7 @@ static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi,
usb_endpoint_descriptor_t
*
epd
;
if
(
endpoint
->
epnum
==
-
1
)
{
intf
=
local_usb_ifnum_to_if
(
umidi
->
chip
->
dev
,
umidi
->
ifnum
)
;
intf
=
umidi
->
iface
;
if
(
!
intf
||
intf
->
num_altsetting
<
1
)
return
-
ENOENT
;
intfd
=
intf
->
altsetting
;
...
...
@@ -890,6 +974,120 @@ static int snd_usbmidi_detect_endpoint(snd_usb_midi_t* umidi,
return
0
;
}
/*
* Detects the endpoints and ports of Yamaha devices.
*/
static
int
snd_usbmidi_detect_yamaha
(
snd_usb_midi_t
*
umidi
,
snd_usb_midi_endpoint_info_t
*
endpoint
)
{
usb_interface_t
*
intf
;
usb_interface_descriptor_t
*
intfd
;
uint8_t
*
cs_desc
;
intf
=
umidi
->
iface
;
if
(
!
intf
)
return
-
ENOENT
;
intfd
=
intf
->
altsetting
;
if
(
intfd
->
bNumEndpoints
<
1
)
return
-
ENOENT
;
for
(
cs_desc
=
intfd
->
extra
;
cs_desc
<
intfd
->
extra
+
intfd
->
extralen
&&
cs_desc
[
0
]
>=
2
;
cs_desc
+=
cs_desc
[
0
])
{
if
(
cs_desc
[
1
]
==
CS_AUDIO_INTERFACE
)
{
if
(
cs_desc
[
2
]
==
MIDI_IN_JACK
)
endpoint
->
in_cables
=
(
endpoint
->
in_cables
<<
1
)
|
1
;
else
if
(
cs_desc
[
2
]
==
MIDI_OUT_JACK
)
endpoint
->
out_cables
=
(
endpoint
->
out_cables
<<
1
)
|
1
;
}
}
if
(
!
endpoint
->
in_cables
&&
!
endpoint
->
out_cables
)
return
-
ENOENT
;
endpoint
->
epnum
=
-
1
;
return
snd_usbmidi_detect_endpoint
(
umidi
,
endpoint
);
}
/*
* Creates the endpoints and their ports for Midiman devices.
*/
static
int
snd_usbmidi_create_endpoints_midiman
(
snd_usb_midi_t
*
umidi
,
int
ports
)
{
snd_usb_midi_endpoint_info_t
ep_info
;
usb_interface_t
*
intf
;
usb_interface_descriptor_t
*
intfd
;
usb_endpoint_descriptor_t
*
epd
;
int
cable
,
err
;
intf
=
umidi
->
iface
;
if
(
!
intf
)
return
-
ENOENT
;
intfd
=
intf
->
altsetting
;
if
(
intfd
->
bNumEndpoints
<
(
ports
>
1
?
5
:
3
))
{
snd_printdd
(
KERN_ERR
"not enough endpoints
\n
"
);
return
-
ENOENT
;
}
epd
=
&
intfd
->
endpoint
[
0
];
if
((
epd
->
bEndpointAddress
&
USB_ENDPOINT_DIR_MASK
)
!=
USB_DIR_IN
||
(
epd
->
bmAttributes
&
USB_ENDPOINT_XFERTYPE_MASK
)
!=
USB_ENDPOINT_XFER_INT
)
{
snd_printdd
(
KERN_ERR
"endpoint[0] isn't interrupt
\n
"
);
return
-
ENXIO
;
}
epd
=
&
intfd
->
endpoint
[
2
];
if
((
epd
->
bEndpointAddress
&
USB_ENDPOINT_DIR_MASK
)
!=
USB_DIR_OUT
||
(
epd
->
bmAttributes
&
USB_ENDPOINT_XFERTYPE_MASK
)
!=
USB_ENDPOINT_XFER_BULK
)
{
snd_printdd
(
KERN_ERR
"endpoint[2] isn't bulk output
\n
"
);
return
-
ENXIO
;
}
if
(
ports
>
1
)
{
epd
=
&
intfd
->
endpoint
[
4
];
if
((
epd
->
bEndpointAddress
&
USB_ENDPOINT_DIR_MASK
)
!=
USB_DIR_OUT
||
(
epd
->
bmAttributes
&
USB_ENDPOINT_XFERTYPE_MASK
)
!=
USB_ENDPOINT_XFER_BULK
)
{
snd_printdd
(
KERN_ERR
"endpoint[4] isn't bulk output
\n
"
);
return
-
ENXIO
;
}
}
ep_info
.
epnum
=
intfd
->
endpoint
[
2
].
bEndpointAddress
&
USB_ENDPOINT_NUMBER_MASK
;
ep_info
.
out_cables
=
0x5555
&
((
1
<<
ports
)
-
1
);
err
=
snd_usbmidi_out_endpoint_create
(
umidi
,
&
ep_info
,
&
umidi
->
endpoints
[
0
]);
if
(
err
<
0
)
return
err
;
ep_info
.
epnum
=
intfd
->
endpoint
[
0
].
bEndpointAddress
&
USB_ENDPOINT_NUMBER_MASK
;
ep_info
.
in_cables
=
(
1
<<
ports
)
-
1
;
err
=
snd_usbmidi_in_endpoint_create
(
umidi
,
&
ep_info
,
&
umidi
->
endpoints
[
0
]);
if
(
err
<
0
)
return
err
;
umidi
->
endpoints
[
0
].
in
->
urb
->
complete
=
snd_usbmidi_in_midiman_complete
;
if
(
ports
>
1
)
{
ep_info
.
epnum
=
intfd
->
endpoint
[
4
].
bEndpointAddress
&
USB_ENDPOINT_NUMBER_MASK
;
ep_info
.
out_cables
=
0xaaaa
&
((
1
<<
ports
)
-
1
);
err
=
snd_usbmidi_out_endpoint_create
(
umidi
,
&
ep_info
,
&
umidi
->
endpoints
[
1
]);
if
(
err
<
0
)
return
err
;
}
for
(
cable
=
0
;
cable
<
ports
;
++
cable
)
{
int
port
=
snd_usbmidi_create_port
(
umidi
,
umidi
->
endpoints
[
cable
&
1
].
out
,
umidi
->
endpoints
[
0
].
in
,
cable
,
cable
);
if
(
port
<
0
)
return
port
;
if
(
cable
<
SNDRV_MINOR_RAWMIDIS
)
{
int
err
=
snd_usbmidi_create_virmidi
(
umidi
,
port
,
cable
,
&
umidi
->
endpoints
[
0
].
rmidi
[
cable
]);
if
(
err
<
0
)
return
err
;
}
}
return
0
;
}
/*
* Initialize the sequencer device.
*/
...
...
@@ -912,6 +1110,7 @@ static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
if
(
umidi
->
seq_client
<
0
)
return
umidi
->
seq_client
;
/* set the client name */
memset
(
&
client_info
,
0
,
sizeof
(
client_info
));
client_info
.
client
=
umidi
->
seq_client
;
client_info
.
type
=
KERNEL_CLIENT
;
...
...
@@ -935,17 +1134,37 @@ static int snd_usbmidi_seq_device_new(snd_seq_device_t* seq_device)
SNDRV_SEQ_IOCTL_SET_CLIENT_INFO
,
&
client_info
);
if
(
umidi
->
quirk
)
{
memcpy
(
endpoints
,
umidi
->
quirk
->
endpoints
,
sizeof
(
endpoints
));
err
=
snd_usbmidi_detect_endpoint
(
umidi
,
&
endpoints
[
0
]);
}
else
{
/* detect the endpoint(s) to use */
memset
(
endpoints
,
0
,
sizeof
(
endpoints
));
if
(
!
umidi
->
quirk
)
{
err
=
snd_usbmidi_get_ms_info
(
umidi
,
endpoints
);
}
else
{
switch
(
umidi
->
quirk
->
type
)
{
case
QUIRK_MIDI_FIXED_ENDPOINT
:
memcpy
(
&
endpoints
[
0
],
umidi
->
quirk
->
data
,
sizeof
(
snd_usb_midi_endpoint_info_t
));
err
=
snd_usbmidi_detect_endpoint
(
umidi
,
&
endpoints
[
0
]);
break
;
case
QUIRK_MIDI_YAMAHA
:
err
=
snd_usbmidi_detect_yamaha
(
umidi
,
&
endpoints
[
0
]);
break
;
case
QUIRK_MIDI_MIDIMAN
:
err
=
0
;
break
;
default:
snd_printd
(
KERN_ERR
"invalid quirk type %d
\n
"
,
umidi
->
quirk
->
type
);
err
=
-
ENXIO
;
break
;
}
if
(
err
<
0
)
{
snd_usbmidi_seq_device_delete
(
seq_device
);
return
err
;
}
/* create ports */
if
(
err
>=
0
)
{
if
(
umidi
->
quirk
&&
umidi
->
quirk
->
type
==
QUIRK_MIDI_MIDIMAN
)
err
=
snd_usbmidi_create_endpoints_midiman
(
umidi
,
(
int
)
umidi
->
quirk
->
data
);
else
err
=
snd_usbmidi_create_endpoints
(
umidi
,
endpoints
);
}
if
(
err
<
0
)
{
snd_usbmidi_seq_device_delete
(
seq_device
);
return
err
;
...
...
sound/usb/usbquirks.h
View file @
273639ad
...
...
@@ -26,45 +26,143 @@
* In a perfect world, this file would be empty.
*/
#define USB_DEVICE_VENDOR_SPEC(vend, prod) \
.match_flags = USB_DEVICE_ID_MATCH_VENDOR | \
USB_DEVICE_ID_MATCH_PRODUCT | \
USB_DEVICE_ID_MATCH_INT_CLASS, \
.idVendor = vend, \
.idProduct = prod, \
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
#if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
/* Yamaha devices */
{
/* from NetBSD's umidi driver */
USB_DEVICE
(
0x0499
,
0x1000
),
/* Yamaha UX256 */
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1000
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
ifnum
=
0
,
.
endpoints
=
{
{
.
epnum
=
-
1
,
.
out_cables
=
0xffff
,
.
in_cables
=
0x00ff
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"UX256"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1001
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MU1000"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1002
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MU2000"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
/* from Nagano Daisuke's usb-midi driver */
USB_DEVICE
(
0x0499
,
0x1001
),
/* Yamaha MU1000 */
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1003
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
ifnum
=
0
,
.
endpoints
=
{
{
.
epnum
=
1
,
.
out_cables
=
0x000f
,
.
in_cables
=
0x0001
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MU500"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1004
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"UW500"
,
.
ifnum
=
3
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1005
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MOTIF6"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1006
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MOTIF7"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1007
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"MOTIF8"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1008
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"UX96"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1009
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"UX16"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x100a
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"EOS BX"
,
.
ifnum
=
3
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x100e
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"S08"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x100f
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"CLP-150"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0499
,
0x1010
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Yamaha"
,
.
product_name
=
"CLP-170"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_YAMAHA
}
},
/*
* I don't know whether the following Yamaha devices need entries or not:
* 0x1002 MU2000 0x1008 UX96
* 0x1003 MU500 0x1009 UX16
* 0x1004 UW500 0x100e S08
* 0x1005 MOTIF6 0x100f CLP-150
* 0x1006 MOTIF7 0x1010 CLP-170
* 0x1007 MOTIF8
*/
/*
* Once upon a time people thought, "Wouldn't it be nice if there was a
...
...
@@ -79,259 +177,316 @@
* class-specific descriptors.
*/
{
USB_DEVICE
(
0x0582
,
0x0000
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0000
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"UA-100"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0007
,
.
in_cables
=
0x0007
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0002
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0002
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UM-4"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x000f
,
.
in_cables
=
0x000f
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0003
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0003
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"SC-8850"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x003f
,
.
in_cables
=
0x003f
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0004
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0004
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"U-8"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0003
,
.
in_cables
=
0x0003
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0005
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0005
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UM-2"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0003
,
.
in_cables
=
0x0003
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0007
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0007
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"SC-8820"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0013
,
.
in_cables
=
0x0013
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0008
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0008
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"PC-300"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0001
,
.
in_cables
=
0x0001
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0009
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0009
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UM-1"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0001
,
.
in_cables
=
0x0001
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x000b
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x000b
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"SK-500"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0013
,
.
in_cables
=
0x0013
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x000c
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x000c
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"SC-D70"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0007
,
.
in_cables
=
0x0007
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0012
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0012
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"Roland"
,
.
product_name
=
"XV-5050"
,
.
ifnum
=
0
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0001
,
.
in_cables
=
0x0001
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0014
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0014
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UM-880"
,
.
ifnum
=
0
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x01ff
,
.
in_cables
=
0x01ff
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0016
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0016
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"SD-90"
,
.
ifnum
=
2
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x000f
,
.
in_cables
=
0x000f
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0023
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0023
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UM-550"
,
.
ifnum
=
0
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x003f
,
.
in_cables
=
0x003f
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0027
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0027
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"SD-20"
,
.
ifnum
=
0
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0003
,
.
in_cables
=
0x0007
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x0029
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x0029
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"SD-80"
,
.
ifnum
=
0
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x000f
,
.
in_cables
=
0x000f
}
}
}
},
{
USB_DEVICE
(
0x0582
,
0x002b
),
USB_DEVICE
_VENDOR_SPEC
(
0x0582
,
0x002b
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"EDIROL"
,
.
product_name
=
"UA-700"
,
.
ifnum
=
3
,
.
endpoints
=
{
{
.
type
=
QUIRK_MIDI_FIXED_ENDPOINT
,
.
data
=
&
(
const
snd_usb_midi_endpoint_info_t
)
{
.
epnum
=
-
1
,
.
out_cables
=
0x0003
,
.
in_cables
=
0x0003
}
}
},
/* Midiman/M-Audio devices */
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x1002
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"MidiSport 2x2"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
2
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x1011
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"MidiSport 1x1"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
1
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x1015
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"Keystation"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
1
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x1021
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"MidiSport 4x4"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
4
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x1033
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"MidiSport 8x8"
,
.
ifnum
=
QUIRK_ANY_INTERFACE
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
9
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x2001
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"Quattro"
,
.
ifnum
=
9
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
1
}
},
{
USB_DEVICE_VENDOR_SPEC
(
0x0763
,
0x2003
),
.
driver_info
=
(
unsigned
long
)
&
(
const
snd_usb_audio_quirk_t
)
{
.
vendor_name
=
"M-Audio"
,
.
product_name
=
"AudioPhile"
,
.
ifnum
=
9
,
.
type
=
QUIRK_MIDI_MIDIMAN
,
.
data
=
(
void
*
)
1
}
},
#endif
/* CONFIG_SND_SEQUENCER(_MODULE) */
#undef USB_DEVICE_VENDOR_SPEC
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