| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Focusrite Control Protocol Driver for ALSA
- *
- * Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu>
- */
- /*
- * DOC: Theory of Operation
- *
- * The Focusrite Control Protocol (FCP) driver provides a minimal
- * kernel interface that allows a user-space driver (primarily
- * fcp-server) to communicate with Focusrite USB audio interfaces
- * using their vendor-specific protocol. This protocol is used by
- * Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and
- * Vocaster series devices.
- *
- * Unlike the existing scarlett2 driver which implements all controls
- * in kernel space, this driver takes a lighter-weight approach by
- * moving most functionality to user space. The only control
- * implemented in kernel space is the Level Meter, since it requires
- * frequent polling of volatile data.
- *
- * The driver provides an hwdep interface that allows the user-space
- * driver to:
- * - Initialise the protocol
- * - Send arbitrary FCP commands to the device
- * - Receive notifications from the device
- * - Configure the Level Meter control
- *
- * Usage Flow
- * ----------
- * 1. Open the hwdep device (requires CAP_SYS_RAWIO)
- * 2. Get protocol version using FCP_IOCTL_PVERSION
- * 3. Initialise protocol using FCP_IOCTL_INIT
- * 4. Send commands using FCP_IOCTL_CMD
- * 5. Receive notifications using read()
- * 6. Optionally set up the Level Meter control using
- * FCP_IOCTL_SET_METER_MAP
- * 7. Optionally add labels to the Level Meter control using
- * FCP_IOCTL_SET_METER_LABELS
- *
- * Level Meter
- * -----------
- * The Level Meter is implemented as an ALSA control that provides
- * real-time level monitoring. When the control is read, the driver
- * requests the current meter levels from the device, translates the
- * levels using the configured mapping, and returns the result to the
- * user. The mapping between device meters and the ALSA control's
- * channels is configured with FCP_IOCTL_SET_METER_MAP.
- *
- * Labels for the Level Meter channels can be set using
- * FCP_IOCTL_SET_METER_LABELS and read by applications through the
- * control's TLV data. The labels are transferred as a sequence of
- * null-terminated strings.
- */
- #include <linux/slab.h>
- #include <linux/usb.h>
- #include <sound/control.h>
- #include <sound/hwdep.h>
- #include <sound/tlv.h>
- #include <uapi/sound/fcp.h>
- #include "usbaudio.h"
- #include "mixer.h"
- #include "helper.h"
- #include "fcp.h"
- /* notify waiting to send to *file */
- struct fcp_notify {
- wait_queue_head_t queue;
- u32 event;
- spinlock_t lock;
- };
- struct fcp_data {
- struct usb_mixer_interface *mixer;
- struct mutex mutex; /* serialise access to the device */
- struct completion cmd_done; /* wait for command completion */
- struct file *file; /* hwdep file */
- struct fcp_notify notify;
- u8 bInterfaceNumber;
- u8 bEndpointAddress;
- u16 wMaxPacketSize;
- u8 bInterval;
- uint16_t step0_resp_size;
- uint16_t step2_resp_size;
- uint32_t init1_opcode;
- uint32_t init2_opcode;
- u8 init;
- u16 seq;
- u8 num_meter_slots;
- s16 *meter_level_map;
- __le32 *meter_levels;
- struct snd_kcontrol *meter_ctl;
- unsigned int *meter_labels_tlv;
- int meter_labels_tlv_size;
- };
- /*** USB Interactions ***/
- /* FCP Command ACK notification bit */
- #define FCP_NOTIFY_ACK 1
- /* Vendor-specific USB control requests */
- #define FCP_USB_REQ_STEP0 0
- #define FCP_USB_REQ_CMD_TX 2
- #define FCP_USB_REQ_CMD_RX 3
- /* Focusrite Control Protocol opcodes that the kernel side needs to
- * know about
- */
- #define FCP_USB_REBOOT 0x00000003
- #define FCP_USB_GET_METER 0x00001001
- #define FCP_USB_FLASH_ERASE 0x00004002
- #define FCP_USB_FLASH_WRITE 0x00004004
- #define FCP_USB_METER_LEVELS_GET_MAGIC 1
- #define FCP_SEGMENT_APP_GOLD 0
- /* Forward declarations */
- static int fcp_init(struct usb_mixer_interface *mixer,
- void *step0_resp, void *step2_resp);
- /* FCP command request/response format */
- struct fcp_usb_packet {
- __le32 opcode;
- __le16 size;
- __le16 seq;
- __le32 error;
- __le32 pad;
- u8 data[];
- };
- static void fcp_fill_request_header(struct fcp_data *private,
- struct fcp_usb_packet *req,
- u32 opcode, u16 req_size)
- {
- /* sequence must go up by 1 for each request */
- u16 seq = private->seq++;
- req->opcode = cpu_to_le32(opcode);
- req->size = cpu_to_le16(req_size);
- req->seq = cpu_to_le16(seq);
- req->error = 0;
- req->pad = 0;
- }
- static int fcp_usb_tx(struct usb_device *dev, int interface,
- void *buf, u16 size)
- {
- return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
- FCP_USB_REQ_CMD_TX,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
- 0, interface, buf, size);
- }
- static int fcp_usb_rx(struct usb_device *dev, int interface,
- void *buf, u16 size)
- {
- return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- FCP_USB_REQ_CMD_RX,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- 0, interface, buf, size);
- }
- /* Send an FCP command and get the response */
- static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
- const void *req_data, u16 req_size,
- void *resp_data, u16 resp_size)
- {
- struct fcp_data *private = mixer->private_data;
- struct usb_device *dev = mixer->chip->dev;
- int retries = 0;
- const int max_retries = 5;
- int err;
- if (!mixer->urb)
- return -ENODEV;
- struct fcp_usb_packet *req __free(kfree) = NULL;
- size_t req_buf_size = struct_size(req, data, req_size);
- req = kmalloc(req_buf_size, GFP_KERNEL);
- if (!req)
- return -ENOMEM;
- struct fcp_usb_packet *resp __free(kfree) = NULL;
- size_t resp_buf_size = struct_size(resp, data, resp_size);
- resp = kmalloc(resp_buf_size, GFP_KERNEL);
- if (!resp)
- return -ENOMEM;
- /* build request message */
- fcp_fill_request_header(private, req, opcode, req_size);
- if (req_size)
- memcpy(req->data, req_data, req_size);
- /* send the request and retry on EPROTO */
- retry:
- err = fcp_usb_tx(dev, private->bInterfaceNumber, req, req_buf_size);
- if (err == -EPROTO && ++retries <= max_retries) {
- msleep(1 << (retries - 1));
- goto retry;
- }
- if (err != req_buf_size) {
- usb_audio_err(mixer->chip,
- "FCP request %08x failed: %d\n", opcode, err);
- return -EINVAL;
- }
- if (!wait_for_completion_timeout(&private->cmd_done,
- msecs_to_jiffies(1000))) {
- usb_audio_err(mixer->chip,
- "FCP request %08x timed out\n", opcode);
- return -ETIMEDOUT;
- }
- /* send a second message to get the response */
- err = fcp_usb_rx(dev, private->bInterfaceNumber, resp, resp_buf_size);
- /* validate the response */
- if (err < 0) {
- /* ESHUTDOWN and EPROTO are valid responses to a
- * reboot request
- */
- if (opcode == FCP_USB_REBOOT &&
- (err == -ESHUTDOWN || err == -EPROTO))
- return 0;
- usb_audio_err(mixer->chip,
- "FCP read response %08x failed: %d\n",
- opcode, err);
- return -EINVAL;
- }
- if (err < sizeof(*resp)) {
- usb_audio_err(mixer->chip,
- "FCP response %08x too short: %d\n",
- opcode, err);
- return -EINVAL;
- }
- if (req->seq != resp->seq) {
- usb_audio_err(mixer->chip,
- "FCP response %08x seq mismatch %d/%d\n",
- opcode,
- le16_to_cpu(req->seq), le16_to_cpu(resp->seq));
- return -EINVAL;
- }
- if (req->opcode != resp->opcode) {
- usb_audio_err(mixer->chip,
- "FCP response %08x opcode mismatch %08x\n",
- opcode, le32_to_cpu(resp->opcode));
- return -EINVAL;
- }
- if (resp->error) {
- usb_audio_err(mixer->chip,
- "FCP response %08x error %d\n",
- opcode, le32_to_cpu(resp->error));
- return -EINVAL;
- }
- if (err != resp_buf_size) {
- usb_audio_err(mixer->chip,
- "FCP response %08x buffer size mismatch %d/%zu\n",
- opcode, err, resp_buf_size);
- return -EINVAL;
- }
- if (resp_size != le16_to_cpu(resp->size)) {
- usb_audio_err(mixer->chip,
- "FCP response %08x size mismatch %d/%d\n",
- opcode, resp_size, le16_to_cpu(resp->size));
- return -EINVAL;
- }
- if (resp_data && resp_size > 0)
- memcpy(resp_data, resp->data, resp_size);
- return 0;
- }
- static int fcp_reinit(struct usb_mixer_interface *mixer)
- {
- struct fcp_data *private = mixer->private_data;
- if (mixer->urb)
- return 0;
- void *step0_resp __free(kfree) =
- kmalloc(private->step0_resp_size, GFP_KERNEL);
- if (!step0_resp)
- return -ENOMEM;
- void *step2_resp __free(kfree) =
- kmalloc(private->step2_resp_size, GFP_KERNEL);
- if (!step2_resp)
- return -ENOMEM;
- return fcp_init(mixer, step0_resp, step2_resp);
- }
- /*** Control Functions ***/
- /* helper function to create a new control */
- static int fcp_add_new_ctl(struct usb_mixer_interface *mixer,
- const struct snd_kcontrol_new *ncontrol,
- int index, int channels, const char *name,
- struct snd_kcontrol **kctl_return)
- {
- struct snd_kcontrol *kctl;
- struct usb_mixer_elem_info *elem;
- int err;
- elem = kzalloc_obj(*elem);
- if (!elem)
- return -ENOMEM;
- /* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code
- * ignores them for resume and other operations.
- * Also, the head.id field is set to 0, as we don't use this field.
- */
- elem->head.mixer = mixer;
- elem->control = index;
- elem->head.id = 0;
- elem->channels = channels;
- elem->val_type = USB_MIXER_BESPOKEN;
- kctl = snd_ctl_new1(ncontrol, elem);
- if (!kctl) {
- kfree(elem);
- return -ENOMEM;
- }
- kctl->private_free = snd_usb_mixer_elem_free;
- strscpy(kctl->id.name, name, sizeof(kctl->id.name));
- err = snd_usb_mixer_add_control(&elem->head, kctl);
- if (err < 0)
- return err;
- if (kctl_return)
- *kctl_return = kctl;
- return 0;
- }
- /*** Level Meter Control ***/
- static int fcp_meter_ctl_info(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_info *uinfo)
- {
- struct usb_mixer_elem_info *elem = kctl->private_data;
- uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = elem->channels;
- uinfo->value.integer.min = 0;
- uinfo->value.integer.max = 4095;
- uinfo->value.integer.step = 1;
- return 0;
- }
- static int fcp_meter_ctl_get(struct snd_kcontrol *kctl,
- struct snd_ctl_elem_value *ucontrol)
- {
- struct usb_mixer_elem_info *elem = kctl->private_data;
- struct usb_mixer_interface *mixer = elem->head.mixer;
- struct fcp_data *private = mixer->private_data;
- int num_meter_slots, resp_size;
- __le32 *resp = private->meter_levels;
- int i, err = 0;
- struct {
- __le16 pad;
- __le16 num_meters;
- __le32 magic;
- } __packed req;
- guard(mutex)(&private->mutex);
- err = fcp_reinit(mixer);
- if (err < 0)
- return err;
- num_meter_slots = private->num_meter_slots;
- resp_size = num_meter_slots * sizeof(u32);
- req.pad = 0;
- req.num_meters = cpu_to_le16(num_meter_slots);
- req.magic = cpu_to_le32(FCP_USB_METER_LEVELS_GET_MAGIC);
- err = fcp_usb(mixer, FCP_USB_GET_METER,
- &req, sizeof(req), resp, resp_size);
- if (err < 0)
- return err;
- /* copy & translate from resp[] using meter_level_map[] */
- for (i = 0; i < elem->channels; i++) {
- int idx = private->meter_level_map[i];
- int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]);
- ucontrol->value.integer.value[i] = value;
- }
- return 0;
- }
- static int fcp_meter_tlv_callback(struct snd_kcontrol *kctl,
- int op_flag, unsigned int size,
- unsigned int __user *tlv)
- {
- struct usb_mixer_elem_info *elem = kctl->private_data;
- struct usb_mixer_interface *mixer = elem->head.mixer;
- struct fcp_data *private = mixer->private_data;
- guard(mutex)(&private->mutex);
- if (op_flag == SNDRV_CTL_TLV_OP_READ) {
- if (private->meter_labels_tlv_size == 0)
- return 0;
- if (size > private->meter_labels_tlv_size)
- size = private->meter_labels_tlv_size;
- if (copy_to_user(tlv, private->meter_labels_tlv, size))
- return -EFAULT;
- return size;
- }
- return -EINVAL;
- }
- static const struct snd_kcontrol_new fcp_meter_ctl = {
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .access = SNDRV_CTL_ELEM_ACCESS_READ |
- SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .info = fcp_meter_ctl_info,
- .get = fcp_meter_ctl_get,
- .tlv = { .c = fcp_meter_tlv_callback },
- };
- /*** hwdep interface ***/
- /* FCP initialisation */
- static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
- struct fcp_init __user *arg)
- {
- struct fcp_init init;
- struct usb_device *dev = mixer->chip->dev;
- struct fcp_data *private = mixer->private_data;
- void *step2_resp;
- int err, buf_size;
- if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
- return -EINVAL;
- /* Get initialisation parameters */
- if (copy_from_user(&init, arg, sizeof(init)))
- return -EFAULT;
- /* Validate the response sizes */
- if (init.step0_resp_size < 1 ||
- init.step0_resp_size > 255 ||
- init.step2_resp_size < 1 ||
- init.step2_resp_size > 255)
- return -EINVAL;
- /* Allocate response buffer */
- buf_size = init.step0_resp_size + init.step2_resp_size;
- void *resp __free(kfree) =
- kmalloc(buf_size, GFP_KERNEL);
- if (!resp)
- return -ENOMEM;
- private->step0_resp_size = init.step0_resp_size;
- private->step2_resp_size = init.step2_resp_size;
- private->init1_opcode = init.init1_opcode;
- private->init2_opcode = init.init2_opcode;
- step2_resp = resp + private->step0_resp_size;
- err = fcp_init(mixer, resp, step2_resp);
- if (err < 0)
- return err;
- if (copy_to_user(arg->resp, resp, buf_size))
- return -EFAULT;
- return 0;
- }
- /* Check that the command is allowed
- * Don't permit erasing/writing segment 0 (App_Gold)
- */
- static int fcp_validate_cmd(u32 opcode, void *data, u16 size)
- {
- if (opcode == FCP_USB_FLASH_ERASE) {
- struct {
- __le32 segment_num;
- __le32 pad;
- } __packed *req = data;
- if (size != sizeof(*req))
- return -EINVAL;
- if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
- return -EPERM;
- if (req->pad != 0)
- return -EINVAL;
- } else if (opcode == FCP_USB_FLASH_WRITE) {
- struct {
- __le32 segment_num;
- __le32 offset;
- __le32 pad;
- u8 data[];
- } __packed *req = data;
- if (size < sizeof(*req))
- return -EINVAL;
- if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
- return -EPERM;
- if (req->pad != 0)
- return -EINVAL;
- }
- return 0;
- }
- /* Execute an FCP command specified by the user */
- static int fcp_ioctl_cmd(struct usb_mixer_interface *mixer,
- struct fcp_cmd __user *arg)
- {
- struct fcp_cmd cmd;
- int err, buf_size;
- void *data __free(kfree) = NULL;
- /* get opcode and request/response size */
- if (copy_from_user(&cmd, arg, sizeof(cmd)))
- return -EFAULT;
- /* validate request and response sizes */
- if (cmd.req_size > 4096 || cmd.resp_size > 4096)
- return -EINVAL;
- /* reinit if needed */
- err = fcp_reinit(mixer);
- if (err < 0)
- return err;
- /* allocate request/response buffer */
- buf_size = max(cmd.req_size, cmd.resp_size);
- if (buf_size > 0) {
- data = kmalloc(buf_size, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
- }
- /* copy request from user */
- if (cmd.req_size > 0)
- if (copy_from_user(data, arg->data, cmd.req_size))
- return -EFAULT;
- /* check that the command is allowed */
- err = fcp_validate_cmd(cmd.opcode, data, cmd.req_size);
- if (err < 0)
- return err;
- /* send request, get response */
- err = fcp_usb(mixer, cmd.opcode,
- data, cmd.req_size, data, cmd.resp_size);
- if (err < 0)
- return err;
- /* copy response to user */
- if (cmd.resp_size > 0)
- if (copy_to_user(arg->data, data, cmd.resp_size))
- return -EFAULT;
- return 0;
- }
- /* Validate the Level Meter map passed by the user */
- static int validate_meter_map(const s16 *map, int map_size, int meter_slots)
- {
- int i;
- for (i = 0; i < map_size; i++)
- if (map[i] < -1 || map[i] >= meter_slots)
- return -EINVAL;
- return 0;
- }
- /* Set the Level Meter map and add the control */
- static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
- struct fcp_meter_map __user *arg)
- {
- struct fcp_meter_map map;
- struct fcp_data *private = mixer->private_data;
- int err;
- if (copy_from_user(&map, arg, sizeof(map)))
- return -EFAULT;
- /* Don't allow changing the map size or meter slots once set */
- if (private->meter_ctl) {
- struct usb_mixer_elem_info *elem =
- private->meter_ctl->private_data;
- if (map.map_size != elem->channels ||
- map.meter_slots != private->num_meter_slots)
- return -EINVAL;
- }
- /* Validate the map size */
- if (map.map_size < 1 || map.map_size > 255 ||
- map.meter_slots < 1 || map.meter_slots > 255)
- return -EINVAL;
- /* Allocate and copy the map data */
- s16 *tmp_map __free(kfree) =
- memdup_array_user(arg->map, map.map_size, sizeof(s16));
- if (IS_ERR(tmp_map))
- return PTR_ERR(tmp_map);
- err = validate_meter_map(tmp_map, map.map_size, map.meter_slots);
- if (err < 0)
- return err;
- /* If the control doesn't exist, create it */
- if (!private->meter_ctl) {
- /* Allocate buffer for the map */
- s16 *new_map __free(kfree) =
- kmalloc_objs(s16, map.map_size);
- if (!new_map)
- return -ENOMEM;
- /* Allocate buffer for reading meter levels */
- __le32 *meter_levels __free(kfree) =
- kmalloc_array(map.meter_slots, sizeof(__le32),
- GFP_KERNEL);
- if (!meter_levels)
- return -ENOMEM;
- /* Create the Level Meter control */
- err = fcp_add_new_ctl(mixer, &fcp_meter_ctl, 0, map.map_size,
- "Level Meter", &private->meter_ctl);
- if (err < 0)
- return err;
- /* Success; save the pointers in private and don't free them */
- private->meter_level_map = new_map;
- private->meter_levels = meter_levels;
- private->num_meter_slots = map.meter_slots;
- new_map = NULL;
- meter_levels = NULL;
- }
- /* Install the new map */
- memcpy(private->meter_level_map, tmp_map, map.map_size * sizeof(s16));
- return 0;
- }
- /* Set the Level Meter labels */
- static int fcp_ioctl_set_meter_labels(struct usb_mixer_interface *mixer,
- struct fcp_meter_labels __user *arg)
- {
- struct fcp_meter_labels labels;
- struct fcp_data *private = mixer->private_data;
- unsigned int *tlv_data;
- unsigned int tlv_size, data_size;
- if (copy_from_user(&labels, arg, sizeof(labels)))
- return -EFAULT;
- /* Remove existing labels if size is zero */
- if (!labels.labels_size) {
- /* Clear TLV read/callback bits if labels were present */
- if (private->meter_labels_tlv) {
- private->meter_ctl->vd[0].access &=
- ~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
- snd_ctl_notify(mixer->chip->card,
- SNDRV_CTL_EVENT_MASK_INFO,
- &private->meter_ctl->id);
- }
- kfree(private->meter_labels_tlv);
- private->meter_labels_tlv = NULL;
- private->meter_labels_tlv_size = 0;
- return 0;
- }
- /* Validate size */
- if (labels.labels_size > 4096)
- return -EINVAL;
- /* Calculate padded data size */
- data_size = ALIGN(labels.labels_size, sizeof(unsigned int));
- /* Calculate total TLV size including header */
- tlv_size = sizeof(unsigned int) * 2 + data_size;
- /* Allocate, set up TLV header, and copy the labels data */
- tlv_data = kzalloc(tlv_size, GFP_KERNEL);
- if (!tlv_data)
- return -ENOMEM;
- tlv_data[0] = SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS;
- tlv_data[1] = data_size;
- if (copy_from_user(&tlv_data[2], arg->labels, labels.labels_size)) {
- kfree(tlv_data);
- return -EFAULT;
- }
- /* Set TLV read/callback bits if labels weren't present */
- if (!private->meter_labels_tlv) {
- private->meter_ctl->vd[0].access |=
- SNDRV_CTL_ELEM_ACCESS_TLV_READ |
- SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
- snd_ctl_notify(mixer->chip->card,
- SNDRV_CTL_EVENT_MASK_INFO,
- &private->meter_ctl->id);
- }
- /* Swap in the new labels */
- kfree(private->meter_labels_tlv);
- private->meter_labels_tlv = tlv_data;
- private->meter_labels_tlv_size = tlv_size;
- return 0;
- }
- static int fcp_hwdep_open(struct snd_hwdep *hw, struct file *file)
- {
- struct usb_mixer_interface *mixer = hw->private_data;
- struct fcp_data *private = mixer->private_data;
- if (!capable(CAP_SYS_RAWIO))
- return -EPERM;
- private->file = file;
- return 0;
- }
- static int fcp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
- unsigned int cmd, unsigned long arg)
- {
- struct usb_mixer_interface *mixer = hw->private_data;
- struct fcp_data *private = mixer->private_data;
- void __user *argp = (void __user *)arg;
- guard(mutex)(&private->mutex);
- switch (cmd) {
- case FCP_IOCTL_PVERSION:
- return put_user(FCP_HWDEP_VERSION,
- (int __user *)argp) ? -EFAULT : 0;
- break;
- case FCP_IOCTL_INIT:
- return fcp_ioctl_init(mixer, argp);
- case FCP_IOCTL_CMD:
- if (!private->init)
- return -EINVAL;
- return fcp_ioctl_cmd(mixer, argp);
- case FCP_IOCTL_SET_METER_MAP:
- if (!private->init)
- return -EINVAL;
- return fcp_ioctl_set_meter_map(mixer, argp);
- case FCP_IOCTL_SET_METER_LABELS:
- if (!private->init)
- return -EINVAL;
- if (!private->meter_ctl)
- return -EINVAL;
- return fcp_ioctl_set_meter_labels(mixer, argp);
- default:
- return -ENOIOCTLCMD;
- }
- /* not reached */
- }
- static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf,
- long count, loff_t *offset)
- {
- struct usb_mixer_interface *mixer = hw->private_data;
- struct fcp_data *private = mixer->private_data;
- long ret = 0;
- u32 event;
- if (count < sizeof(event))
- return -EINVAL;
- ret = wait_event_interruptible(private->notify.queue,
- private->notify.event);
- if (ret)
- return ret;
- scoped_guard(spinlock_irqsave, &private->notify.lock) {
- event = private->notify.event;
- private->notify.event = 0;
- }
- if (copy_to_user(buf, &event, sizeof(event)))
- return -EFAULT;
- return sizeof(event);
- }
- static __poll_t fcp_hwdep_poll(struct snd_hwdep *hw,
- struct file *file,
- poll_table *wait)
- {
- struct usb_mixer_interface *mixer = hw->private_data;
- struct fcp_data *private = mixer->private_data;
- __poll_t mask = 0;
- poll_wait(file, &private->notify.queue, wait);
- if (private->notify.event)
- mask |= EPOLLIN | EPOLLRDNORM;
- return mask;
- }
- static int fcp_hwdep_release(struct snd_hwdep *hw, struct file *file)
- {
- struct usb_mixer_interface *mixer = hw->private_data;
- struct fcp_data *private = mixer->private_data;
- if (!private)
- return 0;
- private->file = NULL;
- return 0;
- }
- static int fcp_hwdep_init(struct usb_mixer_interface *mixer)
- {
- struct snd_hwdep *hw;
- int err;
- err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw);
- if (err < 0)
- return err;
- hw->private_data = mixer;
- hw->exclusive = 1;
- hw->ops.open = fcp_hwdep_open;
- hw->ops.ioctl = fcp_hwdep_ioctl;
- hw->ops.ioctl_compat = fcp_hwdep_ioctl;
- hw->ops.read = fcp_hwdep_read;
- hw->ops.poll = fcp_hwdep_poll;
- hw->ops.release = fcp_hwdep_release;
- return 0;
- }
- /*** Cleanup ***/
- static void fcp_cleanup_urb(struct usb_mixer_interface *mixer)
- {
- if (!mixer->urb)
- return;
- usb_kill_urb(mixer->urb);
- kfree(mixer->urb->transfer_buffer);
- usb_free_urb(mixer->urb);
- mixer->urb = NULL;
- }
- static void fcp_private_free(struct usb_mixer_interface *mixer)
- {
- struct fcp_data *private = mixer->private_data;
- fcp_cleanup_urb(mixer);
- kfree(private->meter_level_map);
- kfree(private->meter_levels);
- kfree(private->meter_labels_tlv);
- kfree(private);
- mixer->private_data = NULL;
- }
- static void fcp_private_suspend(struct usb_mixer_interface *mixer)
- {
- fcp_cleanup_urb(mixer);
- }
- /*** Callbacks ***/
- static void fcp_notify(struct urb *urb)
- {
- struct usb_mixer_interface *mixer = urb->context;
- struct fcp_data *private = mixer->private_data;
- int len = urb->actual_length;
- int ustatus = urb->status;
- u32 data;
- if (ustatus != 0 || len != 8)
- goto requeue;
- data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
- /* Handle command acknowledgement */
- if (data & FCP_NOTIFY_ACK) {
- complete(&private->cmd_done);
- data &= ~FCP_NOTIFY_ACK;
- }
- if (data) {
- scoped_guard(spinlock_irqsave, &private->notify.lock) {
- private->notify.event |= data;
- }
- wake_up_interruptible(&private->notify.queue);
- }
- requeue:
- if (ustatus != -ENOENT &&
- ustatus != -ECONNRESET &&
- ustatus != -ESHUTDOWN) {
- urb->dev = mixer->chip->dev;
- usb_submit_urb(urb, GFP_ATOMIC);
- } else {
- complete(&private->cmd_done);
- }
- }
- /* Submit a URB to receive notifications from the device */
- static int fcp_init_notify(struct usb_mixer_interface *mixer)
- {
- struct usb_device *dev = mixer->chip->dev;
- struct fcp_data *private = mixer->private_data;
- unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress);
- void *transfer_buffer;
- int err;
- /* Already set up */
- if (mixer->urb)
- return 0;
- if (usb_pipe_type_check(dev, pipe))
- return -EINVAL;
- mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
- if (!mixer->urb)
- return -ENOMEM;
- transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
- if (!transfer_buffer) {
- usb_free_urb(mixer->urb);
- mixer->urb = NULL;
- return -ENOMEM;
- }
- usb_fill_int_urb(mixer->urb, dev, pipe,
- transfer_buffer, private->wMaxPacketSize,
- fcp_notify, mixer, private->bInterval);
- init_completion(&private->cmd_done);
- err = usb_submit_urb(mixer->urb, GFP_KERNEL);
- if (err) {
- usb_audio_err(mixer->chip,
- "%s: usb_submit_urb failed: %d\n",
- __func__, err);
- kfree(transfer_buffer);
- usb_free_urb(mixer->urb);
- mixer->urb = NULL;
- }
- return err;
- }
- /*** Initialisation ***/
- static int fcp_init(struct usb_mixer_interface *mixer,
- void *step0_resp, void *step2_resp)
- {
- struct fcp_data *private = mixer->private_data;
- struct usb_device *dev = mixer->chip->dev;
- int err;
- err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
- FCP_USB_REQ_STEP0,
- USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
- 0, private->bInterfaceNumber,
- step0_resp, private->step0_resp_size);
- if (err < 0)
- return err;
- err = fcp_init_notify(mixer);
- if (err < 0)
- return err;
- private->seq = 0;
- private->init = 1;
- err = fcp_usb(mixer, private->init1_opcode, NULL, 0, NULL, 0);
- if (err < 0)
- return err;
- err = fcp_usb(mixer, private->init2_opcode,
- NULL, 0, step2_resp, private->step2_resp_size);
- if (err < 0)
- return err;
- return 0;
- }
- static int fcp_init_private(struct usb_mixer_interface *mixer)
- {
- struct fcp_data *private =
- kzalloc_obj(struct fcp_data);
- if (!private)
- return -ENOMEM;
- mutex_init(&private->mutex);
- init_waitqueue_head(&private->notify.queue);
- spin_lock_init(&private->notify.lock);
- mixer->private_data = private;
- mixer->private_free = fcp_private_free;
- mixer->private_suspend = fcp_private_suspend;
- private->mixer = mixer;
- return 0;
- }
- /* Look through the interface descriptors for the Focusrite Control
- * interface (bInterfaceClass = 255 Vendor Specific Class) and set
- * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
- * in private
- */
- static int fcp_find_fc_interface(struct usb_mixer_interface *mixer)
- {
- struct snd_usb_audio *chip = mixer->chip;
- struct fcp_data *private = mixer->private_data;
- struct usb_host_config *config = chip->dev->actconfig;
- int i;
- for (i = 0; i < config->desc.bNumInterfaces; i++) {
- struct usb_interface *intf = config->interface[i];
- struct usb_interface_descriptor *desc =
- &intf->altsetting[0].desc;
- struct usb_endpoint_descriptor *epd;
- if (desc->bInterfaceClass != 255)
- continue;
- epd = get_endpoint(intf->altsetting, 0);
- private->bInterfaceNumber = desc->bInterfaceNumber;
- private->bEndpointAddress = usb_endpoint_num(epd);
- private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
- private->bInterval = epd->bInterval;
- return 0;
- }
- usb_audio_err(chip, "Focusrite vendor-specific interface not found\n");
- return -EINVAL;
- }
- int snd_fcp_init(struct usb_mixer_interface *mixer)
- {
- struct snd_usb_audio *chip = mixer->chip;
- int err;
- /* only use UAC_VERSION_2 */
- if (!mixer->protocol)
- return 0;
- err = fcp_init_private(mixer);
- if (err < 0)
- return err;
- err = fcp_find_fc_interface(mixer);
- if (err < 0)
- return err;
- err = fcp_hwdep_init(mixer);
- if (err < 0)
- return err;
- usb_audio_info(chip,
- "Focusrite Control Protocol Driver ready (pid=0x%04x); "
- "report any issues to "
- "https://github.com/geoffreybennett/fcp-support/issues",
- USB_ID_PRODUCT(chip->usb_id));
- return err;
- }
|