summaryrefslogtreecommitdiff
path: root/sound/usb
diff options
context:
space:
mode:
authorGeoffrey D. Bennett <g@b4.vu>2023-12-29 22:49:23 +1030
committerTakashi Iwai <tiwai@suse.de>2023-12-29 15:39:27 +0100
commit1abfbd3c952781881dc17d8e3624cac9df6a70b5 (patch)
tree5916fb986463c2837852f356193eb5491f649268 /sound/usb
parent6a7508e64ee3e8320c886020bcdcd70f7fcbff2c (diff)
ALSA: scarlett2: Add support for uploading new firmware
Add ops.write to the hwdep interface. Once the upgrade firmware flash segment has been erased, writes to the hwdep fd are permitted, and translated to SCARLETT2_USB_WRITE_SEGMENT commands to the device. Signed-off-by: Geoffrey D. Bennett <g@b4.vu> Link: https://lore.kernel.org/r/ZY65S0ojShSNSeRQ@m.b4.vu Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'sound/usb')
-rw-r--r--sound/usb/mixer_scarlett2.c96
1 files changed, 93 insertions, 3 deletions
diff --git a/sound/usb/mixer_scarlett2.c b/sound/usb/mixer_scarlett2.c
index 2d60fa607bd8..6a9e8e2e246f 100644
--- a/sound/usb/mixer_scarlett2.c
+++ b/sound/usb/mixer_scarlett2.c
@@ -266,7 +266,8 @@ enum {
enum {
SCARLETT2_FLASH_WRITE_STATE_IDLE = 0,
SCARLETT2_FLASH_WRITE_STATE_SELECTED = 1,
- SCARLETT2_FLASH_WRITE_STATE_ERASING = 2
+ SCARLETT2_FLASH_WRITE_STATE_ERASING = 2,
+ SCARLETT2_FLASH_WRITE_STATE_WRITE = 3
};
static const char *const scarlett2_dim_mute_names[SCARLETT2_DIM_MUTE_COUNT] = {
@@ -1176,6 +1177,7 @@ static int scarlett2_get_port_start_num(
#define SCARLETT2_USB_METER_LEVELS_GET_MAGIC 1
#define SCARLETT2_FLASH_BLOCK_SIZE 4096
+#define SCARLETT2_FLASH_WRITE_MAX 1024
#define SCARLETT2_SEGMENT_NUM_MIN 1
#define SCARLETT2_SEGMENT_NUM_MAX 4
@@ -5079,10 +5081,10 @@ static int scarlett2_ioctl_get_erase_progress(
return -EFAULT;
/* If the erase is complete, change the state from ERASING to
- * IDLE.
+ * WRITE.
*/
if (progress.progress == 0xff)
- private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_IDLE;
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_WRITE;
return 0;
}
@@ -5135,6 +5137,93 @@ static int scarlett2_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
}
}
+static long scarlett2_hwdep_write(struct snd_hwdep *hw,
+ const char __user *buf,
+ long count, loff_t *offset)
+{
+ struct usb_mixer_interface *mixer = hw->private_data;
+ struct scarlett2_data *private = mixer->private_data;
+ int segment_id, segment_num, err, len;
+ int flash_size;
+
+ /* SCARLETT2_USB_WRITE_SEGMENT request data */
+ struct {
+ __le32 segment_num;
+ __le32 offset;
+ __le32 pad;
+ u8 data[];
+ } __packed *req;
+
+ /* Calculate the maximum permitted in data[] */
+ const size_t max_data_size = SCARLETT2_FLASH_WRITE_MAX -
+ offsetof(typeof(*req), data);
+
+ /* If erasing, wait for it to complete */
+ if (private->flash_write_state ==
+ SCARLETT2_FLASH_WRITE_STATE_ERASING) {
+ err = scarlett2_wait_for_erase(mixer);
+ if (err < 0)
+ return err;
+ private->flash_write_state = SCARLETT2_FLASH_WRITE_STATE_WRITE;
+
+ /* Check that an erase has been done & completed */
+ } else if (private->flash_write_state !=
+ SCARLETT2_FLASH_WRITE_STATE_WRITE) {
+ return -EINVAL;
+ }
+
+ /* Check that we're writing to the upgrade firmware */
+ segment_id = private->selected_flash_segment_id;
+ if (segment_id != SCARLETT2_SEGMENT_ID_FIRMWARE)
+ return -EINVAL;
+
+ segment_num = private->flash_segment_nums[segment_id];
+ if (segment_num < SCARLETT2_SEGMENT_NUM_MIN ||
+ segment_num > SCARLETT2_SEGMENT_NUM_MAX)
+ return -EFAULT;
+
+ /* Validate the offset and count */
+ flash_size = private->flash_segment_blocks[segment_id] *
+ SCARLETT2_FLASH_BLOCK_SIZE;
+
+ if (count < 0 || *offset < 0 || *offset + count >= flash_size)
+ return -EINVAL;
+
+ if (!count)
+ return 0;
+
+ /* Limit the *req size to SCARLETT2_FLASH_WRITE_MAX */
+ if (count > max_data_size)
+ count = max_data_size;
+
+ /* Create and send the request */
+ len = struct_size(req, data, count);
+ req = kzalloc(len, GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+
+ req->segment_num = cpu_to_le32(segment_num);
+ req->offset = cpu_to_le32(*offset);
+ req->pad = 0;
+
+ if (copy_from_user(req->data, buf, count)) {
+ err = -EFAULT;
+ goto error;
+ }
+
+ err = scarlett2_usb(mixer, SCARLETT2_USB_WRITE_SEGMENT,
+ req, len, NULL, 0);
+ if (err < 0)
+ goto error;
+
+ *offset += count;
+ err = count;
+
+error:
+ kfree(req);
+ return err;
+}
+
static int scarlett2_hwdep_release(struct snd_hwdep *hw, struct file *file)
{
struct usb_mixer_interface *mixer = hw->private_data;
@@ -5164,6 +5253,7 @@ static int scarlett2_hwdep_init(struct usb_mixer_interface *mixer)
hw->exclusive = 1;
hw->ops.open = scarlett2_hwdep_open;
hw->ops.ioctl = scarlett2_hwdep_ioctl;
+ hw->ops.write = scarlett2_hwdep_write;
hw->ops.release = scarlett2_hwdep_release;
return 0;