[PATCH RFC] ALSA: scarlett2: Add ioctls for user-space access

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



In order to support functions such as firmware upgrade from
user-space, add ioctls for submitting arbitrary proprietary requests
through scarlett2_usb() and requesting/releasing exclusive access.
---

Hi Takashi,

I recently figured how to update the firmware on Scarlett Gen 2+
devices. I think the best way to implement this is with an ioctl
giving access to the scarlett2_usb() function from user-space, plus
two ioctls to request/release exclusive access.

Does something like this seem reasonable?

What ioctl magic/command values should I use?

Should lock/unlock be separate, or one ioctl takes an argument for
which operation to do?

Thanks,
Geoffrey.

 MAINTAINERS                        |  1 +
 include/uapi/sound/scarlett_gen2.h | 28 +++++++++
 sound/usb/mixer_scarlett_gen2.c    | 94 +++++++++++++++++++++++++++++-
 3 files changed, 121 insertions(+), 2 deletions(-)
 create mode 100644 include/uapi/sound/scarlett_gen2.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 4cc6bf79fdd8..25e5e40f7118 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8066,6 +8066,7 @@ M:	Geoffrey D. Bennett <g@xxxxx>
 L:	alsa-devel@xxxxxxxxxxxxxxxx (moderated for non-subscribers)
 S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+F:	include/uapi/sound/scarlett_gen2.h
 F:	sound/usb/mixer_scarlett_gen2.c
 
 FORCEDETH GIGABIT ETHERNET DRIVER
diff --git a/include/uapi/sound/scarlett_gen2.h b/include/uapi/sound/scarlett_gen2.h
new file mode 100644
index 000000000000..0b51a9754ba2
--- /dev/null
+++ b/include/uapi/sound/scarlett_gen2.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ *   Focusrite Scarlett Gen 2/3 and Clarett USB/Clarett+ Driver for ALSA
+ *
+ *   Copyright (c) 2023 by Geoffrey D. Bennett <g at b4.vu>
+ */
+#ifndef __UAPI_SOUND_SCARLETT_GEN2_H
+#define __UAPI_SOUND_SCARLETT_GEN2_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Send a proprietary format request to the interface */
+struct snd_scarlett_gen2_usb_cmd {
+	u32   cmd;
+	void *req_data;
+	u16   req_size;
+	void *resp_data;
+	u16   resp_size;
+};
+
+#define SCARLETT2_IOCTL_USB_CMD _IOWR('S', 0x60, struct snd_scarlett_gen2_usb_cmd)
+
+/* Request/release exclusive access */
+#define SCARLETT2_IOCTL_LOCK _IO('S', 0x61)
+#define SCARLETT2_IOCTL_UNLOCK _IO('S', 0x62)
+
+#endif /* __UAPI_SOUND_SCARLETT_GEN2_H */
diff --git a/sound/usb/mixer_scarlett_gen2.c b/sound/usb/mixer_scarlett_gen2.c
index ffd398f26d2c..f1ef87b54813 100644
--- a/sound/usb/mixer_scarlett_gen2.c
+++ b/sound/usb/mixer_scarlett_gen2.c
@@ -144,6 +144,9 @@
 
 #include <sound/control.h>
 #include <sound/tlv.h>
+#include <sound/hwdep.h>
+
+#include <uapi/sound/scarlett_gen2.h>
 
 #include "usbaudio.h"
 #include "mixer.h"
@@ -4262,10 +4265,84 @@ static int snd_scarlett_gen2_controls_create(
 	return 0;
 }
 
+static int scarlett_gen2_ioctl_usb_cmd(struct snd_hwdep *hw, unsigned long arg)
+{
+	struct scarlett2_data *private = hw->private_data;
+	struct snd_scarlett_gen2_usb_cmd usb_cmd;
+	int err;
+	void *req_data = NULL;
+	void *resp_data = NULL;
+
+	// get cmd & req/resp buffers
+	if (copy_from_user(&usb_cmd, (void __user *)arg, sizeof(usb_cmd)))
+		return -EFAULT;
+
+	// allocate request buffer, copy data from user
+	if (usb_cmd.req_size > 0) {
+		req_data = kmalloc(usb_cmd.req_size, GFP_KERNEL);
+		if (!req_data) {
+			err = -ENOMEM;
+			goto exit;
+		}
+		if (copy_from_user(req_data, usb_cmd.req_data,
+				   usb_cmd.req_size)) {
+			err = -EFAULT;
+			goto exit;
+		}
+	}
+
+	// allocate response buffer
+	if (usb_cmd.resp_size > 0) {
+		resp_data = kmalloc(usb_cmd.resp_size, GFP_KERNEL);
+		if (!resp_data) {
+			err = -ENOMEM;
+			goto exit;
+		}
+	}
+
+	// send request, get response
+	err = scarlett2_usb(private->mixer, usb_cmd.cmd,
+			    req_data, usb_cmd.req_size,
+			    resp_data, usb_cmd.resp_size);
+	if (err < 0)
+		goto exit;
+
+	// copy response to user
+	if (usb_cmd.resp_size > 0)
+		if (copy_to_user(usb_cmd.resp_data, resp_data,
+				 usb_cmd.resp_size))
+			err = -EFAULT;
+
+exit:
+	kfree(req_data);
+	kfree(resp_data);
+
+	return err;
+}
+
+static int scarlett_gen2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
+				     unsigned int cmd, unsigned long arg)
+{
+	switch (cmd) {
+
+	case SCARLETT2_IOCTL_USB_CMD:
+		return scarlett_gen2_ioctl_usb_cmd(hw, arg);
+
+	// TODO
+	case SCARLETT2_IOCTL_LOCK:
+	case SCARLETT2_IOCTL_UNLOCK:
+		return -EINVAL;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
 int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer)
 {
 	struct snd_usb_audio *chip = mixer->chip;
 	const struct scarlett2_device_entry *entry;
+	struct snd_hwdep *hw;
 	int err;
 
 	/* only use UAC_VERSION_2 */
@@ -4302,11 +4379,24 @@ int snd_scarlett_gen2_init(struct usb_mixer_interface *mixer)
 		USB_ID_PRODUCT(chip->usb_id));
 
 	err = snd_scarlett_gen2_controls_create(mixer, entry);
-	if (err < 0)
+	if (err < 0) {
 		usb_audio_err(mixer->chip,
 			      "Error initialising %s Mixer Driver: %d",
 			      entry->series_name,
 			      err);
+		return err;
+	}
+
+	err = snd_hwdep_new(mixer->chip->card, "Focusrite Control", 0, &hw);
+	if (err < 0) {
+		usb_audio_err(mixer->chip,
+			      "Error creating hwdep device: %d",
+			      err);
+		return err;
+	}
 
-	return err;
+	hw->private_data = mixer->private_data;
+	hw->ops.ioctl = scarlett_gen2_hwdep_ioctl;
+
+	return 0;
 }
-- 
2.41.0




[Index of Archives]     [ALSA User]     [Linux Audio Users]     [Pulse Audio]     [Kernel Archive]     [Asterisk PBX]     [Photo Sharing]     [Linux Sound]     [Video 4 Linux]     [Gimp]     [Yosemite News]

  Powered by Linux