mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 13:41:51 +00:00
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>
This commit is contained in:
parent
6a7508e64e
commit
1abfbd3c95
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user