From 8b54f71d01f0564fec83dd33443cc6bfb9b3e9cd Mon Sep 17 00:00:00 2001 From: Frank Li Date: Wed, 28 Aug 2013 16:02:06 -0500 Subject: [PATCH] Cheery picked: commit 125203588824fd5cb13822cb0b1fed7da91d7512 git://git.freescale.com/imx/linux-2.6-imx.git ENGR00280494-1 make the kernel image for mfgtool uboot needs pass down below parameters removable = 1 stall = 0 idVendor = 0x066F idProduct = 0x37FF iSerialNumber = "" Signed-off-by: Lu Lin Signed-off-by: Frank Li resolved Conflicts: drivers/usb/gadget/f_mass_storage.c --- drivers/usb/gadget/Kconfig | 6 + drivers/usb/gadget/f_mass_storage.c | 50 +++ drivers/usb/gadget/fsl_updater.c | 568 ++++++++++++++++++++++++++++ drivers/usb/gadget/fsl_updater.h | 149 ++++++++ drivers/usb/gadget/mass_storage.c | 2 +- 5 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/gadget/fsl_updater.c create mode 100644 drivers/usb/gadget/fsl_updater.h diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ba18e9c110cc..24878f178404 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -945,6 +945,12 @@ config USB_MASS_STORAGE Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_mass_storage". +config FSL_UTP + bool "UTP over Storage Gadget" + depends on USB_MASS_STORAGE + help + Freescale's extension to MSC protocol + config USB_GADGET_TARGET tristate "USB Gadget Target Fabric Module" depends on TARGET_CORE diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c index b96393908860..858f7a34308a 100644 --- a/drivers/usb/gadget/f_mass_storage.c +++ b/drivers/usb/gadget/f_mass_storage.c @@ -336,8 +336,15 @@ struct fsg_dev { struct usb_ep *bulk_in; struct usb_ep *bulk_out; +#ifdef CONFIG_FSL_UTP + void *utp; +#endif }; +#ifdef CONFIG_FSL_UTP +#include "fsl_updater.h" +#endif + static inline int __fsg_is_set(struct fsg_common *common, const char *func, unsigned line) { @@ -1131,6 +1138,13 @@ static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) } #endif +#ifdef CONFIG_FSL_UTP + if (utp_get_sense(common->fsg) == 0) { /* got the sense from the UTP */ + sd = UTP_CTX(common->fsg)->sd; + sdinfo = UTP_CTX(common->fsg)->sdinfo; + valid = 0; + } else +#endif if (!curlun) { /* Unsupported LUNs are okay */ common->bad_lun_okay = 1; sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; @@ -1152,6 +1166,9 @@ static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh) buf[7] = 18 - 8; /* Additional sense length */ buf[12] = ASC(sd); buf[13] = ASCQ(sd); +#ifdef CONFIG_FSL_UTP + put_unaligned_be32(UTP_CTX(common->fsg)->sdinfo_h, &buf[8]); +#endif return 18; } @@ -1645,7 +1662,18 @@ static int send_status(struct fsg_common *common) sd = SS_INVALID_COMMAND; } else if (sd != SS_NO_SENSE) { DBG(common, "sending command-failure status\n"); +#ifdef CONFIG_FSL_UTP +/* + * mfgtool host frequently reset bus during transfer + * - the response in csw to request sense will be 1 due to UTP change + * some storage information + * - host will reset the bus if response to request sense is 1 + * - change the response to 0 if CONFIG_FSL_UTP is defined + */ + status = US_BULK_STAT_OK; +#else status = US_BULK_STAT_FAIL; +#endif VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" " info x%x\n", SK(sd), ASC(sd), ASCQ(sd), sdinfo); @@ -1836,6 +1864,13 @@ static int do_scsi_command(struct fsg_common *common) common->phase_error = 0; common->short_packet_received = 0; +#ifdef CONFIG_FSL_UTP + reply = utp_handle_message(common->fsg, common->cmnd, reply); + + if (reply != -EINVAL) + return reply; +#endif + down_read(&common->filesem); /* We're using the backing file */ switch (common->cmnd[0]) { @@ -2502,12 +2537,14 @@ static int fsg_main_thread(void *common_) /* Allow the thread to be frozen */ set_freezable(); +#ifndef CONFIG_FSL_UTP /* * Arrange for userspace references to be interpreted as kernel * pointers. That way we can pass a kernel pointer to a routine * that expects a __user pointer and it will work okay. */ set_fs(get_ds()); +#endif /* The main loop */ while (common->state != FSG_STATE_TERMINATED) { @@ -3096,6 +3133,10 @@ static void fsg_common_release(struct kref *ref) /*-------------------------------------------------------------------------*/ +#ifdef CONFIG_FSL_UTP +#include "fsl_updater.c" +#endif + static int fsg_bind(struct usb_configuration *c, struct usb_function *f) { struct fsg_dev *fsg = fsg_from_func(f); @@ -3127,6 +3168,10 @@ static int fsg_bind(struct usb_configuration *c, struct usb_function *f) fsg_intf_desc.bInterfaceNumber = i; fsg->interface_number = i; +#ifdef CONFIG_FSL_UTP + utp_init(fsg); +#endif + /* Find all the endpoints we will use */ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc); if (!ep) @@ -3184,7 +3229,12 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f) wait_event(common->fsg_wait, common->fsg != fsg); } + fsg_common_put(common); usb_free_all_descriptors(&fsg->function); + kfree(fsg); +#ifdef CONFIG_FSL_UTP + utp_exit(fsg); +#endif } static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item) diff --git a/drivers/usb/gadget/fsl_updater.c b/drivers/usb/gadget/fsl_updater.c new file mode 100644 index 000000000000..8b4af624ba83 --- /dev/null +++ b/drivers/usb/gadget/fsl_updater.c @@ -0,0 +1,568 @@ +/* + * Freescale UUT driver + * + * Copyright 2008-2013 Freescale Semiconductor, Inc. + * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +static u64 get_be64(u8 *buf) +{ + return ((u64)get_unaligned_be32(buf) << 32) | + get_unaligned_be32(buf + 4); +} + +static int utp_init(struct fsg_dev *fsg) +{ + init_waitqueue_head(&utp_context.wq); + init_waitqueue_head(&utp_context.list_full_wq); + + INIT_LIST_HEAD(&utp_context.read); + INIT_LIST_HEAD(&utp_context.write); + mutex_init(&utp_context.lock); + + /* the max message is 64KB */ + utp_context.buffer = vmalloc(0x10000); + if (!utp_context.buffer) + return -EIO; + utp_context.utp_version = 0x1ull; + fsg->utp = &utp_context; + return misc_register(&utp_dev); +} + +static void utp_exit(struct fsg_dev *fsg) +{ + vfree(utp_context.buffer); + misc_deregister(&utp_dev); +} + +static struct utp_user_data *utp_user_data_alloc(size_t size) +{ + struct utp_user_data *uud; + + uud = kzalloc(size + sizeof(*uud), GFP_KERNEL); + if (!uud) + return uud; + uud->data.size = size + sizeof(uud->data); + INIT_LIST_HEAD(&uud->link); + return uud; +} + +static void utp_user_data_free(struct utp_user_data *uud) +{ + mutex_lock(&utp_context.lock); + list_del(&uud->link); + mutex_unlock(&utp_context.lock); + kfree(uud); +} + +/* Get the number of element for list */ +static u32 count_list(struct list_head *l) +{ + u32 count = 0; + struct list_head *tmp; + + list_for_each(tmp, l) { + count++; + } + + return count; +} +/* The routine will not go on if utp_context.queue is empty */ +#define WAIT_ACTIVITY(queue) \ + wait_event_interruptible(utp_context.wq, !list_empty(&utp_context.queue)) + +/* Called by userspace program (uuc) */ +static ssize_t utp_file_read(struct file *file, + char __user *buf, + size_t size, + loff_t *off) +{ + struct utp_user_data *uud; + size_t size_to_put; + int free = 0; + + WAIT_ACTIVITY(read); + + mutex_lock(&utp_context.lock); + uud = list_first_entry(&utp_context.read, struct utp_user_data, link); + mutex_unlock(&utp_context.lock); + size_to_put = uud->data.size; + + if (size >= size_to_put) + free = !0; + if (copy_to_user(buf, &uud->data, size_to_put)) + return -EACCES; + if (free) + utp_user_data_free(uud); + else { + pr_info("sizeof = %d, size = %d\n", + sizeof(uud->data), + uud->data.size); + + pr_err("Will not free utp_user_data, because buffer size = %d," + "need to put %d\n", size, size_to_put); + } + + /* + * The user program has already finished data process, + * go on getting data from the host + */ + wake_up(&utp_context.list_full_wq); + + return size_to_put; +} + +static ssize_t utp_file_write(struct file *file, const char __user *buf, + size_t size, loff_t *off) +{ + struct utp_user_data *uud; + + if (size < sizeof(uud->data)) + return -EINVAL; + uud = utp_user_data_alloc(size); + if (copy_from_user(&uud->data, buf, size)) + return -EACCES; + mutex_lock(&utp_context.lock); + list_add_tail(&uud->link, &utp_context.write); + /* Go on EXEC routine process */ + wake_up(&utp_context.wq); + mutex_unlock(&utp_context.lock); + return size; +} + +/* + * uuc should change to use soc bus infrastructure to soc information + * /sys/devices/soc0/soc_id + * this function can be removed. + */ +static long +utp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + int cpu_id = 0; + + switch (cmd) { + case UTP_GET_CPU_ID: + return put_user(cpu_id, (int __user *)arg); + default: + return -ENOIOCTLCMD; + } +} + +/* Will be called when the host wants to get the sense data */ +static int utp_get_sense(struct fsg_dev *fsg) +{ + if (UTP_CTX(fsg)->processed == 0) + return -1; + + UTP_CTX(fsg)->processed = 0; + return 0; +} + +static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size) +{ + struct fsg_buffhd *bh; + int rc; + u32 amount_left; + unsigned int amount; + + /* Get the starting Logical Block Address and check that it's + * not too big */ + + amount_left = size; + if (unlikely(amount_left == 0)) + return -EIO; /* No default reply*/ + + pr_debug("%s: sending %d\n", __func__, size); + for (;;) { + /* Figure out how much we need to read: + * Try to read the remaining amount. + * But don't read more than the buffer size. + * And don't try to read past the end of the file. + * Finally, if we're not at a page boundary, don't read past + * the next page. + * If this means reading 0 then we were asked to read past + * the end of file. */ + amount = min((unsigned int) amount_left, FSG_BUFLEN); + + /* Wait for the next buffer to become available */ + bh = fsg->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + /* If we were asked to read past the end of file, + * end with an empty buffer. */ + if (amount == 0) { + bh->inreq->length = 0; + bh->state = BUF_STATE_FULL; + break; + } + + /* Perform the read */ + pr_info("Copied to %p, %d bytes started from %d\n", + bh->buf, amount, size - amount_left); + /* from upt buffer to file_storeage buffer */ + memcpy(bh->buf, data + size - amount_left, amount); + amount_left -= amount; + fsg->common->residue -= amount; + + bh->inreq->length = amount; + bh->state = BUF_STATE_FULL; + + /* Send this buffer and go read some more */ + bh->inreq->zero = 0; + + /* USB Physical transfer: Data from device to host */ + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + + fsg->common->next_buffhd_to_fill = bh->next; + + if (amount_left <= 0) + break; + } + + return size - amount_left; +} + +static int utp_do_write(struct fsg_dev *fsg, void *data, size_t size) +{ + struct fsg_buffhd *bh; + int get_some_more; + u32 amount_left_to_req, amount_left_to_write; + unsigned int amount; + int rc; + loff_t offset; + + /* Carry out the file writes */ + get_some_more = 1; + amount_left_to_req = amount_left_to_write = size; + + if (unlikely(amount_left_to_write == 0)) + return -EIO; + + offset = 0; + while (amount_left_to_write > 0) { + + /* Queue a request for more data from the host */ + bh = fsg->common->next_buffhd_to_fill; + if (bh->state == BUF_STATE_EMPTY && get_some_more) { + + /* Figure out how much we want to get: + * Try to get the remaining amount. + * But don't get more than the buffer size. + * And don't try to go past the end of the file. + * If we're not at a page boundary, + * don't go past the next page. + * If this means getting 0, then we were asked + * to write past the end of file. + * Finally, round down to a block boundary. */ + amount = min(amount_left_to_req, FSG_BUFLEN); + + if (amount == 0) { + get_some_more = 0; + /* cry now */ + continue; + } + + /* Get the next buffer */ + amount_left_to_req -= amount; + if (amount_left_to_req == 0) + get_some_more = 0; + + /* amount is always divisible by 512, hence by + * the bulk-out maxpacket size */ + bh->outreq->length = bh->bulk_out_intended_length = + amount; + bh->outreq->short_not_ok = 1; + start_transfer(fsg, fsg->bulk_out, bh->outreq, + &bh->outreq_busy, &bh->state); + fsg->common->next_buffhd_to_fill = bh->next; + continue; + } + + /* Write the received data to the backing file */ + bh = fsg->common->next_buffhd_to_drain; + if (bh->state == BUF_STATE_EMPTY && !get_some_more) + break; /* We stopped early */ + if (bh->state == BUF_STATE_FULL) { + smp_rmb(); + fsg->common->next_buffhd_to_drain = bh->next; + bh->state = BUF_STATE_EMPTY; + + /* Did something go wrong with the transfer? */ + if (bh->outreq->status != 0) + /* cry again, COMMUNICATION_FAILURE */ + break; + + amount = bh->outreq->actual; + + /* Perform the write */ + memcpy(data + offset, bh->buf, amount); + + offset += amount; + if (signal_pending(current)) + return -EINTR; /* Interrupted!*/ + amount_left_to_write -= amount; + fsg->common->residue -= amount; + + /* Did the host decide to stop early? */ + if (bh->outreq->actual != bh->outreq->length) { + fsg->common->short_packet_received = 1; + break; + } + continue; + } + + /* Wait for something to happen */ + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + return -EIO; +} + +static inline void utp_set_sense(struct fsg_dev *fsg, u16 code, u64 reply) +{ + UTP_CTX(fsg)->processed = true; + UTP_CTX(fsg)->sdinfo = reply & 0xFFFFFFFF; + UTP_CTX(fsg)->sdinfo_h = (reply >> 32) & 0xFFFFFFFF; + UTP_CTX(fsg)->sd = (UTP_SENSE_KEY << 16) | code; +} + +static void utp_poll(struct fsg_dev *fsg) +{ + struct utp_context *ctx = UTP_CTX(fsg); + struct utp_user_data *uud = NULL; + + mutex_lock(&ctx->lock); + if (!list_empty(&ctx->write)) + uud = list_first_entry(&ctx->write, struct utp_user_data, link); + mutex_unlock(&ctx->lock); + + if (uud) { + if (uud->data.flags & UTP_FLAG_STATUS) { + printk(KERN_WARNING "%s: exit with status %d\n", + __func__, uud->data.status); + UTP_SS_EXIT(fsg, uud->data.status); + } else { + pr_debug("%s: pass\n", __func__); + UTP_SS_PASS(fsg); + } + utp_user_data_free(uud); + } else { + pr_debug("%s: still busy...\n", __func__); + UTP_SS_BUSY(fsg, --ctx->counter); + } +} + +static int utp_exec(struct fsg_dev *fsg, + char *command, + int cmdsize, + unsigned long long payload) +{ + struct utp_user_data *uud = NULL, *uud2r; + struct utp_context *ctx = UTP_CTX(fsg); + + uud2r = utp_user_data_alloc(cmdsize + 1); + uud2r->data.flags = UTP_FLAG_COMMAND; + uud2r->data.payload = payload; + strncpy(uud2r->data.command, command, cmdsize); + + mutex_lock(&ctx->lock); + list_add_tail(&uud2r->link, &ctx->read); + mutex_unlock(&ctx->lock); + /* wake up the read routine */ + wake_up(&ctx->wq); + + if (command[0] == '!') /* there will be no response */ + return 0; + + /* + * the user program (uuc) will return utp_message + * and add list to write list + */ + WAIT_ACTIVITY(write); + + mutex_lock(&ctx->lock); + if (!list_empty(&ctx->write)) { + uud = list_first_entry(&ctx->write, struct utp_user_data, link); +#ifdef DEBUG + pr_info("UUD:\n\tFlags = %02X\n", uud->data.flags); + if (uud->data.flags & UTP_FLAG_DATA) { + pr_info("\tbufsize = %d\n", uud->data.bufsize); + print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_NONE, + 16, 2, uud->data.data, uud->data.bufsize, true); + } + if (uud->data.flags & UTP_FLAG_REPORT_BUSY) + pr_info("\tBUSY\n"); +#endif + } + mutex_unlock(&ctx->lock); + + if (uud->data.flags & UTP_FLAG_DATA) { + memcpy(ctx->buffer, uud->data.data, uud->data.bufsize); + UTP_SS_SIZE(fsg, uud->data.bufsize); + } else if (uud->data.flags & UTP_FLAG_REPORT_BUSY) { + ctx->counter = 0xFFFF; + UTP_SS_BUSY(fsg, ctx->counter); + } else if (uud->data.flags & UTP_FLAG_STATUS) { + printk(KERN_WARNING "%s: exit with status %d\n", __func__, + uud->data.status); + UTP_SS_EXIT(fsg, uud->data.status); + } else { + pr_debug("%s: pass\n", __func__); + UTP_SS_PASS(fsg); + } + utp_user_data_free(uud); + return 0; +} + +static int utp_send_status(struct fsg_dev *fsg) +{ + struct fsg_buffhd *bh; + u8 status = US_BULK_STAT_OK; + struct bulk_cs_wrap *csw; + int rc; + + /* Wait for the next buffer to become available */ + bh = fsg->common->next_buffhd_to_fill; + while (bh->state != BUF_STATE_EMPTY) { + rc = sleep_thread(fsg->common); + if (rc) + return rc; + } + + if (fsg->common->phase_error) { + DBG(fsg, "sending phase-error status\n"); + status = US_BULK_STAT_PHASE; + + } else if ((UTP_CTX(fsg)->sd & 0xFFFF) != UTP_REPLY_PASS) { + status = US_BULK_STAT_FAIL; + } + + csw = bh->buf; + + /* Store and send the Bulk-only CSW */ + csw->Signature = __constant_cpu_to_le32(US_BULK_CS_SIGN); + csw->Tag = fsg->common->tag; + csw->Residue = cpu_to_le32(fsg->common->residue); + csw->Status = status; + + bh->inreq->length = US_BULK_CS_WRAP_LEN; + bh->inreq->zero = 0; + start_transfer(fsg, fsg->bulk_in, bh->inreq, + &bh->inreq_busy, &bh->state); + fsg->common->next_buffhd_to_fill = bh->next; + return 0; +} + +static int utp_handle_message(struct fsg_dev *fsg, + char *cdb_data, + int default_reply) +{ + struct utp_msg *m = (struct utp_msg *)cdb_data; + void *data = NULL; + int r; + struct utp_user_data *uud2r; + unsigned long long param; + unsigned long tag; + + if (m->f0 != 0xF0) + return default_reply; + + tag = get_unaligned_be32((void *)&m->utp_msg_tag); + param = get_be64((void *)&m->param); + pr_debug("Type 0x%x, tag 0x%08lx, param %llx\n", + m->utp_msg_type, tag, param); + + switch ((enum utp_msg_type)m->utp_msg_type) { + + case UTP_POLL: + if (get_be64((void *)&m->param) == 1) { + pr_debug("%s: version request\n", __func__); + UTP_SS_EXIT(fsg, UTP_CTX(fsg)->utp_version); + break; + } + utp_poll(fsg); + break; + case UTP_EXEC: + pr_debug("%s: EXEC\n", __func__); + data = kzalloc(fsg->common->data_size, GFP_KERNEL); + /* copy data from usb buffer to utp buffer */ + utp_do_write(fsg, data, fsg->common->data_size); + utp_exec(fsg, data, fsg->common->data_size, param); + kfree(data); + break; + case UTP_GET: /* data from device to host */ + pr_debug("%s: GET, %d bytes\n", __func__, + fsg->common->data_size); + r = utp_do_read(fsg, UTP_CTX(fsg)->buffer, + fsg->common->data_size); + UTP_SS_PASS(fsg); + break; + case UTP_PUT: /* data from host to device */ + pr_debug("%s: PUT, %d bytes\n", __func__, + fsg->common->data_size); + uud2r = utp_user_data_alloc(fsg->common->data_size); + uud2r->data.bufsize = fsg->common->data_size; + uud2r->data.flags = UTP_FLAG_DATA; + utp_do_write(fsg, uud2r->data.data, fsg->common->data_size); + /* don't know what will be written */ + mutex_lock(&UTP_CTX(fsg)->lock); + list_add_tail(&uud2r->link, &UTP_CTX(fsg)->read); + mutex_unlock(&UTP_CTX(fsg)->lock); + wake_up(&UTP_CTX(fsg)->wq); + /* + * Return PASS or FAIL according to uuc's status + * Please open it if need to check uuc's status + * and use another version uuc + */ +#if 0 + struct utp_user_data *uud = NULL; + struct utp_context *ctx; + WAIT_ACTIVITY(write); + ctx = UTP_CTX(fsg); + mutex_lock(&ctx->lock); + + if (!list_empty(&ctx->write)) + uud = list_first_entry(&ctx->write, + struct utp_user_data, link); + + mutex_unlock(&ctx->lock); + if (uud) { + if (uud->data.flags & UTP_FLAG_STATUS) { + printk(KERN_WARNING "%s: exit with status %d\n", + __func__, uud->data.status); + UTP_SS_EXIT(fsg, uud->data.status); + } else { + pr_debug("%s: pass\n", __func__); + UTP_SS_PASS(fsg); + } + utp_user_data_free(uud); + } else{ + UTP_SS_PASS(fsg); + } +#endif + UTP_SS_PASS(fsg); + + wait_event_interruptible(UTP_CTX(fsg)->list_full_wq, + count_list(&UTP_CTX(fsg)->read) < 7); + break; + } + + utp_send_status(fsg); + return -1; +} diff --git a/drivers/usb/gadget/fsl_updater.h b/drivers/usb/gadget/fsl_updater.h new file mode 100644 index 000000000000..857e22ebdabc --- /dev/null +++ b/drivers/usb/gadget/fsl_updater.h @@ -0,0 +1,149 @@ +/* + * Freescale UUT driver + * + * Copyright 2008-2013 Freescale Semiconductor, Inc. + * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved. + */ + +/* + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#ifndef __FSL_UPDATER_H +#define __FSL_UPDATER_H + +#include +#include +#include +#include +/* #include */ + +static int utp_init(struct fsg_dev *fsg); +static void utp_exit(struct fsg_dev *fsg); +static ssize_t utp_file_read(struct file *file, + char __user *buf, + size_t size, + loff_t *off); + +static ssize_t utp_file_write(struct file *file, + const char __user *buf, + size_t size, + loff_t *off); + +static long utp_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +static struct utp_user_data *utp_user_data_alloc(size_t size); +static void utp_user_data_free(struct utp_user_data *uud); +static int utp_get_sense(struct fsg_dev *fsg); +static int utp_do_read(struct fsg_dev *fsg, void *data, size_t size); +static int utp_do_write(struct fsg_dev *fsg, void *data, size_t size); +static inline void utp_set_sense(struct fsg_dev *fsg, u16 code, u64 reply); +static int utp_handle_message(struct fsg_dev *fsg, + char *cdb_data, + int default_reply); + +#define UTP_REPLY_PASS 0 +#define UTP_REPLY_EXIT 0x8001 +#define UTP_REPLY_BUSY 0x8002 +#define UTP_REPLY_SIZE 0x8003 +#define UTP_SENSE_KEY 9 + +#define UTP_MINOR 222 +/* MISC_DYNAMIC_MINOR would be better, but... */ + +#define UTP_COMMAND_SIZE 80 + +#define UTP_SS_EXIT(fsg, r) utp_set_sense(fsg, UTP_REPLY_EXIT, (u64)r) +#define UTP_SS_PASS(fsg) utp_set_sense(fsg, UTP_REPLY_PASS, 0) +#define UTP_SS_BUSY(fsg, r) utp_set_sense(fsg, UTP_REPLY_BUSY, (u64)r) +#define UTP_SS_SIZE(fsg, r) utp_set_sense(fsg, UTP_REPLY_SIZE, (u64)r) + +#define UTP_IOCTL_BASE 'U' +#define UTP_GET_CPU_ID _IOR(UTP_IOCTL_BASE, 0, int) +/* the structure of utp message which is mapped to 16-byte SCSI CBW's CDB */ +#pragma pack(1) +struct utp_msg { + u8 f0; + u8 utp_msg_type; + u32 utp_msg_tag; + union { + struct { + u32 param_lsb; + u32 param_msb; + }; + u64 param; + }; +}; + +enum utp_msg_type { + UTP_POLL = 0, + UTP_EXEC, + UTP_GET, + UTP_PUT, +}; + +static struct utp_context { + wait_queue_head_t wq; + wait_queue_head_t list_full_wq; + struct mutex lock; + struct list_head read; + struct list_head write; + u32 sd, sdinfo, sdinfo_h; /* sense data */ + int processed; + u8 *buffer; + u32 counter; + u64 utp_version; +} utp_context; + +static const struct file_operations utp_fops = { + .open = nonseekable_open, + .read = utp_file_read, + .write = utp_file_write, + /* .ioctl = utp_ioctl, */ + .unlocked_ioctl = utp_ioctl, +}; + +static struct miscdevice utp_dev = { + .minor = UTP_MINOR, + .name = "utp", + .fops = &utp_fops, +}; + +#define UTP_FLAG_COMMAND 0x00000001 +#define UTP_FLAG_DATA 0x00000002 +#define UTP_FLAG_STATUS 0x00000004 +#define UTP_FLAG_REPORT_BUSY 0x10000000 +struct utp_message { + u32 flags; + size_t size; + union { + struct { + u64 payload; + char command[1]; + }; + struct { + size_t bufsize; + u8 data[1]; + }; + u32 status; + }; +}; + +struct utp_user_data { + struct list_head link; + struct utp_message data; +}; +#pragma pack() + +static inline struct utp_context *UTP_CTX(struct fsg_dev *fsg) +{ + return (struct utp_context *)fsg->utp; +} + +#endif /* __FSL_UPDATER_H */ + diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c index 8e27a8c96444..1178aea9c544 100644 --- a/drivers/usb/gadget/mass_storage.c +++ b/drivers/usb/gadget/mass_storage.c @@ -266,7 +266,7 @@ static int __init msg_init(void) { return usb_composite_probe(&msg_driver); } -module_init(msg_init); +late_initcall(msg_init); static void msg_cleanup(void) { -- 2.39.2