Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
b7838c2b
K
Kernel
项目概览
openeuler
/
Kernel
1 年多 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
b7838c2b
编写于
11月 12, 2012
作者:
T
Takashi Iwai
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'stanton-cs1-driver' of
git://git.alsa-project.org/alsa-kprivate
into for-next
上级
063f603c
1999c3a0
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
542 addition
and
0 deletion
+542
-0
sound/firewire/Kconfig
sound/firewire/Kconfig
+13
-0
sound/firewire/Makefile
sound/firewire/Makefile
+2
-0
sound/firewire/scs1x.c
sound/firewire/scs1x.c
+527
-0
未找到文件。
sound/firewire/Kconfig
浏览文件 @
b7838c2b
...
...
@@ -33,4 +33,17 @@ config SND_ISIGHT
To compile this driver as a module, choose M here: the module
will be called snd-isight.
config SND_SCS1X
tristate "Stanton Control System 1 MIDI"
select SND_PCM
select SND_RAWMIDI
select SND_FIREWIRE_LIB
help
Say Y here to include support for the MIDI ports of the Stanton
SCS.1d/SCS.1m DJ controllers. (SCS.1m audio is still handled
by FFADO.)
To compile this driver as a module, choose M here: the module
will be called snd-scs1x.
endif # SND_FIREWIRE
sound/firewire/Makefile
浏览文件 @
b7838c2b
...
...
@@ -2,7 +2,9 @@ snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp.o
snd-firewire-speakers-objs
:=
speakers.o
snd-isight-objs
:=
isight.o
snd-scs1x-objs
:=
scs1x.o
obj-$(CONFIG_SND_FIREWIRE_LIB)
+=
snd-firewire-lib.o
obj-$(CONFIG_SND_FIREWIRE_SPEAKERS)
+=
snd-firewire-speakers.o
obj-$(CONFIG_SND_ISIGHT)
+=
snd-isight.o
obj-$(CONFIG_SND_SCS1X)
+=
snd-scs1x.o
sound/firewire/scs1x.c
0 → 100644
浏览文件 @
b7838c2b
/*
* Stanton Control System 1 MIDI driver
*
* Copyright (c) Clemens Ladisch <clemens@ladisch.de>
* Licensed under the terms of the GNU General Public License, version 2.
*/
#include <linux/device.h>
#include <linux/firewire.h>
#include <linux/firewire-constants.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/wait.h>
#include <sound/core.h>
#include <sound/initval.h>
#include <sound/rawmidi.h>
#include "lib.h"
#define OUI_STANTON 0x001260
#define MODEL_SCS_1M 0x001000
#define MODEL_SCS_1D 0x002000
#define HSS1394_ADDRESS 0xc007dedadadaULL
#define HSS1394_MAX_PACKET_SIZE 64
#define HSS1394_TAG_USER_DATA 0x00
#define HSS1394_TAG_CHANGE_ADDRESS 0xf1
struct
scs
{
struct
snd_card
*
card
;
struct
fw_unit
*
unit
;
struct
fw_address_handler
hss_handler
;
struct
fw_transaction
transaction
;
bool
transaction_running
;
bool
output_idle
;
u8
output_status
;
u8
output_bytes
;
bool
output_escaped
;
bool
output_escape_high_nibble
;
u8
input_escape_count
;
struct
snd_rawmidi_substream
*
output
;
struct
snd_rawmidi_substream
*
input
;
struct
tasklet_struct
tasklet
;
wait_queue_head_t
idle_wait
;
u8
*
buffer
;
};
static
const
u8
sysex_escape_prefix
[]
=
{
0xf0
,
/* SysEx begin */
0x00
,
0x01
,
0x60
,
/* Stanton DJ */
0x48
,
0x53
,
0x53
,
/* "HSS" */
};
static
int
scs_output_open
(
struct
snd_rawmidi_substream
*
stream
)
{
struct
scs
*
scs
=
stream
->
rmidi
->
private_data
;
scs
->
output_status
=
0
;
scs
->
output_bytes
=
1
;
scs
->
output_escaped
=
false
;
return
0
;
}
static
int
scs_output_close
(
struct
snd_rawmidi_substream
*
stream
)
{
return
0
;
}
static
void
scs_output_trigger
(
struct
snd_rawmidi_substream
*
stream
,
int
up
)
{
struct
scs
*
scs
=
stream
->
rmidi
->
private_data
;
ACCESS_ONCE
(
scs
->
output
)
=
up
?
stream
:
NULL
;
if
(
up
)
{
scs
->
output_idle
=
false
;
tasklet_schedule
(
&
scs
->
tasklet
);
}
}
static
void
scs_write_callback
(
struct
fw_card
*
card
,
int
rcode
,
void
*
data
,
size_t
length
,
void
*
callback_data
)
{
struct
scs
*
scs
=
callback_data
;
if
(
rcode
==
RCODE_GENERATION
)
{
/* TODO: retry this packet */
}
scs
->
transaction_running
=
false
;
tasklet_schedule
(
&
scs
->
tasklet
);
}
static
bool
is_valid_running_status
(
u8
status
)
{
return
status
>=
0x80
&&
status
<=
0xef
;
}
static
bool
is_one_byte_cmd
(
u8
status
)
{
return
status
==
0xf6
||
status
>=
0xf8
;
}
static
bool
is_two_bytes_cmd
(
u8
status
)
{
return
(
status
>=
0xc0
&&
status
<=
0xdf
)
||
status
==
0xf1
||
status
==
0xf3
;
}
static
bool
is_three_bytes_cmd
(
u8
status
)
{
return
(
status
>=
0x80
&&
status
<=
0xbf
)
||
(
status
>=
0xe0
&&
status
<=
0xef
)
||
status
==
0xf2
;
}
static
bool
is_invalid_cmd
(
u8
status
)
{
return
status
==
0xf4
||
status
==
0xf5
||
status
==
0xf9
||
status
==
0xfd
;
}
static
void
scs_output_tasklet
(
unsigned
long
data
)
{
struct
scs
*
scs
=
(
void
*
)
data
;
struct
snd_rawmidi_substream
*
stream
;
unsigned
int
i
;
u8
byte
;
struct
fw_device
*
dev
;
int
generation
;
if
(
scs
->
transaction_running
)
return
;
stream
=
ACCESS_ONCE
(
scs
->
output
);
if
(
!
stream
)
{
scs
->
output_idle
=
true
;
wake_up
(
&
scs
->
idle_wait
);
return
;
}
i
=
scs
->
output_bytes
;
for
(;;)
{
if
(
snd_rawmidi_transmit
(
stream
,
&
byte
,
1
)
!=
1
)
{
scs
->
output_bytes
=
i
;
scs
->
output_idle
=
true
;
wake_up
(
&
scs
->
idle_wait
);
return
;
}
/*
* Convert from real MIDI to what I think the device expects (no
* running status, one command per packet, unescaped SysExs).
*/
if
(
scs
->
output_escaped
&&
byte
<
0x80
)
{
if
(
scs
->
output_escape_high_nibble
)
{
if
(
i
<
HSS1394_MAX_PACKET_SIZE
)
{
scs
->
buffer
[
i
]
=
byte
<<
4
;
scs
->
output_escape_high_nibble
=
false
;
}
}
else
{
scs
->
buffer
[
i
++
]
|=
byte
&
0x0f
;
scs
->
output_escape_high_nibble
=
true
;
}
}
else
if
(
byte
<
0x80
)
{
if
(
i
==
1
)
{
if
(
!
is_valid_running_status
(
scs
->
output_status
))
continue
;
scs
->
buffer
[
0
]
=
HSS1394_TAG_USER_DATA
;
scs
->
buffer
[
i
++
]
=
scs
->
output_status
;
}
scs
->
buffer
[
i
++
]
=
byte
;
if
((
i
==
3
&&
is_two_bytes_cmd
(
scs
->
output_status
))
||
(
i
==
4
&&
is_three_bytes_cmd
(
scs
->
output_status
)))
break
;
if
(
i
==
1
+
ARRAY_SIZE
(
sysex_escape_prefix
)
&&
!
memcmp
(
scs
->
buffer
+
1
,
sysex_escape_prefix
,
ARRAY_SIZE
(
sysex_escape_prefix
)))
{
scs
->
output_escaped
=
true
;
scs
->
output_escape_high_nibble
=
true
;
i
=
0
;
}
if
(
i
>=
HSS1394_MAX_PACKET_SIZE
)
i
=
1
;
}
else
if
(
byte
==
0xf7
)
{
if
(
scs
->
output_escaped
)
{
if
(
i
>=
1
&&
scs
->
output_escape_high_nibble
&&
scs
->
buffer
[
0
]
!=
HSS1394_TAG_CHANGE_ADDRESS
)
break
;
}
else
{
if
(
i
>
1
&&
scs
->
output_status
==
0xf0
)
{
scs
->
buffer
[
i
++
]
=
0xf7
;
break
;
}
}
i
=
1
;
scs
->
output_escaped
=
false
;
}
else
if
(
!
is_invalid_cmd
(
byte
)
&&
byte
<
0xf8
)
{
i
=
1
;
scs
->
buffer
[
0
]
=
HSS1394_TAG_USER_DATA
;
scs
->
buffer
[
i
++
]
=
byte
;
scs
->
output_status
=
byte
;
scs
->
output_escaped
=
false
;
if
(
is_one_byte_cmd
(
byte
))
break
;
}
}
scs
->
output_bytes
=
1
;
scs
->
output_escaped
=
false
;
scs
->
transaction_running
=
true
;
dev
=
fw_parent_device
(
scs
->
unit
);
generation
=
dev
->
generation
;
smp_rmb
();
/* node_id vs. generation */
fw_send_request
(
dev
->
card
,
&
scs
->
transaction
,
TCODE_WRITE_BLOCK_REQUEST
,
dev
->
node_id
,
generation
,
dev
->
max_speed
,
HSS1394_ADDRESS
,
scs
->
buffer
,
i
,
scs_write_callback
,
scs
);
}
static
void
scs_output_drain
(
struct
snd_rawmidi_substream
*
stream
)
{
struct
scs
*
scs
=
stream
->
rmidi
->
private_data
;
wait_event
(
scs
->
idle_wait
,
scs
->
output_idle
);
}
static
struct
snd_rawmidi_ops
output_ops
=
{
.
open
=
scs_output_open
,
.
close
=
scs_output_close
,
.
trigger
=
scs_output_trigger
,
.
drain
=
scs_output_drain
,
};
static
int
scs_input_open
(
struct
snd_rawmidi_substream
*
stream
)
{
struct
scs
*
scs
=
stream
->
rmidi
->
private_data
;
scs
->
input_escape_count
=
0
;
return
0
;
}
static
int
scs_input_close
(
struct
snd_rawmidi_substream
*
stream
)
{
return
0
;
}
static
void
scs_input_trigger
(
struct
snd_rawmidi_substream
*
stream
,
int
up
)
{
struct
scs
*
scs
=
stream
->
rmidi
->
private_data
;
ACCESS_ONCE
(
scs
->
input
)
=
up
?
stream
:
NULL
;
}
static
void
scs_input_escaped_byte
(
struct
snd_rawmidi_substream
*
stream
,
u8
byte
)
{
u8
nibbles
[
2
];
nibbles
[
0
]
=
byte
>>
4
;
nibbles
[
1
]
=
byte
&
0x0f
;
snd_rawmidi_receive
(
stream
,
nibbles
,
2
);
}
static
void
scs_input_midi_byte
(
struct
scs
*
scs
,
struct
snd_rawmidi_substream
*
stream
,
u8
byte
)
{
if
(
scs
->
input_escape_count
>
0
)
{
scs_input_escaped_byte
(
stream
,
byte
);
scs
->
input_escape_count
--
;
if
(
scs
->
input_escape_count
==
0
)
snd_rawmidi_receive
(
stream
,
(
const
u8
[])
{
0xf7
},
1
);
}
else
if
(
byte
==
0xf9
)
{
snd_rawmidi_receive
(
stream
,
sysex_escape_prefix
,
ARRAY_SIZE
(
sysex_escape_prefix
));
scs_input_escaped_byte
(
stream
,
0x00
);
scs_input_escaped_byte
(
stream
,
0xf9
);
scs
->
input_escape_count
=
3
;
}
else
{
snd_rawmidi_receive
(
stream
,
&
byte
,
1
);
}
}
static
void
scs_input_packet
(
struct
scs
*
scs
,
struct
snd_rawmidi_substream
*
stream
,
const
u8
*
data
,
unsigned
int
bytes
)
{
unsigned
int
i
;
if
(
data
[
0
]
==
HSS1394_TAG_USER_DATA
)
{
for
(
i
=
1
;
i
<
bytes
;
++
i
)
scs_input_midi_byte
(
scs
,
stream
,
data
[
i
]);
}
else
{
snd_rawmidi_receive
(
stream
,
sysex_escape_prefix
,
ARRAY_SIZE
(
sysex_escape_prefix
));
for
(
i
=
0
;
i
<
bytes
;
++
i
)
scs_input_escaped_byte
(
stream
,
data
[
i
]);
snd_rawmidi_receive
(
stream
,
(
const
u8
[])
{
0xf7
},
1
);
}
}
static
struct
snd_rawmidi_ops
input_ops
=
{
.
open
=
scs_input_open
,
.
close
=
scs_input_close
,
.
trigger
=
scs_input_trigger
,
};
static
int
scs_create_midi
(
struct
scs
*
scs
)
{
struct
snd_rawmidi
*
rmidi
;
int
err
;
err
=
snd_rawmidi_new
(
scs
->
card
,
"SCS.1x"
,
0
,
1
,
1
,
&
rmidi
);
if
(
err
<
0
)
return
err
;
snprintf
(
rmidi
->
name
,
sizeof
(
rmidi
->
name
),
"%s MIDI"
,
scs
->
card
->
shortname
);
rmidi
->
info_flags
=
SNDRV_RAWMIDI_INFO_OUTPUT
|
SNDRV_RAWMIDI_INFO_INPUT
|
SNDRV_RAWMIDI_INFO_DUPLEX
;
rmidi
->
private_data
=
scs
;
snd_rawmidi_set_ops
(
rmidi
,
SNDRV_RAWMIDI_STREAM_OUTPUT
,
&
output_ops
);
snd_rawmidi_set_ops
(
rmidi
,
SNDRV_RAWMIDI_STREAM_INPUT
,
&
input_ops
);
return
0
;
}
static
void
handle_hss
(
struct
fw_card
*
card
,
struct
fw_request
*
request
,
int
tcode
,
int
destination
,
int
source
,
int
generation
,
unsigned
long
long
offset
,
void
*
data
,
size_t
length
,
void
*
callback_data
)
{
struct
scs
*
scs
=
callback_data
;
struct
snd_rawmidi_substream
*
stream
;
if
(
offset
!=
scs
->
hss_handler
.
offset
)
{
fw_send_response
(
card
,
request
,
RCODE_ADDRESS_ERROR
);
return
;
}
if
(
tcode
!=
TCODE_WRITE_QUADLET_REQUEST
&&
tcode
!=
TCODE_WRITE_BLOCK_REQUEST
)
{
fw_send_response
(
card
,
request
,
RCODE_TYPE_ERROR
);
return
;
}
if
(
length
>=
1
)
{
stream
=
ACCESS_ONCE
(
scs
->
input
);
if
(
stream
)
scs_input_packet
(
scs
,
stream
,
data
,
length
);
}
fw_send_response
(
card
,
request
,
RCODE_COMPLETE
);
}
static
int
scs_init_hss_address
(
struct
scs
*
scs
)
{
__be64
data
;
int
err
;
data
=
cpu_to_be64
(((
u64
)
HSS1394_TAG_CHANGE_ADDRESS
<<
56
)
|
scs
->
hss_handler
.
offset
);
err
=
snd_fw_transaction
(
scs
->
unit
,
TCODE_WRITE_BLOCK_REQUEST
,
HSS1394_ADDRESS
,
&
data
,
8
);
if
(
err
<
0
)
dev_err
(
&
scs
->
unit
->
device
,
"HSS1394 communication failed
\n
"
);
return
err
;
}
static
void
scs_card_free
(
struct
snd_card
*
card
)
{
struct
scs
*
scs
=
card
->
private_data
;
fw_core_remove_address_handler
(
&
scs
->
hss_handler
);
kfree
(
scs
->
buffer
);
}
static
int
scs_probe
(
struct
device
*
unit_dev
)
{
struct
fw_unit
*
unit
=
fw_unit
(
unit_dev
);
struct
fw_device
*
fw_dev
=
fw_parent_device
(
unit
);
struct
snd_card
*
card
;
struct
scs
*
scs
;
int
err
;
err
=
snd_card_create
(
-
16
,
NULL
,
THIS_MODULE
,
sizeof
(
*
scs
),
&
card
);
if
(
err
<
0
)
return
err
;
snd_card_set_dev
(
card
,
unit_dev
);
scs
=
card
->
private_data
;
scs
->
card
=
card
;
scs
->
unit
=
unit
;
tasklet_init
(
&
scs
->
tasklet
,
scs_output_tasklet
,
(
unsigned
long
)
scs
);
init_waitqueue_head
(
&
scs
->
idle_wait
);
scs
->
output_idle
=
true
;
scs
->
buffer
=
kmalloc
(
HSS1394_MAX_PACKET_SIZE
,
GFP_KERNEL
);
if
(
!
scs
->
buffer
)
goto
err_card
;
scs
->
hss_handler
.
length
=
HSS1394_MAX_PACKET_SIZE
;
scs
->
hss_handler
.
address_callback
=
handle_hss
;
scs
->
hss_handler
.
callback_data
=
scs
;
err
=
fw_core_add_address_handler
(
&
scs
->
hss_handler
,
&
fw_high_memory_region
);
if
(
err
<
0
)
goto
err_buffer
;
card
->
private_free
=
scs_card_free
;
strcpy
(
card
->
driver
,
"SCS.1x"
);
strcpy
(
card
->
shortname
,
"SCS.1x"
);
fw_csr_string
(
unit
->
directory
,
CSR_MODEL
,
card
->
shortname
,
sizeof
(
card
->
shortname
));
snprintf
(
card
->
longname
,
sizeof
(
card
->
longname
),
"Stanton DJ %s (GUID %08x%08x) at %s, S%d"
,
card
->
shortname
,
fw_dev
->
config_rom
[
3
],
fw_dev
->
config_rom
[
4
],
dev_name
(
&
unit
->
device
),
100
<<
fw_dev
->
max_speed
);
strcpy
(
card
->
mixername
,
card
->
shortname
);
err
=
scs_init_hss_address
(
scs
);
if
(
err
<
0
)
goto
err_card
;
err
=
scs_create_midi
(
scs
);
if
(
err
<
0
)
goto
err_card
;
err
=
snd_card_register
(
card
);
if
(
err
<
0
)
goto
err_card
;
dev_set_drvdata
(
unit_dev
,
scs
);
return
0
;
err_buffer:
kfree
(
scs
->
buffer
);
err_card:
snd_card_free
(
card
);
return
err
;
}
static
int
scs_remove
(
struct
device
*
dev
)
{
struct
scs
*
scs
=
dev_get_drvdata
(
dev
);
snd_card_disconnect
(
scs
->
card
);
ACCESS_ONCE
(
scs
->
output
)
=
NULL
;
ACCESS_ONCE
(
scs
->
input
)
=
NULL
;
wait_event
(
scs
->
idle_wait
,
scs
->
output_idle
);
tasklet_kill
(
&
scs
->
tasklet
);
snd_card_free_when_closed
(
scs
->
card
);
return
0
;
}
static
void
scs_update
(
struct
fw_unit
*
unit
)
{
struct
scs
*
scs
=
dev_get_drvdata
(
&
unit
->
device
);
__be64
data
;
data
=
cpu_to_be64
(((
u64
)
HSS1394_TAG_CHANGE_ADDRESS
<<
56
)
|
scs
->
hss_handler
.
offset
);
snd_fw_transaction
(
scs
->
unit
,
TCODE_WRITE_BLOCK_REQUEST
,
HSS1394_ADDRESS
,
&
data
,
8
);
}
static
const
struct
ieee1394_device_id
scs_id_table
[]
=
{
{
.
match_flags
=
IEEE1394_MATCH_VENDOR_ID
|
IEEE1394_MATCH_MODEL_ID
,
.
vendor_id
=
OUI_STANTON
,
.
model_id
=
MODEL_SCS_1M
,
},
{
.
match_flags
=
IEEE1394_MATCH_VENDOR_ID
|
IEEE1394_MATCH_MODEL_ID
,
.
vendor_id
=
OUI_STANTON
,
.
model_id
=
MODEL_SCS_1D
,
},
{}
};
MODULE_DEVICE_TABLE
(
ieee1394
,
scs_id_table
);
MODULE_DESCRIPTION
(
"SCS.1x MIDI driver"
);
MODULE_AUTHOR
(
"Clemens Ladisch <clemens@ladisch.de>"
);
MODULE_LICENSE
(
"GPL v2"
);
static
struct
fw_driver
scs_driver
=
{
.
driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
KBUILD_MODNAME
,
.
bus
=
&
fw_bus_type
,
.
probe
=
scs_probe
,
.
remove
=
scs_remove
,
},
.
update
=
scs_update
,
.
id_table
=
scs_id_table
,
};
static
int
__init
alsa_scs1x_init
(
void
)
{
return
driver_register
(
&
scs_driver
.
driver
);
}
static
void
__exit
alsa_scs1x_exit
(
void
)
{
driver_unregister
(
&
scs_driver
.
driver
);
}
module_init
(
alsa_scs1x_init
);
module_exit
(
alsa_scs1x_exit
);
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录