xref: /linux/sound/usb/fcp.c (revision a9e6060bb2a6cae6d43a98ec0794844ad01273d3)
146757a3eSGeoffrey D. Bennett // SPDX-License-Identifier: GPL-2.0
246757a3eSGeoffrey D. Bennett /*
346757a3eSGeoffrey D. Bennett  * Focusrite Control Protocol Driver for ALSA
446757a3eSGeoffrey D. Bennett  *
546757a3eSGeoffrey D. Bennett  * Copyright (c) 2024-2025 by Geoffrey D. Bennett <g at b4.vu>
646757a3eSGeoffrey D. Bennett  */
746757a3eSGeoffrey D. Bennett /*
846757a3eSGeoffrey D. Bennett  * DOC: Theory of Operation
946757a3eSGeoffrey D. Bennett  *
1046757a3eSGeoffrey D. Bennett  * The Focusrite Control Protocol (FCP) driver provides a minimal
1146757a3eSGeoffrey D. Bennett  * kernel interface that allows a user-space driver (primarily
1246757a3eSGeoffrey D. Bennett  * fcp-server) to communicate with Focusrite USB audio interfaces
1346757a3eSGeoffrey D. Bennett  * using their vendor-specific protocol. This protocol is used by
1446757a3eSGeoffrey D. Bennett  * Scarlett 2nd Gen, 3rd Gen, 4th Gen, Clarett USB, Clarett+, and
1546757a3eSGeoffrey D. Bennett  * Vocaster series devices.
1646757a3eSGeoffrey D. Bennett  *
1746757a3eSGeoffrey D. Bennett  * Unlike the existing scarlett2 driver which implements all controls
1846757a3eSGeoffrey D. Bennett  * in kernel space, this driver takes a lighter-weight approach by
1946757a3eSGeoffrey D. Bennett  * moving most functionality to user space. The only control
2046757a3eSGeoffrey D. Bennett  * implemented in kernel space is the Level Meter, since it requires
2146757a3eSGeoffrey D. Bennett  * frequent polling of volatile data.
2246757a3eSGeoffrey D. Bennett  *
2346757a3eSGeoffrey D. Bennett  * The driver provides an hwdep interface that allows the user-space
2446757a3eSGeoffrey D. Bennett  * driver to:
2546757a3eSGeoffrey D. Bennett  *  - Initialise the protocol
2646757a3eSGeoffrey D. Bennett  *  - Send arbitrary FCP commands to the device
2746757a3eSGeoffrey D. Bennett  *  - Receive notifications from the device
2846757a3eSGeoffrey D. Bennett  *  - Configure the Level Meter control
2946757a3eSGeoffrey D. Bennett  *
3046757a3eSGeoffrey D. Bennett  * Usage Flow
3146757a3eSGeoffrey D. Bennett  * ----------
3246757a3eSGeoffrey D. Bennett  * 1. Open the hwdep device (requires CAP_SYS_RAWIO)
3346757a3eSGeoffrey D. Bennett  * 2. Get protocol version using FCP_IOCTL_PVERSION
3446757a3eSGeoffrey D. Bennett  * 3. Initialise protocol using FCP_IOCTL_INIT
3546757a3eSGeoffrey D. Bennett  * 4. Send commands using FCP_IOCTL_CMD
3646757a3eSGeoffrey D. Bennett  * 5. Receive notifications using read()
3746757a3eSGeoffrey D. Bennett  * 6. Optionally set up the Level Meter control using
3846757a3eSGeoffrey D. Bennett  *    FCP_IOCTL_SET_METER_MAP
3946757a3eSGeoffrey D. Bennett  * 7. Optionally add labels to the Level Meter control using
4046757a3eSGeoffrey D. Bennett  *    FCP_IOCTL_SET_METER_LABELS
4146757a3eSGeoffrey D. Bennett  *
4246757a3eSGeoffrey D. Bennett  * Level Meter
4346757a3eSGeoffrey D. Bennett  * -----------
4446757a3eSGeoffrey D. Bennett  * The Level Meter is implemented as an ALSA control that provides
4546757a3eSGeoffrey D. Bennett  * real-time level monitoring. When the control is read, the driver
4646757a3eSGeoffrey D. Bennett  * requests the current meter levels from the device, translates the
4746757a3eSGeoffrey D. Bennett  * levels using the configured mapping, and returns the result to the
4846757a3eSGeoffrey D. Bennett  * user. The mapping between device meters and the ALSA control's
4946757a3eSGeoffrey D. Bennett  * channels is configured with FCP_IOCTL_SET_METER_MAP.
5046757a3eSGeoffrey D. Bennett  *
5146757a3eSGeoffrey D. Bennett  * Labels for the Level Meter channels can be set using
5246757a3eSGeoffrey D. Bennett  * FCP_IOCTL_SET_METER_LABELS and read by applications through the
5346757a3eSGeoffrey D. Bennett  * control's TLV data. The labels are transferred as a sequence of
5446757a3eSGeoffrey D. Bennett  * null-terminated strings.
5546757a3eSGeoffrey D. Bennett  */
5646757a3eSGeoffrey D. Bennett 
5746757a3eSGeoffrey D. Bennett #include <linux/slab.h>
5846757a3eSGeoffrey D. Bennett #include <linux/usb.h>
5946757a3eSGeoffrey D. Bennett 
6046757a3eSGeoffrey D. Bennett #include <sound/control.h>
6146757a3eSGeoffrey D. Bennett #include <sound/hwdep.h>
6246757a3eSGeoffrey D. Bennett #include <sound/tlv.h>
6346757a3eSGeoffrey D. Bennett 
6446757a3eSGeoffrey D. Bennett #include <uapi/sound/fcp.h>
6546757a3eSGeoffrey D. Bennett 
6646757a3eSGeoffrey D. Bennett #include "usbaudio.h"
6746757a3eSGeoffrey D. Bennett #include "mixer.h"
6846757a3eSGeoffrey D. Bennett #include "helper.h"
6946757a3eSGeoffrey D. Bennett 
7046757a3eSGeoffrey D. Bennett #include "fcp.h"
7146757a3eSGeoffrey D. Bennett 
7246757a3eSGeoffrey D. Bennett /* notify waiting to send to *file */
7346757a3eSGeoffrey D. Bennett struct fcp_notify {
7446757a3eSGeoffrey D. Bennett 	wait_queue_head_t queue;
7546757a3eSGeoffrey D. Bennett 	u32               event;
7646757a3eSGeoffrey D. Bennett 	spinlock_t        lock;
7746757a3eSGeoffrey D. Bennett };
7846757a3eSGeoffrey D. Bennett 
7946757a3eSGeoffrey D. Bennett struct fcp_data {
8046757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer;
8146757a3eSGeoffrey D. Bennett 
8246757a3eSGeoffrey D. Bennett 	struct mutex mutex;         /* serialise access to the device */
8346757a3eSGeoffrey D. Bennett 	struct completion cmd_done; /* wait for command completion */
8446757a3eSGeoffrey D. Bennett 	struct file *file;          /* hwdep file */
8546757a3eSGeoffrey D. Bennett 
8646757a3eSGeoffrey D. Bennett 	struct fcp_notify notify;
8746757a3eSGeoffrey D. Bennett 
8846757a3eSGeoffrey D. Bennett 	u8  bInterfaceNumber;
8946757a3eSGeoffrey D. Bennett 	u8  bEndpointAddress;
9046757a3eSGeoffrey D. Bennett 	u16 wMaxPacketSize;
9146757a3eSGeoffrey D. Bennett 	u8  bInterval;
9246757a3eSGeoffrey D. Bennett 
9346757a3eSGeoffrey D. Bennett 	uint16_t step0_resp_size;
9446757a3eSGeoffrey D. Bennett 	uint16_t step2_resp_size;
9546757a3eSGeoffrey D. Bennett 	uint32_t init1_opcode;
9646757a3eSGeoffrey D. Bennett 	uint32_t init2_opcode;
9746757a3eSGeoffrey D. Bennett 
9846757a3eSGeoffrey D. Bennett 	u8  init;
9946757a3eSGeoffrey D. Bennett 	u16 seq;
10046757a3eSGeoffrey D. Bennett 
10146757a3eSGeoffrey D. Bennett 	u8                   num_meter_slots;
10246757a3eSGeoffrey D. Bennett 	s16                 *meter_level_map;
103e7217011STakashi Iwai 	__le32              *meter_levels;
10446757a3eSGeoffrey D. Bennett 	struct snd_kcontrol *meter_ctl;
10546757a3eSGeoffrey D. Bennett 
10646757a3eSGeoffrey D. Bennett 	unsigned int *meter_labels_tlv;
10746757a3eSGeoffrey D. Bennett 	int           meter_labels_tlv_size;
10846757a3eSGeoffrey D. Bennett };
10946757a3eSGeoffrey D. Bennett 
11046757a3eSGeoffrey D. Bennett /*** USB Interactions ***/
11146757a3eSGeoffrey D. Bennett 
11246757a3eSGeoffrey D. Bennett /* FCP Command ACK notification bit */
11346757a3eSGeoffrey D. Bennett #define FCP_NOTIFY_ACK 1
11446757a3eSGeoffrey D. Bennett 
11546757a3eSGeoffrey D. Bennett /* Vendor-specific USB control requests */
11646757a3eSGeoffrey D. Bennett #define FCP_USB_REQ_STEP0  0
11746757a3eSGeoffrey D. Bennett #define FCP_USB_REQ_CMD_TX 2
11846757a3eSGeoffrey D. Bennett #define FCP_USB_REQ_CMD_RX 3
11946757a3eSGeoffrey D. Bennett 
12046757a3eSGeoffrey D. Bennett /* Focusrite Control Protocol opcodes that the kernel side needs to
12146757a3eSGeoffrey D. Bennett  * know about
12246757a3eSGeoffrey D. Bennett  */
12346757a3eSGeoffrey D. Bennett #define FCP_USB_REBOOT      0x00000003
12446757a3eSGeoffrey D. Bennett #define FCP_USB_GET_METER   0x00001001
12546757a3eSGeoffrey D. Bennett #define FCP_USB_FLASH_ERASE 0x00004002
12646757a3eSGeoffrey D. Bennett #define FCP_USB_FLASH_WRITE 0x00004004
12746757a3eSGeoffrey D. Bennett 
12846757a3eSGeoffrey D. Bennett #define FCP_USB_METER_LEVELS_GET_MAGIC 1
12946757a3eSGeoffrey D. Bennett 
13046757a3eSGeoffrey D. Bennett #define FCP_SEGMENT_APP_GOLD 0
13146757a3eSGeoffrey D. Bennett 
13246757a3eSGeoffrey D. Bennett /* Forward declarations */
13346757a3eSGeoffrey D. Bennett static int fcp_init(struct usb_mixer_interface *mixer,
13446757a3eSGeoffrey D. Bennett 		    void *step0_resp, void *step2_resp);
13546757a3eSGeoffrey D. Bennett 
13646757a3eSGeoffrey D. Bennett /* FCP command request/response format */
13746757a3eSGeoffrey D. Bennett struct fcp_usb_packet {
13846757a3eSGeoffrey D. Bennett 	__le32 opcode;
13946757a3eSGeoffrey D. Bennett 	__le16 size;
14046757a3eSGeoffrey D. Bennett 	__le16 seq;
14146757a3eSGeoffrey D. Bennett 	__le32 error;
14246757a3eSGeoffrey D. Bennett 	__le32 pad;
14346757a3eSGeoffrey D. Bennett 	u8 data[];
14446757a3eSGeoffrey D. Bennett };
14546757a3eSGeoffrey D. Bennett 
fcp_fill_request_header(struct fcp_data * private,struct fcp_usb_packet * req,u32 opcode,u16 req_size)14646757a3eSGeoffrey D. Bennett static void fcp_fill_request_header(struct fcp_data *private,
14746757a3eSGeoffrey D. Bennett 				    struct fcp_usb_packet *req,
14846757a3eSGeoffrey D. Bennett 				    u32 opcode, u16 req_size)
14946757a3eSGeoffrey D. Bennett {
15046757a3eSGeoffrey D. Bennett 	/* sequence must go up by 1 for each request */
15146757a3eSGeoffrey D. Bennett 	u16 seq = private->seq++;
15246757a3eSGeoffrey D. Bennett 
15346757a3eSGeoffrey D. Bennett 	req->opcode = cpu_to_le32(opcode);
15446757a3eSGeoffrey D. Bennett 	req->size = cpu_to_le16(req_size);
15546757a3eSGeoffrey D. Bennett 	req->seq = cpu_to_le16(seq);
15646757a3eSGeoffrey D. Bennett 	req->error = 0;
15746757a3eSGeoffrey D. Bennett 	req->pad = 0;
15846757a3eSGeoffrey D. Bennett }
15946757a3eSGeoffrey D. Bennett 
fcp_usb_tx(struct usb_device * dev,int interface,void * buf,u16 size)16046757a3eSGeoffrey D. Bennett static int fcp_usb_tx(struct usb_device *dev, int interface,
16146757a3eSGeoffrey D. Bennett 		      void *buf, u16 size)
16246757a3eSGeoffrey D. Bennett {
16346757a3eSGeoffrey D. Bennett 	return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0),
16446757a3eSGeoffrey D. Bennett 			FCP_USB_REQ_CMD_TX,
16546757a3eSGeoffrey D. Bennett 			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
16646757a3eSGeoffrey D. Bennett 			0, interface, buf, size);
16746757a3eSGeoffrey D. Bennett }
16846757a3eSGeoffrey D. Bennett 
fcp_usb_rx(struct usb_device * dev,int interface,void * buf,u16 size)16946757a3eSGeoffrey D. Bennett static int fcp_usb_rx(struct usb_device *dev, int interface,
17046757a3eSGeoffrey D. Bennett 		      void *buf, u16 size)
17146757a3eSGeoffrey D. Bennett {
17246757a3eSGeoffrey D. Bennett 	return snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
17346757a3eSGeoffrey D. Bennett 			FCP_USB_REQ_CMD_RX,
17446757a3eSGeoffrey D. Bennett 			USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
17546757a3eSGeoffrey D. Bennett 			0, interface, buf, size);
17646757a3eSGeoffrey D. Bennett }
17746757a3eSGeoffrey D. Bennett 
17846757a3eSGeoffrey D. Bennett /* Send an FCP command and get the response */
fcp_usb(struct usb_mixer_interface * mixer,u32 opcode,const void * req_data,u16 req_size,void * resp_data,u16 resp_size)17946757a3eSGeoffrey D. Bennett static int fcp_usb(struct usb_mixer_interface *mixer, u32 opcode,
18046757a3eSGeoffrey D. Bennett 		   const void *req_data, u16 req_size,
18146757a3eSGeoffrey D. Bennett 		   void *resp_data, u16 resp_size)
18246757a3eSGeoffrey D. Bennett {
18346757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
18446757a3eSGeoffrey D. Bennett 	struct usb_device *dev = mixer->chip->dev;
18546757a3eSGeoffrey D. Bennett 	struct fcp_usb_packet *req __free(kfree) = NULL;
18646757a3eSGeoffrey D. Bennett 	struct fcp_usb_packet *resp __free(kfree) = NULL;
18746757a3eSGeoffrey D. Bennett 	size_t req_buf_size = struct_size(req, data, req_size);
18846757a3eSGeoffrey D. Bennett 	size_t resp_buf_size = struct_size(resp, data, resp_size);
18946757a3eSGeoffrey D. Bennett 	int retries = 0;
19046757a3eSGeoffrey D. Bennett 	const int max_retries = 5;
19146757a3eSGeoffrey D. Bennett 	int err;
19246757a3eSGeoffrey D. Bennett 
19346757a3eSGeoffrey D. Bennett 	if (!mixer->urb)
19446757a3eSGeoffrey D. Bennett 		return -ENODEV;
19546757a3eSGeoffrey D. Bennett 
19646757a3eSGeoffrey D. Bennett 	req = kmalloc(req_buf_size, GFP_KERNEL);
19746757a3eSGeoffrey D. Bennett 	if (!req)
19846757a3eSGeoffrey D. Bennett 		return -ENOMEM;
19946757a3eSGeoffrey D. Bennett 
20046757a3eSGeoffrey D. Bennett 	resp = kmalloc(resp_buf_size, GFP_KERNEL);
20146757a3eSGeoffrey D. Bennett 	if (!resp)
20246757a3eSGeoffrey D. Bennett 		return -ENOMEM;
20346757a3eSGeoffrey D. Bennett 
20446757a3eSGeoffrey D. Bennett 	/* build request message */
20546757a3eSGeoffrey D. Bennett 	fcp_fill_request_header(private, req, opcode, req_size);
20646757a3eSGeoffrey D. Bennett 	if (req_size)
20746757a3eSGeoffrey D. Bennett 		memcpy(req->data, req_data, req_size);
20846757a3eSGeoffrey D. Bennett 
20946757a3eSGeoffrey D. Bennett 	/* send the request and retry on EPROTO */
21046757a3eSGeoffrey D. Bennett retry:
21146757a3eSGeoffrey D. Bennett 	err = fcp_usb_tx(dev, private->bInterfaceNumber, req, req_buf_size);
21246757a3eSGeoffrey D. Bennett 	if (err == -EPROTO && ++retries <= max_retries) {
21346757a3eSGeoffrey D. Bennett 		msleep(1 << (retries - 1));
21446757a3eSGeoffrey D. Bennett 		goto retry;
21546757a3eSGeoffrey D. Bennett 	}
21646757a3eSGeoffrey D. Bennett 
21746757a3eSGeoffrey D. Bennett 	if (err != req_buf_size) {
21846757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
21946757a3eSGeoffrey D. Bennett 			      "FCP request %08x failed: %d\n", opcode, err);
22046757a3eSGeoffrey D. Bennett 		return -EINVAL;
22146757a3eSGeoffrey D. Bennett 	}
22246757a3eSGeoffrey D. Bennett 
22346757a3eSGeoffrey D. Bennett 	if (!wait_for_completion_timeout(&private->cmd_done,
22446757a3eSGeoffrey D. Bennett 					 msecs_to_jiffies(1000))) {
22546757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
22646757a3eSGeoffrey D. Bennett 			      "FCP request %08x timed out\n", opcode);
22746757a3eSGeoffrey D. Bennett 
22846757a3eSGeoffrey D. Bennett 		return -ETIMEDOUT;
22946757a3eSGeoffrey D. Bennett 	}
23046757a3eSGeoffrey D. Bennett 
23146757a3eSGeoffrey D. Bennett 	/* send a second message to get the response */
23246757a3eSGeoffrey D. Bennett 	err = fcp_usb_rx(dev, private->bInterfaceNumber, resp, resp_buf_size);
23346757a3eSGeoffrey D. Bennett 
23446757a3eSGeoffrey D. Bennett 	/* validate the response */
23546757a3eSGeoffrey D. Bennett 
23646757a3eSGeoffrey D. Bennett 	if (err < 0) {
23746757a3eSGeoffrey D. Bennett 
23846757a3eSGeoffrey D. Bennett 		/* ESHUTDOWN and EPROTO are valid responses to a
23946757a3eSGeoffrey D. Bennett 		 * reboot request
24046757a3eSGeoffrey D. Bennett 		 */
24146757a3eSGeoffrey D. Bennett 		if (opcode == FCP_USB_REBOOT &&
24246757a3eSGeoffrey D. Bennett 		    (err == -ESHUTDOWN || err == -EPROTO))
24346757a3eSGeoffrey D. Bennett 			return 0;
24446757a3eSGeoffrey D. Bennett 
24546757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
24646757a3eSGeoffrey D. Bennett 			      "FCP read response %08x failed: %d\n",
24746757a3eSGeoffrey D. Bennett 			      opcode, err);
24846757a3eSGeoffrey D. Bennett 		return -EINVAL;
24946757a3eSGeoffrey D. Bennett 	}
25046757a3eSGeoffrey D. Bennett 
25146757a3eSGeoffrey D. Bennett 	if (err < sizeof(*resp)) {
25246757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
25346757a3eSGeoffrey D. Bennett 			      "FCP response %08x too short: %d\n",
25446757a3eSGeoffrey D. Bennett 			      opcode, err);
25546757a3eSGeoffrey D. Bennett 		return -EINVAL;
25646757a3eSGeoffrey D. Bennett 	}
25746757a3eSGeoffrey D. Bennett 
25846757a3eSGeoffrey D. Bennett 	if (req->seq != resp->seq) {
25946757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
26046757a3eSGeoffrey D. Bennett 			      "FCP response %08x seq mismatch %d/%d\n",
26146757a3eSGeoffrey D. Bennett 			      opcode,
26246757a3eSGeoffrey D. Bennett 			      le16_to_cpu(req->seq), le16_to_cpu(resp->seq));
26346757a3eSGeoffrey D. Bennett 		return -EINVAL;
26446757a3eSGeoffrey D. Bennett 	}
26546757a3eSGeoffrey D. Bennett 
26646757a3eSGeoffrey D. Bennett 	if (req->opcode != resp->opcode) {
26746757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
26846757a3eSGeoffrey D. Bennett 			      "FCP response %08x opcode mismatch %08x\n",
269f08cc80fSTakashi Iwai 			      opcode, le32_to_cpu(resp->opcode));
27046757a3eSGeoffrey D. Bennett 		return -EINVAL;
27146757a3eSGeoffrey D. Bennett 	}
27246757a3eSGeoffrey D. Bennett 
27346757a3eSGeoffrey D. Bennett 	if (resp->error) {
27446757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
27546757a3eSGeoffrey D. Bennett 			      "FCP response %08x error %d\n",
27646757a3eSGeoffrey D. Bennett 			      opcode, le32_to_cpu(resp->error));
27746757a3eSGeoffrey D. Bennett 		return -EINVAL;
27846757a3eSGeoffrey D. Bennett 	}
27946757a3eSGeoffrey D. Bennett 
28046757a3eSGeoffrey D. Bennett 	if (err != resp_buf_size) {
28146757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
28246757a3eSGeoffrey D. Bennett 			      "FCP response %08x buffer size mismatch %d/%zu\n",
28346757a3eSGeoffrey D. Bennett 			      opcode, err, resp_buf_size);
28446757a3eSGeoffrey D. Bennett 		return -EINVAL;
28546757a3eSGeoffrey D. Bennett 	}
28646757a3eSGeoffrey D. Bennett 
28746757a3eSGeoffrey D. Bennett 	if (resp_size != le16_to_cpu(resp->size)) {
28846757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
28946757a3eSGeoffrey D. Bennett 			      "FCP response %08x size mismatch %d/%d\n",
29046757a3eSGeoffrey D. Bennett 			      opcode, resp_size, le16_to_cpu(resp->size));
29146757a3eSGeoffrey D. Bennett 		return -EINVAL;
29246757a3eSGeoffrey D. Bennett 	}
29346757a3eSGeoffrey D. Bennett 
29446757a3eSGeoffrey D. Bennett 	if (resp_data && resp_size > 0)
29546757a3eSGeoffrey D. Bennett 		memcpy(resp_data, resp->data, resp_size);
29646757a3eSGeoffrey D. Bennett 
29746757a3eSGeoffrey D. Bennett 	return 0;
29846757a3eSGeoffrey D. Bennett }
29946757a3eSGeoffrey D. Bennett 
fcp_reinit(struct usb_mixer_interface * mixer)30046757a3eSGeoffrey D. Bennett static int fcp_reinit(struct usb_mixer_interface *mixer)
30146757a3eSGeoffrey D. Bennett {
30246757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
30346757a3eSGeoffrey D. Bennett 	void *step0_resp __free(kfree) = NULL;
30446757a3eSGeoffrey D. Bennett 	void *step2_resp __free(kfree) = NULL;
30546757a3eSGeoffrey D. Bennett 
30646757a3eSGeoffrey D. Bennett 	if (mixer->urb)
30746757a3eSGeoffrey D. Bennett 		return 0;
30846757a3eSGeoffrey D. Bennett 
30946757a3eSGeoffrey D. Bennett 	step0_resp = kmalloc(private->step0_resp_size, GFP_KERNEL);
31046757a3eSGeoffrey D. Bennett 	if (!step0_resp)
31146757a3eSGeoffrey D. Bennett 		return -ENOMEM;
31246757a3eSGeoffrey D. Bennett 	step2_resp = kmalloc(private->step2_resp_size, GFP_KERNEL);
31346757a3eSGeoffrey D. Bennett 	if (!step2_resp)
31446757a3eSGeoffrey D. Bennett 		return -ENOMEM;
31546757a3eSGeoffrey D. Bennett 
31646757a3eSGeoffrey D. Bennett 	return fcp_init(mixer, step0_resp, step2_resp);
31746757a3eSGeoffrey D. Bennett }
31846757a3eSGeoffrey D. Bennett 
31946757a3eSGeoffrey D. Bennett /*** Control Functions ***/
32046757a3eSGeoffrey D. Bennett 
32146757a3eSGeoffrey D. Bennett /* helper function to create a new control */
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)32246757a3eSGeoffrey D. Bennett static int fcp_add_new_ctl(struct usb_mixer_interface *mixer,
32346757a3eSGeoffrey D. Bennett 			   const struct snd_kcontrol_new *ncontrol,
32446757a3eSGeoffrey D. Bennett 			   int index, int channels, const char *name,
32546757a3eSGeoffrey D. Bennett 			   struct snd_kcontrol **kctl_return)
32646757a3eSGeoffrey D. Bennett {
32746757a3eSGeoffrey D. Bennett 	struct snd_kcontrol *kctl;
32846757a3eSGeoffrey D. Bennett 	struct usb_mixer_elem_info *elem;
32946757a3eSGeoffrey D. Bennett 	int err;
33046757a3eSGeoffrey D. Bennett 
33146757a3eSGeoffrey D. Bennett 	elem = kzalloc(sizeof(*elem), GFP_KERNEL);
33246757a3eSGeoffrey D. Bennett 	if (!elem)
33346757a3eSGeoffrey D. Bennett 		return -ENOMEM;
33446757a3eSGeoffrey D. Bennett 
33546757a3eSGeoffrey D. Bennett 	/* We set USB_MIXER_BESPOKEN type, so that the core USB mixer code
33646757a3eSGeoffrey D. Bennett 	 * ignores them for resume and other operations.
33746757a3eSGeoffrey D. Bennett 	 * Also, the head.id field is set to 0, as we don't use this field.
33846757a3eSGeoffrey D. Bennett 	 */
33946757a3eSGeoffrey D. Bennett 	elem->head.mixer = mixer;
34046757a3eSGeoffrey D. Bennett 	elem->control = index;
34146757a3eSGeoffrey D. Bennett 	elem->head.id = 0;
34246757a3eSGeoffrey D. Bennett 	elem->channels = channels;
34346757a3eSGeoffrey D. Bennett 	elem->val_type = USB_MIXER_BESPOKEN;
34446757a3eSGeoffrey D. Bennett 
34546757a3eSGeoffrey D. Bennett 	kctl = snd_ctl_new1(ncontrol, elem);
34646757a3eSGeoffrey D. Bennett 	if (!kctl) {
34746757a3eSGeoffrey D. Bennett 		kfree(elem);
34846757a3eSGeoffrey D. Bennett 		return -ENOMEM;
34946757a3eSGeoffrey D. Bennett 	}
35046757a3eSGeoffrey D. Bennett 	kctl->private_free = snd_usb_mixer_elem_free;
35146757a3eSGeoffrey D. Bennett 
35246757a3eSGeoffrey D. Bennett 	strscpy(kctl->id.name, name, sizeof(kctl->id.name));
35346757a3eSGeoffrey D. Bennett 
35446757a3eSGeoffrey D. Bennett 	err = snd_usb_mixer_add_control(&elem->head, kctl);
35546757a3eSGeoffrey D. Bennett 	if (err < 0)
35646757a3eSGeoffrey D. Bennett 		return err;
35746757a3eSGeoffrey D. Bennett 
35846757a3eSGeoffrey D. Bennett 	if (kctl_return)
35946757a3eSGeoffrey D. Bennett 		*kctl_return = kctl;
36046757a3eSGeoffrey D. Bennett 
36146757a3eSGeoffrey D. Bennett 	return 0;
36246757a3eSGeoffrey D. Bennett }
36346757a3eSGeoffrey D. Bennett 
36446757a3eSGeoffrey D. Bennett /*** Level Meter Control ***/
36546757a3eSGeoffrey D. Bennett 
fcp_meter_ctl_info(struct snd_kcontrol * kctl,struct snd_ctl_elem_info * uinfo)36646757a3eSGeoffrey D. Bennett static int fcp_meter_ctl_info(struct snd_kcontrol *kctl,
36746757a3eSGeoffrey D. Bennett 			      struct snd_ctl_elem_info *uinfo)
36846757a3eSGeoffrey D. Bennett {
36946757a3eSGeoffrey D. Bennett 	struct usb_mixer_elem_info *elem = kctl->private_data;
37046757a3eSGeoffrey D. Bennett 
37146757a3eSGeoffrey D. Bennett 	uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
37246757a3eSGeoffrey D. Bennett 	uinfo->count = elem->channels;
37346757a3eSGeoffrey D. Bennett 	uinfo->value.integer.min = 0;
37446757a3eSGeoffrey D. Bennett 	uinfo->value.integer.max = 4095;
37546757a3eSGeoffrey D. Bennett 	uinfo->value.integer.step = 1;
37646757a3eSGeoffrey D. Bennett 	return 0;
37746757a3eSGeoffrey D. Bennett }
37846757a3eSGeoffrey D. Bennett 
fcp_meter_ctl_get(struct snd_kcontrol * kctl,struct snd_ctl_elem_value * ucontrol)37946757a3eSGeoffrey D. Bennett static int fcp_meter_ctl_get(struct snd_kcontrol *kctl,
38046757a3eSGeoffrey D. Bennett 			     struct snd_ctl_elem_value *ucontrol)
38146757a3eSGeoffrey D. Bennett {
38246757a3eSGeoffrey D. Bennett 	struct usb_mixer_elem_info *elem = kctl->private_data;
38346757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = elem->head.mixer;
38446757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
38546757a3eSGeoffrey D. Bennett 	int num_meter_slots, resp_size;
386e7217011STakashi Iwai 	__le32 *resp = private->meter_levels;
38746757a3eSGeoffrey D. Bennett 	int i, err = 0;
38846757a3eSGeoffrey D. Bennett 
38946757a3eSGeoffrey D. Bennett 	struct {
39046757a3eSGeoffrey D. Bennett 		__le16 pad;
39146757a3eSGeoffrey D. Bennett 		__le16 num_meters;
39246757a3eSGeoffrey D. Bennett 		__le32 magic;
39346757a3eSGeoffrey D. Bennett 	} __packed req;
39446757a3eSGeoffrey D. Bennett 
39546757a3eSGeoffrey D. Bennett 	guard(mutex)(&private->mutex);
39646757a3eSGeoffrey D. Bennett 
39746757a3eSGeoffrey D. Bennett 	err = fcp_reinit(mixer);
39846757a3eSGeoffrey D. Bennett 	if (err < 0)
39946757a3eSGeoffrey D. Bennett 		return err;
40046757a3eSGeoffrey D. Bennett 
40146757a3eSGeoffrey D. Bennett 	num_meter_slots = private->num_meter_slots;
40246757a3eSGeoffrey D. Bennett 	resp_size = num_meter_slots * sizeof(u32);
40346757a3eSGeoffrey D. Bennett 
40446757a3eSGeoffrey D. Bennett 	req.pad = 0;
40546757a3eSGeoffrey D. Bennett 	req.num_meters = cpu_to_le16(num_meter_slots);
40646757a3eSGeoffrey D. Bennett 	req.magic = cpu_to_le32(FCP_USB_METER_LEVELS_GET_MAGIC);
40746757a3eSGeoffrey D. Bennett 	err = fcp_usb(mixer, FCP_USB_GET_METER,
40846757a3eSGeoffrey D. Bennett 		      &req, sizeof(req), resp, resp_size);
40946757a3eSGeoffrey D. Bennett 	if (err < 0)
41046757a3eSGeoffrey D. Bennett 		return err;
41146757a3eSGeoffrey D. Bennett 
41246757a3eSGeoffrey D. Bennett 	/* copy & translate from resp[] using meter_level_map[] */
41346757a3eSGeoffrey D. Bennett 	for (i = 0; i < elem->channels; i++) {
41446757a3eSGeoffrey D. Bennett 		int idx = private->meter_level_map[i];
41546757a3eSGeoffrey D. Bennett 		int value = idx < 0 ? 0 : le32_to_cpu(resp[idx]);
41646757a3eSGeoffrey D. Bennett 
41746757a3eSGeoffrey D. Bennett 		ucontrol->value.integer.value[i] = value;
41846757a3eSGeoffrey D. Bennett 	}
41946757a3eSGeoffrey D. Bennett 
42046757a3eSGeoffrey D. Bennett 	return 0;
42146757a3eSGeoffrey D. Bennett }
42246757a3eSGeoffrey D. Bennett 
fcp_meter_tlv_callback(struct snd_kcontrol * kctl,int op_flag,unsigned int size,unsigned int __user * tlv)42346757a3eSGeoffrey D. Bennett static int fcp_meter_tlv_callback(struct snd_kcontrol *kctl,
42446757a3eSGeoffrey D. Bennett 				  int op_flag, unsigned int size,
42546757a3eSGeoffrey D. Bennett 				  unsigned int __user *tlv)
42646757a3eSGeoffrey D. Bennett {
42746757a3eSGeoffrey D. Bennett 	struct usb_mixer_elem_info *elem = kctl->private_data;
42846757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = elem->head.mixer;
42946757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
43046757a3eSGeoffrey D. Bennett 
43146757a3eSGeoffrey D. Bennett 	guard(mutex)(&private->mutex);
43246757a3eSGeoffrey D. Bennett 
43346757a3eSGeoffrey D. Bennett 	if (op_flag == SNDRV_CTL_TLV_OP_READ) {
43446757a3eSGeoffrey D. Bennett 		if (private->meter_labels_tlv_size == 0)
43546757a3eSGeoffrey D. Bennett 			return 0;
43646757a3eSGeoffrey D. Bennett 
43746757a3eSGeoffrey D. Bennett 		if (size > private->meter_labels_tlv_size)
43846757a3eSGeoffrey D. Bennett 			size = private->meter_labels_tlv_size;
43946757a3eSGeoffrey D. Bennett 
44046757a3eSGeoffrey D. Bennett 		if (copy_to_user(tlv, private->meter_labels_tlv, size))
44146757a3eSGeoffrey D. Bennett 			return -EFAULT;
44246757a3eSGeoffrey D. Bennett 
44346757a3eSGeoffrey D. Bennett 		return size;
44446757a3eSGeoffrey D. Bennett 	}
44546757a3eSGeoffrey D. Bennett 
44646757a3eSGeoffrey D. Bennett 	return -EINVAL;
44746757a3eSGeoffrey D. Bennett }
44846757a3eSGeoffrey D. Bennett 
44946757a3eSGeoffrey D. Bennett static const struct snd_kcontrol_new fcp_meter_ctl = {
45046757a3eSGeoffrey D. Bennett 	.iface  = SNDRV_CTL_ELEM_IFACE_PCM,
45146757a3eSGeoffrey D. Bennett 	.access = SNDRV_CTL_ELEM_ACCESS_READ |
45246757a3eSGeoffrey D. Bennett 		  SNDRV_CTL_ELEM_ACCESS_VOLATILE,
45346757a3eSGeoffrey D. Bennett 	.info = fcp_meter_ctl_info,
45446757a3eSGeoffrey D. Bennett 	.get  = fcp_meter_ctl_get,
45546757a3eSGeoffrey D. Bennett 	.tlv  = { .c = fcp_meter_tlv_callback },
45646757a3eSGeoffrey D. Bennett };
45746757a3eSGeoffrey D. Bennett 
45846757a3eSGeoffrey D. Bennett /*** hwdep interface ***/
45946757a3eSGeoffrey D. Bennett 
46046757a3eSGeoffrey D. Bennett /* FCP initialisation */
fcp_ioctl_init(struct usb_mixer_interface * mixer,struct fcp_init __user * arg)46146757a3eSGeoffrey D. Bennett static int fcp_ioctl_init(struct usb_mixer_interface *mixer,
46246757a3eSGeoffrey D. Bennett 			  struct fcp_init __user *arg)
46346757a3eSGeoffrey D. Bennett {
46446757a3eSGeoffrey D. Bennett 	struct fcp_init init;
46546757a3eSGeoffrey D. Bennett 	struct usb_device *dev = mixer->chip->dev;
46646757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
46746757a3eSGeoffrey D. Bennett 	void *resp __free(kfree) = NULL;
46846757a3eSGeoffrey D. Bennett 	void *step2_resp;
46946757a3eSGeoffrey D. Bennett 	int err, buf_size;
47046757a3eSGeoffrey D. Bennett 
47146757a3eSGeoffrey D. Bennett 	if (usb_pipe_type_check(dev, usb_sndctrlpipe(dev, 0)))
47246757a3eSGeoffrey D. Bennett 		return -EINVAL;
47346757a3eSGeoffrey D. Bennett 
47446757a3eSGeoffrey D. Bennett 	/* Get initialisation parameters */
47546757a3eSGeoffrey D. Bennett 	if (copy_from_user(&init, arg, sizeof(init)))
47646757a3eSGeoffrey D. Bennett 		return -EFAULT;
47746757a3eSGeoffrey D. Bennett 
47846757a3eSGeoffrey D. Bennett 	/* Validate the response sizes */
47946757a3eSGeoffrey D. Bennett 	if (init.step0_resp_size < 1 ||
48046757a3eSGeoffrey D. Bennett 	    init.step0_resp_size > 255 ||
48146757a3eSGeoffrey D. Bennett 	    init.step2_resp_size < 1 ||
48246757a3eSGeoffrey D. Bennett 	    init.step2_resp_size > 255)
48346757a3eSGeoffrey D. Bennett 		return -EINVAL;
48446757a3eSGeoffrey D. Bennett 
48546757a3eSGeoffrey D. Bennett 	/* Allocate response buffer */
48646757a3eSGeoffrey D. Bennett 	buf_size = init.step0_resp_size + init.step2_resp_size;
48746757a3eSGeoffrey D. Bennett 
48846757a3eSGeoffrey D. Bennett 	resp = kmalloc(buf_size, GFP_KERNEL);
48946757a3eSGeoffrey D. Bennett 	if (!resp)
49046757a3eSGeoffrey D. Bennett 		return -ENOMEM;
49146757a3eSGeoffrey D. Bennett 
49246757a3eSGeoffrey D. Bennett 	private->step0_resp_size = init.step0_resp_size;
49346757a3eSGeoffrey D. Bennett 	private->step2_resp_size = init.step2_resp_size;
49446757a3eSGeoffrey D. Bennett 	private->init1_opcode = init.init1_opcode;
49546757a3eSGeoffrey D. Bennett 	private->init2_opcode = init.init2_opcode;
49646757a3eSGeoffrey D. Bennett 
49746757a3eSGeoffrey D. Bennett 	step2_resp = resp + private->step0_resp_size;
49846757a3eSGeoffrey D. Bennett 
49946757a3eSGeoffrey D. Bennett 	err = fcp_init(mixer, resp, step2_resp);
50046757a3eSGeoffrey D. Bennett 	if (err < 0)
50146757a3eSGeoffrey D. Bennett 		return err;
50246757a3eSGeoffrey D. Bennett 
50346757a3eSGeoffrey D. Bennett 	if (copy_to_user(arg->resp, resp, buf_size))
50446757a3eSGeoffrey D. Bennett 		return -EFAULT;
50546757a3eSGeoffrey D. Bennett 
50646757a3eSGeoffrey D. Bennett 	return 0;
50746757a3eSGeoffrey D. Bennett }
50846757a3eSGeoffrey D. Bennett 
50946757a3eSGeoffrey D. Bennett /* Check that the command is allowed
51046757a3eSGeoffrey D. Bennett  * Don't permit erasing/writing segment 0 (App_Gold)
51146757a3eSGeoffrey D. Bennett  */
fcp_validate_cmd(u32 opcode,void * data,u16 size)51246757a3eSGeoffrey D. Bennett static int fcp_validate_cmd(u32 opcode, void *data, u16 size)
51346757a3eSGeoffrey D. Bennett {
51446757a3eSGeoffrey D. Bennett 	if (opcode == FCP_USB_FLASH_ERASE) {
51546757a3eSGeoffrey D. Bennett 		struct {
51646757a3eSGeoffrey D. Bennett 			__le32 segment_num;
51746757a3eSGeoffrey D. Bennett 			__le32 pad;
51846757a3eSGeoffrey D. Bennett 		} __packed *req = data;
51946757a3eSGeoffrey D. Bennett 
52046757a3eSGeoffrey D. Bennett 		if (size != sizeof(*req))
52146757a3eSGeoffrey D. Bennett 			return -EINVAL;
52246757a3eSGeoffrey D. Bennett 
52346757a3eSGeoffrey D. Bennett 		if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
52446757a3eSGeoffrey D. Bennett 			return -EPERM;
52546757a3eSGeoffrey D. Bennett 
52646757a3eSGeoffrey D. Bennett 		if (req->pad != 0)
52746757a3eSGeoffrey D. Bennett 			return -EINVAL;
52846757a3eSGeoffrey D. Bennett 
52946757a3eSGeoffrey D. Bennett 	} else if (opcode == FCP_USB_FLASH_WRITE) {
53046757a3eSGeoffrey D. Bennett 		struct {
53146757a3eSGeoffrey D. Bennett 			__le32 segment_num;
53246757a3eSGeoffrey D. Bennett 			__le32 offset;
53346757a3eSGeoffrey D. Bennett 			__le32 pad;
53446757a3eSGeoffrey D. Bennett 			u8 data[];
53546757a3eSGeoffrey D. Bennett 		} __packed *req = data;
53646757a3eSGeoffrey D. Bennett 
53746757a3eSGeoffrey D. Bennett 		if (size < sizeof(*req))
53846757a3eSGeoffrey D. Bennett 			return -EINVAL;
53946757a3eSGeoffrey D. Bennett 
54046757a3eSGeoffrey D. Bennett 		if (le32_to_cpu(req->segment_num) == FCP_SEGMENT_APP_GOLD)
54146757a3eSGeoffrey D. Bennett 			return -EPERM;
54246757a3eSGeoffrey D. Bennett 
54346757a3eSGeoffrey D. Bennett 		if (req->pad != 0)
54446757a3eSGeoffrey D. Bennett 			return -EINVAL;
54546757a3eSGeoffrey D. Bennett 	}
54646757a3eSGeoffrey D. Bennett 
54746757a3eSGeoffrey D. Bennett 	return 0;
54846757a3eSGeoffrey D. Bennett }
54946757a3eSGeoffrey D. Bennett 
55046757a3eSGeoffrey D. Bennett /* Execute an FCP command specified by the user */
fcp_ioctl_cmd(struct usb_mixer_interface * mixer,struct fcp_cmd __user * arg)55146757a3eSGeoffrey D. Bennett static int fcp_ioctl_cmd(struct usb_mixer_interface *mixer,
55246757a3eSGeoffrey D. Bennett 			 struct fcp_cmd __user *arg)
55346757a3eSGeoffrey D. Bennett {
55446757a3eSGeoffrey D. Bennett 	struct fcp_cmd cmd;
55546757a3eSGeoffrey D. Bennett 	int err, buf_size;
55646757a3eSGeoffrey D. Bennett 	void *data __free(kfree) = NULL;
55746757a3eSGeoffrey D. Bennett 
55846757a3eSGeoffrey D. Bennett 	/* get opcode and request/response size */
55946757a3eSGeoffrey D. Bennett 	if (copy_from_user(&cmd, arg, sizeof(cmd)))
56046757a3eSGeoffrey D. Bennett 		return -EFAULT;
56146757a3eSGeoffrey D. Bennett 
56246757a3eSGeoffrey D. Bennett 	/* validate request and response sizes */
56346757a3eSGeoffrey D. Bennett 	if (cmd.req_size > 4096 || cmd.resp_size > 4096)
56446757a3eSGeoffrey D. Bennett 		return -EINVAL;
56546757a3eSGeoffrey D. Bennett 
56646757a3eSGeoffrey D. Bennett 	/* reinit if needed */
56746757a3eSGeoffrey D. Bennett 	err = fcp_reinit(mixer);
56846757a3eSGeoffrey D. Bennett 	if (err < 0)
56946757a3eSGeoffrey D. Bennett 		return err;
57046757a3eSGeoffrey D. Bennett 
57146757a3eSGeoffrey D. Bennett 	/* allocate request/response buffer */
57246757a3eSGeoffrey D. Bennett 	buf_size = max(cmd.req_size, cmd.resp_size);
57346757a3eSGeoffrey D. Bennett 
57446757a3eSGeoffrey D. Bennett 	if (buf_size > 0) {
57546757a3eSGeoffrey D. Bennett 		data = kmalloc(buf_size, GFP_KERNEL);
57646757a3eSGeoffrey D. Bennett 		if (!data)
57746757a3eSGeoffrey D. Bennett 			return -ENOMEM;
57846757a3eSGeoffrey D. Bennett 	}
57946757a3eSGeoffrey D. Bennett 
58046757a3eSGeoffrey D. Bennett 	/* copy request from user */
58146757a3eSGeoffrey D. Bennett 	if (cmd.req_size > 0)
58246757a3eSGeoffrey D. Bennett 		if (copy_from_user(data, arg->data, cmd.req_size))
58346757a3eSGeoffrey D. Bennett 			return -EFAULT;
58446757a3eSGeoffrey D. Bennett 
58546757a3eSGeoffrey D. Bennett 	/* check that the command is allowed */
58646757a3eSGeoffrey D. Bennett 	err = fcp_validate_cmd(cmd.opcode, data, cmd.req_size);
58746757a3eSGeoffrey D. Bennett 	if (err < 0)
58846757a3eSGeoffrey D. Bennett 		return err;
58946757a3eSGeoffrey D. Bennett 
59046757a3eSGeoffrey D. Bennett 	/* send request, get response */
59146757a3eSGeoffrey D. Bennett 	err = fcp_usb(mixer, cmd.opcode,
59246757a3eSGeoffrey D. Bennett 		      data, cmd.req_size, data, cmd.resp_size);
59346757a3eSGeoffrey D. Bennett 	if (err < 0)
59446757a3eSGeoffrey D. Bennett 		return err;
59546757a3eSGeoffrey D. Bennett 
59646757a3eSGeoffrey D. Bennett 	/* copy response to user */
59746757a3eSGeoffrey D. Bennett 	if (cmd.resp_size > 0)
59846757a3eSGeoffrey D. Bennett 		if (copy_to_user(arg->data, data, cmd.resp_size))
59946757a3eSGeoffrey D. Bennett 			return -EFAULT;
60046757a3eSGeoffrey D. Bennett 
60146757a3eSGeoffrey D. Bennett 	return 0;
60246757a3eSGeoffrey D. Bennett }
60346757a3eSGeoffrey D. Bennett 
60446757a3eSGeoffrey D. Bennett /* Validate the Level Meter map passed by the user */
validate_meter_map(const s16 * map,int map_size,int meter_slots)60546757a3eSGeoffrey D. Bennett static int validate_meter_map(const s16 *map, int map_size, int meter_slots)
60646757a3eSGeoffrey D. Bennett {
60746757a3eSGeoffrey D. Bennett 	int i;
60846757a3eSGeoffrey D. Bennett 
60946757a3eSGeoffrey D. Bennett 	for (i = 0; i < map_size; i++)
61046757a3eSGeoffrey D. Bennett 		if (map[i] < -1 || map[i] >= meter_slots)
61146757a3eSGeoffrey D. Bennett 			return -EINVAL;
61246757a3eSGeoffrey D. Bennett 
61346757a3eSGeoffrey D. Bennett 	return 0;
61446757a3eSGeoffrey D. Bennett }
61546757a3eSGeoffrey D. Bennett 
61646757a3eSGeoffrey D. Bennett /* Set the Level Meter map and add the control */
fcp_ioctl_set_meter_map(struct usb_mixer_interface * mixer,struct fcp_meter_map __user * arg)61746757a3eSGeoffrey D. Bennett static int fcp_ioctl_set_meter_map(struct usb_mixer_interface *mixer,
61846757a3eSGeoffrey D. Bennett 				   struct fcp_meter_map __user *arg)
61946757a3eSGeoffrey D. Bennett {
62046757a3eSGeoffrey D. Bennett 	struct fcp_meter_map map;
62146757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
62246757a3eSGeoffrey D. Bennett 	s16 *tmp_map __free(kfree) = NULL;
62346757a3eSGeoffrey D. Bennett 	int err;
62446757a3eSGeoffrey D. Bennett 
62546757a3eSGeoffrey D. Bennett 	if (copy_from_user(&map, arg, sizeof(map)))
62646757a3eSGeoffrey D. Bennett 		return -EFAULT;
62746757a3eSGeoffrey D. Bennett 
62846757a3eSGeoffrey D. Bennett 	/* Don't allow changing the map size or meter slots once set */
62946757a3eSGeoffrey D. Bennett 	if (private->meter_ctl) {
63046757a3eSGeoffrey D. Bennett 		struct usb_mixer_elem_info *elem =
63146757a3eSGeoffrey D. Bennett 			private->meter_ctl->private_data;
63246757a3eSGeoffrey D. Bennett 
63346757a3eSGeoffrey D. Bennett 		if (map.map_size != elem->channels ||
63446757a3eSGeoffrey D. Bennett 		    map.meter_slots != private->num_meter_slots)
63546757a3eSGeoffrey D. Bennett 			return -EINVAL;
63646757a3eSGeoffrey D. Bennett 	}
63746757a3eSGeoffrey D. Bennett 
63846757a3eSGeoffrey D. Bennett 	/* Validate the map size */
63946757a3eSGeoffrey D. Bennett 	if (map.map_size < 1 || map.map_size > 255 ||
64046757a3eSGeoffrey D. Bennett 	    map.meter_slots < 1 || map.meter_slots > 255)
64146757a3eSGeoffrey D. Bennett 		return -EINVAL;
64246757a3eSGeoffrey D. Bennett 
64346757a3eSGeoffrey D. Bennett 	/* Allocate and copy the map data */
64446757a3eSGeoffrey D. Bennett 	tmp_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
64546757a3eSGeoffrey D. Bennett 	if (!tmp_map)
64646757a3eSGeoffrey D. Bennett 		return -ENOMEM;
64746757a3eSGeoffrey D. Bennett 
64846757a3eSGeoffrey D. Bennett 	if (copy_from_user(tmp_map, arg->map, map.map_size * sizeof(s16)))
64946757a3eSGeoffrey D. Bennett 		return -EFAULT;
65046757a3eSGeoffrey D. Bennett 
65146757a3eSGeoffrey D. Bennett 	err = validate_meter_map(tmp_map, map.map_size, map.meter_slots);
65246757a3eSGeoffrey D. Bennett 	if (err < 0)
65346757a3eSGeoffrey D. Bennett 		return err;
65446757a3eSGeoffrey D. Bennett 
65546757a3eSGeoffrey D. Bennett 	/* If the control doesn't exist, create it */
65646757a3eSGeoffrey D. Bennett 	if (!private->meter_ctl) {
65746757a3eSGeoffrey D. Bennett 		s16 *new_map __free(kfree) = NULL;
658e7217011STakashi Iwai 		__le32 *meter_levels __free(kfree) = NULL;
65946757a3eSGeoffrey D. Bennett 
66046757a3eSGeoffrey D. Bennett 		/* Allocate buffer for the map */
66146757a3eSGeoffrey D. Bennett 		new_map = kmalloc_array(map.map_size, sizeof(s16), GFP_KERNEL);
66246757a3eSGeoffrey D. Bennett 		if (!new_map)
66346757a3eSGeoffrey D. Bennett 			return -ENOMEM;
66446757a3eSGeoffrey D. Bennett 
66546757a3eSGeoffrey D. Bennett 		/* Allocate buffer for reading meter levels */
666e7217011STakashi Iwai 		meter_levels = kmalloc_array(map.meter_slots, sizeof(__le32),
66746757a3eSGeoffrey D. Bennett 					     GFP_KERNEL);
66846757a3eSGeoffrey D. Bennett 		if (!meter_levels)
66946757a3eSGeoffrey D. Bennett 			return -ENOMEM;
67046757a3eSGeoffrey D. Bennett 
67146757a3eSGeoffrey D. Bennett 		/* Create the Level Meter control */
67246757a3eSGeoffrey D. Bennett 		err = fcp_add_new_ctl(mixer, &fcp_meter_ctl, 0, map.map_size,
67346757a3eSGeoffrey D. Bennett 				      "Level Meter", &private->meter_ctl);
67446757a3eSGeoffrey D. Bennett 		if (err < 0)
67546757a3eSGeoffrey D. Bennett 			return err;
67646757a3eSGeoffrey D. Bennett 
67746757a3eSGeoffrey D. Bennett 		/* Success; save the pointers in private and don't free them */
67846757a3eSGeoffrey D. Bennett 		private->meter_level_map = new_map;
67946757a3eSGeoffrey D. Bennett 		private->meter_levels = meter_levels;
68046757a3eSGeoffrey D. Bennett 		private->num_meter_slots = map.meter_slots;
68146757a3eSGeoffrey D. Bennett 		new_map = NULL;
68246757a3eSGeoffrey D. Bennett 		meter_levels = NULL;
68346757a3eSGeoffrey D. Bennett 	}
68446757a3eSGeoffrey D. Bennett 
68546757a3eSGeoffrey D. Bennett 	/* Install the new map */
68646757a3eSGeoffrey D. Bennett 	memcpy(private->meter_level_map, tmp_map, map.map_size * sizeof(s16));
68746757a3eSGeoffrey D. Bennett 
68846757a3eSGeoffrey D. Bennett 	return 0;
68946757a3eSGeoffrey D. Bennett }
69046757a3eSGeoffrey D. Bennett 
69146757a3eSGeoffrey D. Bennett /* Set the Level Meter labels */
fcp_ioctl_set_meter_labels(struct usb_mixer_interface * mixer,struct fcp_meter_labels __user * arg)69246757a3eSGeoffrey D. Bennett static int fcp_ioctl_set_meter_labels(struct usb_mixer_interface *mixer,
69346757a3eSGeoffrey D. Bennett 				      struct fcp_meter_labels __user *arg)
69446757a3eSGeoffrey D. Bennett {
69546757a3eSGeoffrey D. Bennett 	struct fcp_meter_labels labels;
69646757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
69746757a3eSGeoffrey D. Bennett 	unsigned int *tlv_data;
69846757a3eSGeoffrey D. Bennett 	unsigned int tlv_size, data_size;
69946757a3eSGeoffrey D. Bennett 
70046757a3eSGeoffrey D. Bennett 	if (copy_from_user(&labels, arg, sizeof(labels)))
70146757a3eSGeoffrey D. Bennett 		return -EFAULT;
70246757a3eSGeoffrey D. Bennett 
70346757a3eSGeoffrey D. Bennett 	/* Remove existing labels if size is zero */
70446757a3eSGeoffrey D. Bennett 	if (!labels.labels_size) {
70546757a3eSGeoffrey D. Bennett 
70646757a3eSGeoffrey D. Bennett 		/* Clear TLV read/callback bits if labels were present */
70746757a3eSGeoffrey D. Bennett 		if (private->meter_labels_tlv) {
70846757a3eSGeoffrey D. Bennett 			private->meter_ctl->vd[0].access &=
70946757a3eSGeoffrey D. Bennett 				~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
71046757a3eSGeoffrey D. Bennett 				  SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
71146757a3eSGeoffrey D. Bennett 			snd_ctl_notify(mixer->chip->card,
71246757a3eSGeoffrey D. Bennett 				       SNDRV_CTL_EVENT_MASK_INFO,
71346757a3eSGeoffrey D. Bennett 				       &private->meter_ctl->id);
71446757a3eSGeoffrey D. Bennett 		}
71546757a3eSGeoffrey D. Bennett 
71646757a3eSGeoffrey D. Bennett 		kfree(private->meter_labels_tlv);
71746757a3eSGeoffrey D. Bennett 		private->meter_labels_tlv = NULL;
71846757a3eSGeoffrey D. Bennett 		private->meter_labels_tlv_size = 0;
71946757a3eSGeoffrey D. Bennett 
72046757a3eSGeoffrey D. Bennett 		return 0;
72146757a3eSGeoffrey D. Bennett 	}
72246757a3eSGeoffrey D. Bennett 
72346757a3eSGeoffrey D. Bennett 	/* Validate size */
72446757a3eSGeoffrey D. Bennett 	if (labels.labels_size > 4096)
72546757a3eSGeoffrey D. Bennett 		return -EINVAL;
72646757a3eSGeoffrey D. Bennett 
72746757a3eSGeoffrey D. Bennett 	/* Calculate padded data size */
72846757a3eSGeoffrey D. Bennett 	data_size = ALIGN(labels.labels_size, sizeof(unsigned int));
72946757a3eSGeoffrey D. Bennett 
73046757a3eSGeoffrey D. Bennett 	/* Calculate total TLV size including header */
73146757a3eSGeoffrey D. Bennett 	tlv_size = sizeof(unsigned int) * 2 + data_size;
73246757a3eSGeoffrey D. Bennett 
73346757a3eSGeoffrey D. Bennett 	/* Allocate, set up TLV header, and copy the labels data */
73446757a3eSGeoffrey D. Bennett 	tlv_data = kzalloc(tlv_size, GFP_KERNEL);
73546757a3eSGeoffrey D. Bennett 	if (!tlv_data)
73646757a3eSGeoffrey D. Bennett 		return -ENOMEM;
73746757a3eSGeoffrey D. Bennett 	tlv_data[0] = SNDRV_CTL_TLVT_FCP_CHANNEL_LABELS;
73846757a3eSGeoffrey D. Bennett 	tlv_data[1] = data_size;
73946757a3eSGeoffrey D. Bennett 	if (copy_from_user(&tlv_data[2], arg->labels, labels.labels_size)) {
74046757a3eSGeoffrey D. Bennett 		kfree(tlv_data);
74146757a3eSGeoffrey D. Bennett 		return -EFAULT;
74246757a3eSGeoffrey D. Bennett 	}
74346757a3eSGeoffrey D. Bennett 
74446757a3eSGeoffrey D. Bennett 	/* Set TLV read/callback bits if labels weren't present */
74546757a3eSGeoffrey D. Bennett 	if (!private->meter_labels_tlv) {
74646757a3eSGeoffrey D. Bennett 		private->meter_ctl->vd[0].access |=
74746757a3eSGeoffrey D. Bennett 			SNDRV_CTL_ELEM_ACCESS_TLV_READ |
74846757a3eSGeoffrey D. Bennett 			SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
74946757a3eSGeoffrey D. Bennett 		snd_ctl_notify(mixer->chip->card,
75046757a3eSGeoffrey D. Bennett 			       SNDRV_CTL_EVENT_MASK_INFO,
75146757a3eSGeoffrey D. Bennett 			       &private->meter_ctl->id);
75246757a3eSGeoffrey D. Bennett 	}
75346757a3eSGeoffrey D. Bennett 
75446757a3eSGeoffrey D. Bennett 	/* Swap in the new labels */
75546757a3eSGeoffrey D. Bennett 	kfree(private->meter_labels_tlv);
75646757a3eSGeoffrey D. Bennett 	private->meter_labels_tlv = tlv_data;
75746757a3eSGeoffrey D. Bennett 	private->meter_labels_tlv_size = tlv_size;
75846757a3eSGeoffrey D. Bennett 
75946757a3eSGeoffrey D. Bennett 	return 0;
76046757a3eSGeoffrey D. Bennett }
76146757a3eSGeoffrey D. Bennett 
fcp_hwdep_open(struct snd_hwdep * hw,struct file * file)76246757a3eSGeoffrey D. Bennett static int fcp_hwdep_open(struct snd_hwdep *hw, struct file *file)
76346757a3eSGeoffrey D. Bennett {
76446757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = hw->private_data;
76546757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
76646757a3eSGeoffrey D. Bennett 
76746757a3eSGeoffrey D. Bennett 	if (!capable(CAP_SYS_RAWIO))
76846757a3eSGeoffrey D. Bennett 		return -EPERM;
76946757a3eSGeoffrey D. Bennett 
77046757a3eSGeoffrey D. Bennett 	private->file = file;
77146757a3eSGeoffrey D. Bennett 
77246757a3eSGeoffrey D. Bennett 	return 0;
77346757a3eSGeoffrey D. Bennett }
77446757a3eSGeoffrey D. Bennett 
fcp_hwdep_ioctl(struct snd_hwdep * hw,struct file * file,unsigned int cmd,unsigned long arg)77546757a3eSGeoffrey D. Bennett static int fcp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
77646757a3eSGeoffrey D. Bennett 			   unsigned int cmd, unsigned long arg)
77746757a3eSGeoffrey D. Bennett {
77846757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = hw->private_data;
77946757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
78046757a3eSGeoffrey D. Bennett 	void __user *argp = (void __user *)arg;
78146757a3eSGeoffrey D. Bennett 
78246757a3eSGeoffrey D. Bennett 	guard(mutex)(&private->mutex);
78346757a3eSGeoffrey D. Bennett 
78446757a3eSGeoffrey D. Bennett 	switch (cmd) {
78546757a3eSGeoffrey D. Bennett 
78646757a3eSGeoffrey D. Bennett 	case FCP_IOCTL_PVERSION:
78746757a3eSGeoffrey D. Bennett 		return put_user(FCP_HWDEP_VERSION,
78846757a3eSGeoffrey D. Bennett 				(int __user *)argp) ? -EFAULT : 0;
78946757a3eSGeoffrey D. Bennett 		break;
79046757a3eSGeoffrey D. Bennett 
79146757a3eSGeoffrey D. Bennett 	case FCP_IOCTL_INIT:
79246757a3eSGeoffrey D. Bennett 		return fcp_ioctl_init(mixer, argp);
79346757a3eSGeoffrey D. Bennett 
79446757a3eSGeoffrey D. Bennett 	case FCP_IOCTL_CMD:
79546757a3eSGeoffrey D. Bennett 		if (!private->init)
79646757a3eSGeoffrey D. Bennett 			return -EINVAL;
79746757a3eSGeoffrey D. Bennett 		return fcp_ioctl_cmd(mixer, argp);
79846757a3eSGeoffrey D. Bennett 
79946757a3eSGeoffrey D. Bennett 	case FCP_IOCTL_SET_METER_MAP:
80046757a3eSGeoffrey D. Bennett 		if (!private->init)
80146757a3eSGeoffrey D. Bennett 			return -EINVAL;
80246757a3eSGeoffrey D. Bennett 		return fcp_ioctl_set_meter_map(mixer, argp);
80346757a3eSGeoffrey D. Bennett 
80446757a3eSGeoffrey D. Bennett 	case FCP_IOCTL_SET_METER_LABELS:
80546757a3eSGeoffrey D. Bennett 		if (!private->init)
80646757a3eSGeoffrey D. Bennett 			return -EINVAL;
80746757a3eSGeoffrey D. Bennett 		if (!private->meter_ctl)
80846757a3eSGeoffrey D. Bennett 			return -EINVAL;
80946757a3eSGeoffrey D. Bennett 		return fcp_ioctl_set_meter_labels(mixer, argp);
81046757a3eSGeoffrey D. Bennett 
81146757a3eSGeoffrey D. Bennett 	default:
81246757a3eSGeoffrey D. Bennett 		return -ENOIOCTLCMD;
81346757a3eSGeoffrey D. Bennett 	}
81446757a3eSGeoffrey D. Bennett 
81546757a3eSGeoffrey D. Bennett 	/* not reached */
81646757a3eSGeoffrey D. Bennett }
81746757a3eSGeoffrey D. Bennett 
fcp_hwdep_read(struct snd_hwdep * hw,char __user * buf,long count,loff_t * offset)818f95719b0SStephen Rothwell static long fcp_hwdep_read(struct snd_hwdep *hw, char __user *buf,
819f95719b0SStephen Rothwell 			   long count, loff_t *offset)
82046757a3eSGeoffrey D. Bennett {
82146757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = hw->private_data;
82246757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
82346757a3eSGeoffrey D. Bennett 	unsigned long flags;
824f95719b0SStephen Rothwell 	long ret = 0;
82546757a3eSGeoffrey D. Bennett 	u32 event;
82646757a3eSGeoffrey D. Bennett 
82746757a3eSGeoffrey D. Bennett 	if (count < sizeof(event))
82846757a3eSGeoffrey D. Bennett 		return -EINVAL;
82946757a3eSGeoffrey D. Bennett 
83046757a3eSGeoffrey D. Bennett 	ret = wait_event_interruptible(private->notify.queue,
83146757a3eSGeoffrey D. Bennett 				       private->notify.event);
83246757a3eSGeoffrey D. Bennett 	if (ret)
83346757a3eSGeoffrey D. Bennett 		return ret;
83446757a3eSGeoffrey D. Bennett 
83546757a3eSGeoffrey D. Bennett 	spin_lock_irqsave(&private->notify.lock, flags);
83646757a3eSGeoffrey D. Bennett 	event = private->notify.event;
83746757a3eSGeoffrey D. Bennett 	private->notify.event = 0;
83846757a3eSGeoffrey D. Bennett 	spin_unlock_irqrestore(&private->notify.lock, flags);
83946757a3eSGeoffrey D. Bennett 
84046757a3eSGeoffrey D. Bennett 	if (copy_to_user(buf, &event, sizeof(event)))
84146757a3eSGeoffrey D. Bennett 		return -EFAULT;
84246757a3eSGeoffrey D. Bennett 
84346757a3eSGeoffrey D. Bennett 	return sizeof(event);
84446757a3eSGeoffrey D. Bennett }
84546757a3eSGeoffrey D. Bennett 
fcp_hwdep_poll(struct snd_hwdep * hw,struct file * file,poll_table * wait)8460a8f5f46STakashi Iwai static __poll_t fcp_hwdep_poll(struct snd_hwdep *hw,
84746757a3eSGeoffrey D. Bennett 			       struct file *file,
84846757a3eSGeoffrey D. Bennett 			       poll_table *wait)
84946757a3eSGeoffrey D. Bennett {
85046757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = hw->private_data;
85146757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
8520a8f5f46STakashi Iwai 	__poll_t mask = 0;
85346757a3eSGeoffrey D. Bennett 
85446757a3eSGeoffrey D. Bennett 	poll_wait(file, &private->notify.queue, wait);
85546757a3eSGeoffrey D. Bennett 
85646757a3eSGeoffrey D. Bennett 	if (private->notify.event)
8570a8f5f46STakashi Iwai 		mask |= EPOLLIN | EPOLLRDNORM;
85846757a3eSGeoffrey D. Bennett 
85946757a3eSGeoffrey D. Bennett 	return mask;
86046757a3eSGeoffrey D. Bennett }
86146757a3eSGeoffrey D. Bennett 
fcp_hwdep_release(struct snd_hwdep * hw,struct file * file)86246757a3eSGeoffrey D. Bennett static int fcp_hwdep_release(struct snd_hwdep *hw, struct file *file)
86346757a3eSGeoffrey D. Bennett {
86446757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = hw->private_data;
86546757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
86646757a3eSGeoffrey D. Bennett 
86746757a3eSGeoffrey D. Bennett 	if (!private)
86846757a3eSGeoffrey D. Bennett 		return 0;
86946757a3eSGeoffrey D. Bennett 
87046757a3eSGeoffrey D. Bennett 	private->file = NULL;
87146757a3eSGeoffrey D. Bennett 
87246757a3eSGeoffrey D. Bennett 	return 0;
87346757a3eSGeoffrey D. Bennett }
87446757a3eSGeoffrey D. Bennett 
fcp_hwdep_init(struct usb_mixer_interface * mixer)87546757a3eSGeoffrey D. Bennett static int fcp_hwdep_init(struct usb_mixer_interface *mixer)
87646757a3eSGeoffrey D. Bennett {
87746757a3eSGeoffrey D. Bennett 	struct snd_hwdep *hw;
87846757a3eSGeoffrey D. Bennett 	int err;
87946757a3eSGeoffrey D. Bennett 
88046757a3eSGeoffrey D. Bennett 	err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw);
88146757a3eSGeoffrey D. Bennett 	if (err < 0)
88246757a3eSGeoffrey D. Bennett 		return err;
88346757a3eSGeoffrey D. Bennett 
88446757a3eSGeoffrey D. Bennett 	hw->private_data = mixer;
88546757a3eSGeoffrey D. Bennett 	hw->exclusive = 1;
88646757a3eSGeoffrey D. Bennett 	hw->ops.open = fcp_hwdep_open;
88746757a3eSGeoffrey D. Bennett 	hw->ops.ioctl = fcp_hwdep_ioctl;
88846757a3eSGeoffrey D. Bennett 	hw->ops.ioctl_compat = fcp_hwdep_ioctl;
88946757a3eSGeoffrey D. Bennett 	hw->ops.read = fcp_hwdep_read;
89046757a3eSGeoffrey D. Bennett 	hw->ops.poll = fcp_hwdep_poll;
89146757a3eSGeoffrey D. Bennett 	hw->ops.release = fcp_hwdep_release;
89246757a3eSGeoffrey D. Bennett 
89346757a3eSGeoffrey D. Bennett 	return 0;
89446757a3eSGeoffrey D. Bennett }
89546757a3eSGeoffrey D. Bennett 
89646757a3eSGeoffrey D. Bennett /*** Cleanup ***/
89746757a3eSGeoffrey D. Bennett 
fcp_cleanup_urb(struct usb_mixer_interface * mixer)89846757a3eSGeoffrey D. Bennett static void fcp_cleanup_urb(struct usb_mixer_interface *mixer)
89946757a3eSGeoffrey D. Bennett {
90046757a3eSGeoffrey D. Bennett 	if (!mixer->urb)
90146757a3eSGeoffrey D. Bennett 		return;
90246757a3eSGeoffrey D. Bennett 
90346757a3eSGeoffrey D. Bennett 	usb_kill_urb(mixer->urb);
90446757a3eSGeoffrey D. Bennett 	kfree(mixer->urb->transfer_buffer);
90546757a3eSGeoffrey D. Bennett 	usb_free_urb(mixer->urb);
90646757a3eSGeoffrey D. Bennett 	mixer->urb = NULL;
90746757a3eSGeoffrey D. Bennett }
90846757a3eSGeoffrey D. Bennett 
fcp_private_free(struct usb_mixer_interface * mixer)90946757a3eSGeoffrey D. Bennett static void fcp_private_free(struct usb_mixer_interface *mixer)
91046757a3eSGeoffrey D. Bennett {
91146757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
91246757a3eSGeoffrey D. Bennett 
91346757a3eSGeoffrey D. Bennett 	fcp_cleanup_urb(mixer);
91446757a3eSGeoffrey D. Bennett 
91546757a3eSGeoffrey D. Bennett 	kfree(private->meter_level_map);
91646757a3eSGeoffrey D. Bennett 	kfree(private->meter_levels);
91746757a3eSGeoffrey D. Bennett 	kfree(private->meter_labels_tlv);
91846757a3eSGeoffrey D. Bennett 	kfree(private);
91946757a3eSGeoffrey D. Bennett 	mixer->private_data = NULL;
92046757a3eSGeoffrey D. Bennett }
92146757a3eSGeoffrey D. Bennett 
fcp_private_suspend(struct usb_mixer_interface * mixer)92246757a3eSGeoffrey D. Bennett static void fcp_private_suspend(struct usb_mixer_interface *mixer)
92346757a3eSGeoffrey D. Bennett {
92446757a3eSGeoffrey D. Bennett 	fcp_cleanup_urb(mixer);
92546757a3eSGeoffrey D. Bennett }
92646757a3eSGeoffrey D. Bennett 
92746757a3eSGeoffrey D. Bennett /*** Callbacks ***/
92846757a3eSGeoffrey D. Bennett 
fcp_notify(struct urb * urb)92946757a3eSGeoffrey D. Bennett static void fcp_notify(struct urb *urb)
93046757a3eSGeoffrey D. Bennett {
93146757a3eSGeoffrey D. Bennett 	struct usb_mixer_interface *mixer = urb->context;
93246757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
93346757a3eSGeoffrey D. Bennett 	int len = urb->actual_length;
93446757a3eSGeoffrey D. Bennett 	int ustatus = urb->status;
93546757a3eSGeoffrey D. Bennett 	u32 data;
93646757a3eSGeoffrey D. Bennett 
93746757a3eSGeoffrey D. Bennett 	if (ustatus != 0 || len != 8)
93846757a3eSGeoffrey D. Bennett 		goto requeue;
93946757a3eSGeoffrey D. Bennett 
94046757a3eSGeoffrey D. Bennett 	data = le32_to_cpu(*(__le32 *)urb->transfer_buffer);
94146757a3eSGeoffrey D. Bennett 
94246757a3eSGeoffrey D. Bennett 	/* Handle command acknowledgement */
94346757a3eSGeoffrey D. Bennett 	if (data & FCP_NOTIFY_ACK) {
94446757a3eSGeoffrey D. Bennett 		complete(&private->cmd_done);
94546757a3eSGeoffrey D. Bennett 		data &= ~FCP_NOTIFY_ACK;
94646757a3eSGeoffrey D. Bennett 	}
94746757a3eSGeoffrey D. Bennett 
94846757a3eSGeoffrey D. Bennett 	if (data) {
94946757a3eSGeoffrey D. Bennett 		unsigned long flags;
95046757a3eSGeoffrey D. Bennett 
95146757a3eSGeoffrey D. Bennett 		spin_lock_irqsave(&private->notify.lock, flags);
95246757a3eSGeoffrey D. Bennett 		private->notify.event |= data;
95346757a3eSGeoffrey D. Bennett 		spin_unlock_irqrestore(&private->notify.lock, flags);
95446757a3eSGeoffrey D. Bennett 
95546757a3eSGeoffrey D. Bennett 		wake_up_interruptible(&private->notify.queue);
95646757a3eSGeoffrey D. Bennett 	}
95746757a3eSGeoffrey D. Bennett 
95846757a3eSGeoffrey D. Bennett requeue:
95946757a3eSGeoffrey D. Bennett 	if (ustatus != -ENOENT &&
96046757a3eSGeoffrey D. Bennett 	    ustatus != -ECONNRESET &&
96146757a3eSGeoffrey D. Bennett 	    ustatus != -ESHUTDOWN) {
96246757a3eSGeoffrey D. Bennett 		urb->dev = mixer->chip->dev;
96346757a3eSGeoffrey D. Bennett 		usb_submit_urb(urb, GFP_ATOMIC);
96446757a3eSGeoffrey D. Bennett 	} else {
96546757a3eSGeoffrey D. Bennett 		complete(&private->cmd_done);
96646757a3eSGeoffrey D. Bennett 	}
96746757a3eSGeoffrey D. Bennett }
96846757a3eSGeoffrey D. Bennett 
96946757a3eSGeoffrey D. Bennett /* Submit a URB to receive notifications from the device */
fcp_init_notify(struct usb_mixer_interface * mixer)97046757a3eSGeoffrey D. Bennett static int fcp_init_notify(struct usb_mixer_interface *mixer)
97146757a3eSGeoffrey D. Bennett {
97246757a3eSGeoffrey D. Bennett 	struct usb_device *dev = mixer->chip->dev;
97346757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
97446757a3eSGeoffrey D. Bennett 	unsigned int pipe = usb_rcvintpipe(dev, private->bEndpointAddress);
97546757a3eSGeoffrey D. Bennett 	void *transfer_buffer;
97646757a3eSGeoffrey D. Bennett 	int err;
97746757a3eSGeoffrey D. Bennett 
97846757a3eSGeoffrey D. Bennett 	/* Already set up */
97946757a3eSGeoffrey D. Bennett 	if (mixer->urb)
98046757a3eSGeoffrey D. Bennett 		return 0;
98146757a3eSGeoffrey D. Bennett 
98246757a3eSGeoffrey D. Bennett 	if (usb_pipe_type_check(dev, pipe))
98346757a3eSGeoffrey D. Bennett 		return -EINVAL;
98446757a3eSGeoffrey D. Bennett 
98546757a3eSGeoffrey D. Bennett 	mixer->urb = usb_alloc_urb(0, GFP_KERNEL);
98646757a3eSGeoffrey D. Bennett 	if (!mixer->urb)
98746757a3eSGeoffrey D. Bennett 		return -ENOMEM;
98846757a3eSGeoffrey D. Bennett 
98946757a3eSGeoffrey D. Bennett 	transfer_buffer = kmalloc(private->wMaxPacketSize, GFP_KERNEL);
99046757a3eSGeoffrey D. Bennett 	if (!transfer_buffer) {
99146757a3eSGeoffrey D. Bennett 		usb_free_urb(mixer->urb);
99246757a3eSGeoffrey D. Bennett 		mixer->urb = NULL;
99346757a3eSGeoffrey D. Bennett 		return -ENOMEM;
99446757a3eSGeoffrey D. Bennett 	}
99546757a3eSGeoffrey D. Bennett 
99646757a3eSGeoffrey D. Bennett 	usb_fill_int_urb(mixer->urb, dev, pipe,
99746757a3eSGeoffrey D. Bennett 			 transfer_buffer, private->wMaxPacketSize,
99846757a3eSGeoffrey D. Bennett 			 fcp_notify, mixer, private->bInterval);
99946757a3eSGeoffrey D. Bennett 
100046757a3eSGeoffrey D. Bennett 	init_completion(&private->cmd_done);
100146757a3eSGeoffrey D. Bennett 
100246757a3eSGeoffrey D. Bennett 	err = usb_submit_urb(mixer->urb, GFP_KERNEL);
100346757a3eSGeoffrey D. Bennett 	if (err) {
100446757a3eSGeoffrey D. Bennett 		usb_audio_err(mixer->chip,
100546757a3eSGeoffrey D. Bennett 			      "%s: usb_submit_urb failed: %d\n",
100646757a3eSGeoffrey D. Bennett 			      __func__, err);
100746757a3eSGeoffrey D. Bennett 		kfree(transfer_buffer);
100846757a3eSGeoffrey D. Bennett 		usb_free_urb(mixer->urb);
100946757a3eSGeoffrey D. Bennett 		mixer->urb = NULL;
101046757a3eSGeoffrey D. Bennett 	}
101146757a3eSGeoffrey D. Bennett 
101246757a3eSGeoffrey D. Bennett 	return err;
101346757a3eSGeoffrey D. Bennett }
101446757a3eSGeoffrey D. Bennett 
101546757a3eSGeoffrey D. Bennett /*** Initialisation ***/
101646757a3eSGeoffrey D. Bennett 
fcp_init(struct usb_mixer_interface * mixer,void * step0_resp,void * step2_resp)101746757a3eSGeoffrey D. Bennett static int fcp_init(struct usb_mixer_interface *mixer,
101846757a3eSGeoffrey D. Bennett 		    void *step0_resp, void *step2_resp)
101946757a3eSGeoffrey D. Bennett {
102046757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
102146757a3eSGeoffrey D. Bennett 	struct usb_device *dev = mixer->chip->dev;
102246757a3eSGeoffrey D. Bennett 	int err;
102346757a3eSGeoffrey D. Bennett 
102446757a3eSGeoffrey D. Bennett 	err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0),
102546757a3eSGeoffrey D. Bennett 		FCP_USB_REQ_STEP0,
102646757a3eSGeoffrey D. Bennett 		USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
102746757a3eSGeoffrey D. Bennett 		0, private->bInterfaceNumber,
102846757a3eSGeoffrey D. Bennett 		step0_resp, private->step0_resp_size);
102946757a3eSGeoffrey D. Bennett 	if (err < 0)
103046757a3eSGeoffrey D. Bennett 		return err;
103146757a3eSGeoffrey D. Bennett 
103246757a3eSGeoffrey D. Bennett 	err = fcp_init_notify(mixer);
103346757a3eSGeoffrey D. Bennett 	if (err < 0)
103446757a3eSGeoffrey D. Bennett 		return err;
103546757a3eSGeoffrey D. Bennett 
103646757a3eSGeoffrey D. Bennett 	private->seq = 0;
103746757a3eSGeoffrey D. Bennett 	private->init = 1;
103846757a3eSGeoffrey D. Bennett 
103946757a3eSGeoffrey D. Bennett 	err = fcp_usb(mixer, private->init1_opcode, NULL, 0, NULL, 0);
104046757a3eSGeoffrey D. Bennett 	if (err < 0)
104146757a3eSGeoffrey D. Bennett 		return err;
104246757a3eSGeoffrey D. Bennett 
104346757a3eSGeoffrey D. Bennett 	err = fcp_usb(mixer, private->init2_opcode,
104446757a3eSGeoffrey D. Bennett 		      NULL, 0, step2_resp, private->step2_resp_size);
104546757a3eSGeoffrey D. Bennett 	if (err < 0)
104646757a3eSGeoffrey D. Bennett 		return err;
104746757a3eSGeoffrey D. Bennett 
104846757a3eSGeoffrey D. Bennett 	return 0;
104946757a3eSGeoffrey D. Bennett }
105046757a3eSGeoffrey D. Bennett 
fcp_init_private(struct usb_mixer_interface * mixer)105146757a3eSGeoffrey D. Bennett static int fcp_init_private(struct usb_mixer_interface *mixer)
105246757a3eSGeoffrey D. Bennett {
105346757a3eSGeoffrey D. Bennett 	struct fcp_data *private =
105446757a3eSGeoffrey D. Bennett 		kzalloc(sizeof(struct fcp_data), GFP_KERNEL);
105546757a3eSGeoffrey D. Bennett 
105646757a3eSGeoffrey D. Bennett 	if (!private)
105746757a3eSGeoffrey D. Bennett 		return -ENOMEM;
105846757a3eSGeoffrey D. Bennett 
105946757a3eSGeoffrey D. Bennett 	mutex_init(&private->mutex);
106046757a3eSGeoffrey D. Bennett 	init_waitqueue_head(&private->notify.queue);
106146757a3eSGeoffrey D. Bennett 	spin_lock_init(&private->notify.lock);
106246757a3eSGeoffrey D. Bennett 
106346757a3eSGeoffrey D. Bennett 	mixer->private_data = private;
106446757a3eSGeoffrey D. Bennett 	mixer->private_free = fcp_private_free;
106546757a3eSGeoffrey D. Bennett 	mixer->private_suspend = fcp_private_suspend;
106646757a3eSGeoffrey D. Bennett 
106746757a3eSGeoffrey D. Bennett 	private->mixer = mixer;
106846757a3eSGeoffrey D. Bennett 
106946757a3eSGeoffrey D. Bennett 	return 0;
107046757a3eSGeoffrey D. Bennett }
107146757a3eSGeoffrey D. Bennett 
107246757a3eSGeoffrey D. Bennett /* Look through the interface descriptors for the Focusrite Control
107346757a3eSGeoffrey D. Bennett  * interface (bInterfaceClass = 255 Vendor Specific Class) and set
107446757a3eSGeoffrey D. Bennett  * bInterfaceNumber, bEndpointAddress, wMaxPacketSize, and bInterval
107546757a3eSGeoffrey D. Bennett  * in private
107646757a3eSGeoffrey D. Bennett  */
fcp_find_fc_interface(struct usb_mixer_interface * mixer)107746757a3eSGeoffrey D. Bennett static int fcp_find_fc_interface(struct usb_mixer_interface *mixer)
107846757a3eSGeoffrey D. Bennett {
107946757a3eSGeoffrey D. Bennett 	struct snd_usb_audio *chip = mixer->chip;
108046757a3eSGeoffrey D. Bennett 	struct fcp_data *private = mixer->private_data;
108146757a3eSGeoffrey D. Bennett 	struct usb_host_config *config = chip->dev->actconfig;
108246757a3eSGeoffrey D. Bennett 	int i;
108346757a3eSGeoffrey D. Bennett 
108446757a3eSGeoffrey D. Bennett 	for (i = 0; i < config->desc.bNumInterfaces; i++) {
108546757a3eSGeoffrey D. Bennett 		struct usb_interface *intf = config->interface[i];
108646757a3eSGeoffrey D. Bennett 		struct usb_interface_descriptor *desc =
108746757a3eSGeoffrey D. Bennett 			&intf->altsetting[0].desc;
108846757a3eSGeoffrey D. Bennett 		struct usb_endpoint_descriptor *epd;
108946757a3eSGeoffrey D. Bennett 
109046757a3eSGeoffrey D. Bennett 		if (desc->bInterfaceClass != 255)
109146757a3eSGeoffrey D. Bennett 			continue;
109246757a3eSGeoffrey D. Bennett 
109346757a3eSGeoffrey D. Bennett 		epd = get_endpoint(intf->altsetting, 0);
109446757a3eSGeoffrey D. Bennett 		private->bInterfaceNumber = desc->bInterfaceNumber;
1095*1c116e55SChen Ni 		private->bEndpointAddress = usb_endpoint_num(epd);
109646757a3eSGeoffrey D. Bennett 		private->wMaxPacketSize = le16_to_cpu(epd->wMaxPacketSize);
109746757a3eSGeoffrey D. Bennett 		private->bInterval = epd->bInterval;
109846757a3eSGeoffrey D. Bennett 		return 0;
109946757a3eSGeoffrey D. Bennett 	}
110046757a3eSGeoffrey D. Bennett 
110146757a3eSGeoffrey D. Bennett 	usb_audio_err(chip, "Focusrite vendor-specific interface not found\n");
110246757a3eSGeoffrey D. Bennett 	return -EINVAL;
110346757a3eSGeoffrey D. Bennett }
110446757a3eSGeoffrey D. Bennett 
snd_fcp_init(struct usb_mixer_interface * mixer)110546757a3eSGeoffrey D. Bennett int snd_fcp_init(struct usb_mixer_interface *mixer)
110646757a3eSGeoffrey D. Bennett {
110746757a3eSGeoffrey D. Bennett 	struct snd_usb_audio *chip = mixer->chip;
110846757a3eSGeoffrey D. Bennett 	int err;
110946757a3eSGeoffrey D. Bennett 
111046757a3eSGeoffrey D. Bennett 	/* only use UAC_VERSION_2 */
111146757a3eSGeoffrey D. Bennett 	if (!mixer->protocol)
111246757a3eSGeoffrey D. Bennett 		return 0;
111346757a3eSGeoffrey D. Bennett 
111446757a3eSGeoffrey D. Bennett 	err = fcp_init_private(mixer);
111546757a3eSGeoffrey D. Bennett 	if (err < 0)
111646757a3eSGeoffrey D. Bennett 		return err;
111746757a3eSGeoffrey D. Bennett 
111846757a3eSGeoffrey D. Bennett 	err = fcp_find_fc_interface(mixer);
111946757a3eSGeoffrey D. Bennett 	if (err < 0)
112046757a3eSGeoffrey D. Bennett 		return err;
112146757a3eSGeoffrey D. Bennett 
112246757a3eSGeoffrey D. Bennett 	err = fcp_hwdep_init(mixer);
112346757a3eSGeoffrey D. Bennett 	if (err < 0)
112446757a3eSGeoffrey D. Bennett 		return err;
112546757a3eSGeoffrey D. Bennett 
112646757a3eSGeoffrey D. Bennett 	usb_audio_info(chip,
112746757a3eSGeoffrey D. Bennett 		"Focusrite Control Protocol Driver ready (pid=0x%04x); "
112846757a3eSGeoffrey D. Bennett 		"report any issues to "
112946757a3eSGeoffrey D. Bennett 		"https://github.com/geoffreybennett/fcp-support/issues",
113046757a3eSGeoffrey D. Bennett 		USB_ID_PRODUCT(chip->usb_id));
113146757a3eSGeoffrey D. Bennett 
113246757a3eSGeoffrey D. Bennett 	return err;
113346757a3eSGeoffrey D. Bennett }
1134