ALSA: FCP: Add Focusrite Control Protocol driver

Add a new kernel driver for the Focusrite Control Protocol (FCP),
which is used by Focusrite Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett
USB, Clarett+, and Vocaster series audio interfaces. This driver
provides a user-space control interface via ALSA's hwdep subsystem.

Unlike the existing Scarlett2 driver which implements all ALSA
controls in kernel space, this new FCP driver takes a different
approach by providing a minimal kernel interface that allows a
user-space driver to send FCP commands and receive notifications. The
only control implemented in kernel space is the Level Meter, since it
requires frequent polling of volatile data.

While this driver supports all interfaces that the Scarlett2 driver
works with, it is initially enabled only for 4th Gen 16i16, 18i16,
and 18i20 interfaces that are not supported by the Scarlett2 driver.

Signed-off-by: Geoffrey D. Bennett <g@b4.vu>
Link: https://patch.msgid.link/597741a9b1198b965561547511d3d345f91cba20.1737048528.git.g@b4.vu
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Geoffrey D. Bennett 2025-01-17 04:17:38 +10:30 committed by Takashi Iwai
parent bb5f86ea50
commit 46757a3e7d
7 changed files with 1277 additions and 4 deletions

View File

@ -8951,14 +8951,16 @@ L: linux-input@vger.kernel.org
S: Maintained
F: drivers/input/joystick/fsia6b.c
FOCUSRITE SCARLETT2 MIXER DRIVER (Scarlett Gen 2+ and Clarett)
FOCUSRITE CONTROL PROTOCOL/SCARLETT2 MIXER DRIVERS (Scarlett Gen 2+, Clarett, and Vocaster)
M: Geoffrey D. Bennett <g@b4.vu>
L: linux-sound@vger.kernel.org
S: Maintained
W: https://github.com/geoffreybennett/scarlett-gen2
B: https://github.com/geoffreybennett/scarlett-gen2/issues
T: git https://github.com/geoffreybennett/scarlett-gen2.git
W: https://github.com/geoffreybennett/linux-fcp
B: https://github.com/geoffreybennett/linux-fcp/issues
T: git https://github.com/geoffreybennett/linux-fcp.git
F: include/uapi/sound/fcp.h
F: include/uapi/sound/scarlett2.h
F: sound/usb/fcp.c
F: sound/usb/mixer_scarlett2.c
FORCEDETH GIGABIT ETHERNET DRIVER

120
include/uapi/sound/fcp.h Normal file
View File

