mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-05 01:32:47 -04:00
ALSA: scarlett2: Add support for device map retrieval
Add support for retrieving the device map from Focusrite Scarlett 4th Gen and Vocaster devices. The device map is a base64-encoded, zlib-compressed JSON description of the device's capabilities and configuration. This patch adds: - a has_devmap field to the scarlett2_device_info struct - a /proc/asound/cardX/device-map.json.zz.b64 file when supported Signed-off-by: Geoffrey D. Bennett <g@b4.vu> Signed-off-by: Takashi Iwai <tiwai@suse.de> Link: https://patch.msgid.link/e86380c6792460d8d05a8ecc37c9ebd072be25a5.1727971672.git.g@b4.vu
This commit is contained in:
committed by
Takashi Iwai
parent
8eba063b5b
commit
9930c26060
@@ -1079,6 +1079,9 @@ struct scarlett2_device_info {
|
||||
/* minimum firmware version required */
|
||||
u16 min_firmware_version;
|
||||
|
||||
/* has a downloadable device map */
|
||||
u8 has_devmap;
|
||||
|
||||
/* support for main/alt speaker switching */
|
||||
u8 has_speaker_switching;
|
||||
|
||||
@@ -1773,6 +1776,7 @@ static const struct scarlett2_device_info s18i20_gen3_info = {
|
||||
static const struct scarlett2_device_info vocaster_one_info = {
|
||||
.config_set = &scarlett2_config_set_vocaster,
|
||||
.min_firmware_version = 1769,
|
||||
.has_devmap = 1,
|
||||
|
||||
.phantom_count = 1,
|
||||
.inputs_per_phantom = 1,
|
||||
@@ -1815,6 +1819,7 @@ static const struct scarlett2_device_info vocaster_one_info = {
|
||||
static const struct scarlett2_device_info vocaster_two_info = {
|
||||
.config_set = &scarlett2_config_set_vocaster,
|
||||
.min_firmware_version = 1769,
|
||||
.has_devmap = 1,
|
||||
|
||||
.phantom_count = 2,
|
||||
.inputs_per_phantom = 1,
|
||||
@@ -1858,6 +1863,7 @@ static const struct scarlett2_device_info vocaster_two_info = {
|
||||
static const struct scarlett2_device_info solo_gen4_info = {
|
||||
.config_set = &scarlett2_config_set_gen4_solo,
|
||||
.min_firmware_version = 2115,
|
||||
.has_devmap = 1,
|
||||
|
||||
.level_input_count = 1,
|
||||
.air_input_count = 1,
|
||||
@@ -1912,6 +1918,7 @@ static const struct scarlett2_device_info solo_gen4_info = {
|
||||
static const struct scarlett2_device_info s2i2_gen4_info = {
|
||||
.config_set = &scarlett2_config_set_gen4_2i2,
|
||||
.min_firmware_version = 2115,
|
||||
.has_devmap = 1,
|
||||
|
||||
.level_input_count = 2,
|
||||
.air_input_count = 2,
|
||||
@@ -1966,6 +1973,7 @@ static const struct scarlett2_device_info s2i2_gen4_info = {
|
||||
static const struct scarlett2_device_info s4i4_gen4_info = {
|
||||
.config_set = &scarlett2_config_set_gen4_4i4,
|
||||
.min_firmware_version = 2089,
|
||||
.has_devmap = 1,
|
||||
|
||||
.level_input_count = 2,
|
||||
.air_input_count = 2,
|
||||
@@ -2264,6 +2272,8 @@ static int scarlett2_get_port_start_num(
|
||||
#define SCARLETT2_USB_GET_DATA 0x00800000
|
||||
#define SCARLETT2_USB_SET_DATA 0x00800001
|
||||
#define SCARLETT2_USB_DATA_CMD 0x00800002
|
||||
#define SCARLETT2_USB_INFO_DEVMAP 0x0080000c
|
||||
#define SCARLETT2_USB_GET_DEVMAP 0x0080000d
|
||||
|
||||
#define SCARLETT2_USB_CONFIG_SAVE 6
|
||||
|
||||
@@ -2277,6 +2287,14 @@ static int scarlett2_get_port_start_num(
|
||||
#define SCARLETT2_SEGMENT_SETTINGS_NAME "App_Settings"
|
||||
#define SCARLETT2_SEGMENT_FIRMWARE_NAME "App_Upgrade"
|
||||
|
||||
/* Gen 4 device firmware provides access to a base64-encoded
|
||||
* zlib-compressed JSON description of the device's capabilities and
|
||||
* configuration. This device map is made available in
|
||||
* /proc/asound/cardX/device-map.json.zz.b64
|
||||
*/
|
||||
#define SCARLETT2_DEVMAP_BLOCK_SIZE 1024
|
||||
#define SCARLETT2_DEVMAP_FILENAME "device-map.json.zz.b64"
|
||||
|
||||
/* proprietary request/response format */
|
||||
struct scarlett2_usb_packet {
|
||||
__le32 cmd;
|
||||
@@ -9562,6 +9580,116 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*** device-map file ***/
|
||||
|
||||
static ssize_t scarlett2_devmap_read(
|
||||
struct snd_info_entry *entry,
|
||||
void *file_private_data,
|
||||
struct file *file,
|
||||
char __user *buf,
|
||||
size_t count,
|
||||
loff_t pos)
|
||||
{
|
||||
struct usb_mixer_interface *mixer = entry->private_data;
|
||||
u8 *resp_buf;
|
||||
const size_t block_size = SCARLETT2_DEVMAP_BLOCK_SIZE;
|
||||
size_t copied = 0;
|
||||
|
||||
if (pos >= entry->size)
|
||||
return 0;
|
||||
|
||||
if (pos + count > entry->size)
|
||||
count = entry->size - pos;
|
||||
|
||||
resp_buf = kmalloc(block_size, GFP_KERNEL);
|
||||
if (!resp_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
while (count > 0) {
|
||||
/* SCARLETT2_USB_GET_DEVMAP reads only on block boundaries,
|
||||
* so we need to read a whole block and copy the requested
|
||||
* chunk to userspace.
|
||||
*/
|
||||
|
||||
__le32 req;
|
||||
int err;
|
||||
|
||||
/* offset within the block that we're reading */
|
||||
size_t offset = pos % block_size;
|
||||
|
||||
/* read_size is block_size except for the last block */
|
||||
size_t block_start = pos - offset;
|
||||
size_t read_size = min_t(size_t,
|
||||
block_size,
|
||||
entry->size - block_start);
|
||||
|
||||
/* size of the chunk to copy to userspace */
|
||||
size_t copy_size = min_t(size_t, count, read_size - offset);
|
||||
|
||||
/* request the block */
|
||||
req = cpu_to_le32(pos / block_size);
|
||||
err = scarlett2_usb(mixer, SCARLETT2_USB_GET_DEVMAP,
|
||||
&req, sizeof(req), resp_buf, read_size);
|
||||
if (err < 0) {
|
||||
kfree(resp_buf);
|
||||
return copied ? copied : err;
|
||||
}
|
||||
|
||||
if (copy_to_user(buf, resp_buf + offset, copy_size)) {
|
||||
kfree(resp_buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
buf += copy_size;
|
||||
pos += copy_size;
|
||||
copied += copy_size;
|
||||
count -= copy_size;
|
||||
}
|
||||
|
||||
kfree(resp_buf);
|
||||
return copied;
|
||||
}
|
||||
|
||||
static const struct snd_info_entry_ops scarlett2_devmap_ops = {
|
||||
.read = scarlett2_devmap_read,
|
||||
};
|
||||
|
||||
static int scarlett2_devmap_init(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
struct snd_card *card = mixer->chip->card;
|
||||
struct scarlett2_data *private = mixer->private_data;
|
||||
const struct scarlett2_device_info *info = private->info;
|
||||
__le16 config_len_buf[2];
|
||||
int config_len;
|
||||
struct snd_info_entry *entry;
|
||||
int err;
|
||||
|
||||
/* If the device doesn't support the DEVMAP commands, don't
|
||||
* create the /proc/asound/cardX/scarlett.json.zlib entry
|
||||
*/
|
||||
if (!info->has_devmap)
|
||||
return 0;
|
||||
|
||||
err = scarlett2_usb(mixer, SCARLETT2_USB_INFO_DEVMAP,
|
||||
NULL, 0, &config_len_buf, sizeof(config_len_buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
config_len = le16_to_cpu(config_len_buf[1]);
|
||||
|
||||
err = snd_card_proc_new(card, SCARLETT2_DEVMAP_FILENAME, &entry);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
entry->content = SNDRV_INFO_CONTENT_DATA;
|
||||
entry->private_data = mixer;
|
||||
entry->c.ops = &scarlett2_devmap_ops;
|
||||
entry->size = config_len;
|
||||
entry->mode = S_IFREG | 0444;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_scarlett2_init(struct usb_mixer_interface *mixer)
|
||||
{
|
||||
struct snd_usb_audio *chip = mixer->chip;
|
||||
@@ -9612,11 +9740,20 @@ int snd_scarlett2_init(struct usb_mixer_interface *mixer)
|
||||
}
|
||||
|
||||
err = scarlett2_hwdep_init(mixer);
|
||||
if (err < 0)
|
||||
if (err < 0) {
|
||||
usb_audio_err(mixer->chip,
|
||||
"Error creating %s hwdep device: %d",
|
||||
entry->series_name,
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = scarlett2_devmap_init(mixer);
|
||||
if (err < 0)
|
||||
usb_audio_err(mixer->chip,
|
||||
"Error creating %s devmap entry: %d",
|
||||
entry->series_name,
|
||||
err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user