@ -0,0 +1,120 @@
/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
/*
* Focusrite Control Protocol Driver for ALSA
*
* Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu>
*/
/*
* DOC: FCP (Focusrite Control Protocol) User-Space API
*
* This header defines the interface between the FCP kernel driver and
* user-space programs to enable the use of the proprietary features
* available in Focusrite USB audio interfaces. This includes Scarlett
* 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and Vocaster
* series devices.
*
* The interface is provided via ALSA's hwdep interface. Opening the
* hwdep device requires CAP_SYS_RAWIO privileges as this interface
* provides near-direct access.
*
* For details on the FCP protocol, refer to the kernel scarlett2
* driver in sound/usb/mixer_scarlett2.c and the fcp-support project
* at https://github.com/geoffreybennett/fcp-support
*
* For examples of using these IOCTLs, see the fcp-server source in
* the fcp-support project.
*
* IOCTL Interface
* --------------
* FCP_IOCTL_PVERSION:
* Returns the protocol version supported by the driver.
*
* FCP_IOCTL_INIT:
* Initialises the protocol and synchronises sequence numbers
* between the driver and device. Must be called at least once
* before sending commands. Can be safely called again at any time.
*
* FCP_IOCTL_CMD:
* Sends an FCP command to the device and returns the response.
* Requires prior initialisation via FCP_IOCTL_INIT.
*
* FCP_IOCTL_SET_METER_MAP:
* Configures the Level Meter control's mapping between device
* meters and control channels. Requires FCP_IOCTL_INIT to have been
* called first. The map size and number of slots cannot be changed
* after initial configuration, although the map itself can be
* updated. Once configured, the Level Meter remains functional even
* after the hwdep device is closed.
*
* FCP_IOCTL_SET_METER_LABELS:
* Set the labels for the Level Meter control. Requires
* FCP_IOCTL_SET_METER_MAP to have been called first. labels[]
* should contain a sequence of null-terminated labels corresponding
* to the control's channels.
*/
#ifndef __UAPI_SOUND_FCP_H
#define __UAPI_SOUND_FCP_H
#include <linux/types.h>
#include <linux/ioctl.h>
#define FCP_HWDEP_MAJOR 2
#define FCP_HWDEP_MINOR 0
#define FCP_HWDEP_SUBMINOR 0
#define FCP_HWDEP_VERSION \
((FCP_HWDEP_MAJOR << 16) | \
(FCP_HWDEP_MINOR << 8) | \
FCP_HWDEP_SUBMINOR)
#define FCP_HWDEP_VERSION_MAJOR(v) (((v) >> 16) & 0xFF)
#define FCP_HWDEP_VERSION_MINOR(v) (((v) >> 8) & 0xFF)
#define FCP_HWDEP_VERSION_SUBMINOR(v) ((v) & 0xFF)
/* Get protocol version */
#define FCP_IOCTL_PVERSION _IOR('S', 0x60, int)
/* Start the protocol */
/* Step 0 and step 2 responses are variable length and placed in
* resp[] one after the other.
*/
struct fcp_init {
__u16 step0_resp_size;
__u16 step2_resp_size;
__u32 init1_opcode;
__u32 init2_opcode;
__u8 resp[];
} __attribute__((packed));
#define FCP_IOCTL_INIT _IOWR('S', 0x64, struct fcp_init)
/* Perform a command */
/* The request data is placed in data[] and the response data will
* overwrite it.
*/
struct fcp_cmd {
__u32 opcode;
__u16 req_size;
__u16 resp_size;
__u8 data[];
} __attribute__((packed));
#define FCP_IOCTL_CMD _IOWR('S', 0x65, struct fcp_cmd)
/* Set the meter map */
struct fcp_meter_map {
__u16 map_size;
__u16 meter_slots;
__s16 map[];
} __attribute__((packed));
#define FCP_IOCTL_SET_METER_MAP _IOW('S', 0x66, struct fcp_meter_map)
/* Set the meter labels */
struct fcp_meter_labels {
__u16 labels_size;
char labels[];
} __attribute__((packed));
#define FCP_IOCTL_SET_METER_LABELS _IOW('S', 0x67, struct fcp_meter_labels)
#endif /* __UAPI_SOUND_FCP_H */

View File

@ -18,6 +18,8 @@
#define SNDRV_CTL_TLVT_CHMAP_VAR 0x102 /* channels freely swappable */
#define SNDRV_CTL_TLVT_CHMAP_PAIRED 0x103 /* pair-wise swappable */
#define SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS 0x110 /* channel labels */
/*
* TLV structure is right behind the struct snd_ctl_tlv:
* unsigned int type - see SNDRV_CTL_TLVT_*

View File

@ -6,6 +6,7 @@
snd-usb-audio-y := card.o \
clock.o \
endpoint.o \
fcp.o \
format.o \
helper.o \
implicit.o \

1134
sound/usb/fcp.c Normal file

File diff suppressed because it is too large Load Diff

7
sound/usb/fcp.h Normal file
View File

@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __USBAUDIO_FCP_H
#define __USBAUDIO_FCP_H
int snd_fcp_init(struct usb_mixer_interface *mixer);
#endif /* __USBAUDIO_FCP_H */

View File

@ -38,6 +38,7 @@
#include "mixer_us16x08.h"
#include "mixer_s1810c.h"
#include "helper.h"
#include "fcp.h"
struct std_mono_table {
unsigned int unitid, control, cmask;
@ -4090,6 +4091,12 @@ int snd_usb_mixer_apply_create_quirk(struct usb_mixer_interface *mixer)
err = snd_scarlett2_init(mixer);
break;
case USB_ID(0x1235, 0x821b): /* Focusrite Scarlett 16i16 4th Gen */
case USB_ID(0x1235, 0x821c): /* Focusrite Scarlett 18i16 4th Gen */
case USB_ID(0x1235, 0x821d): /* Focusrite Scarlett 18i20 4th Gen */
err = snd_fcp_init(mixer);
break;
case USB_ID(0x041e, 0x323b): /* Creative Sound Blaster E1 */
err = snd_soundblaster_e1_switch_create(mixer);
break;