summaryrefslogtreecommitdiff
path: root/drivers/usb/gadget
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r--drivers/usb/gadget/Kconfig6
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c70
-rw-r--r--drivers/usb/gadget/function/fsl_updater.c644
-rw-r--r--drivers/usb/gadget/function/fsl_updater.h152
4 files changed, 871 insertions, 1 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 31cce7805eb2..53114b126f42 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -343,6 +343,12 @@ config USB_CONFIGFS_MASS_STORAGE
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
+config FSL_UTP
+ bool "UTP over Storage Gadget"
+ depends on USB_CONFIGFS_MASS_STORAGE
+ help
+ Freescale's extension to MSC protocol
+
config USB_CONFIGFS_F_LB_SS
bool "Loopback and sourcesink function (for testing)"
depends on USB_CONFIGFS
diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index 41b5baa1f43b..5ec48dbaff6b 100644
--- a/drivers/usb/gadget/function/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
@@ -333,8 +333,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)
{
@@ -1118,6 +1125,15 @@ static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
}
#endif
+#ifdef CONFIG_FSL_UTP
+ if (is_utp_device(common->fsg) &&
+ 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;
@@ -1139,6 +1155,10 @@ 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
+ if (is_utp_device(common->fsg))
+ put_unaligned_be32(UTP_CTX(common->fsg)->sdinfo_h, &buf[8]);
+#endif
return 18;
}
@@ -1625,7 +1645,20 @@ static void send_status(struct fsg_common *common)
sd = SS_INVALID_COMMAND;
} else if (sd != SS_NO_SENSE) {
DBG(common, "sending command-failure status\n");
- status = US_BULK_STAT_FAIL;
+#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
+ */
+ if (is_utp_device(common->fsg))
+ status = US_BULK_STAT_OK;
+ else
+#endif
+ status = US_BULK_STAT_FAIL;
+
VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
" info x%x\n",
SK(sd), ASC(sd), ASCQ(sd), sdinfo);
@@ -1815,6 +1848,14 @@ static int do_scsi_command(struct fsg_common *common)
common->phase_error = 0;
common->short_packet_received = 0;
+#ifdef CONFIG_FSL_UTP
+ if (is_utp_device(common->fsg))
+ 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]) {
@@ -2461,6 +2502,18 @@ static int fsg_main_thread(void *common_)
/* Allow the thread to be frozen */
set_freezable();
+ /*
+ * 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.
+ */
+#ifdef CONFIG_FSL_UTP
+ if (!is_utp_device(common->fsg))
+ set_fs(get_ds());
+#else
+ set_fs(get_ds());
+#endif
+
/* The main loop */
while (common->state != FSG_STATE_TERMINATED) {
if (exception_in_progress(common) || signal_pending(current)) {
@@ -2909,6 +2962,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);
@@ -2960,6 +3017,11 @@ 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
+ if (is_utp_device(fsg))
+ utp_init(fsg);
+#endif
+
/* Find all the endpoints we will use */
ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
if (!ep)
@@ -3022,6 +3084,12 @@ static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
}
usb_free_all_descriptors(&fsg->function);
+
+#ifdef CONFIG_FSL_UTP
+ if (is_utp_device(common->fsg))
+ utp_exit(fsg);
+#endif
+
}
static inline struct fsg_lun_opts *to_fsg_lun_opts(struct config_item *item)
diff --git a/drivers/usb/gadget/function/fsl_updater.c b/drivers/usb/gadget/function/fsl_updater.c
new file mode 100644
index 000000000000..eec5bf1f8f1e
--- /dev/null
+++ b/drivers/usb/gadget/function/fsl_updater.c
@@ -0,0 +1,644 @@
+/*
+ * Freescale UUT driver
+ *
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ * Copyright 2017 NXP
+ */
+
+/*
+ * 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 bool is_utp_device(struct fsg_dev *fsg)
+{
+ struct usb_device_descriptor *pdesc;
+
+ if (!fsg || !fsg->common || !fsg->common->cdev)
+ return false;
+
+ pdesc = &fsg->common->cdev->desc;
+ if (pdesc->idVendor == UTP_IDVENDOR &&
+ pdesc->idProduct == UTP_IDPRODUCT)
+ return true;
+
+ return false;
+}
+
+static u64 get_be64(u8 *buf)
+{
+ return ((u64)get_unaligned_be32(buf) << 32) |
+ get_unaligned_be32(buf + 4);
+}
+
+static DEFINE_IDA(utp_ida);
+
+static int utp_init(struct fsg_dev *fsg)
+{
+ struct utp_context *utp;
+ int index;
+
+ utp = vzalloc(sizeof(struct utp_context));
+ if (!utp)
+ return -EIO;
+
+ init_waitqueue_head(&utp->wq);
+ init_waitqueue_head(&utp->list_full_wq);
+
+ INIT_LIST_HEAD(&utp->read);
+ INIT_LIST_HEAD(&utp->write);
+ mutex_init(&utp->lock);
+
+ /* the max message is 64KB */
+ utp->buffer = vmalloc(0x10000);
+ if (!utp->buffer) {
+ vfree(utp);
+ return -EIO;
+ }
+
+ utp->utp_version = 0x1ull;
+ fsg->utp = utp;
+ utp->utp_dev.minor = MISC_DYNAMIC_MINOR;
+ utp->utp_dev.fops = &utp_fops;
+
+ index = ida_simple_get(&utp_ida, 0, 0, GFP_KERNEL);
+ if (index == 0)
+ snprintf(utp->utp_name, 8, "utp");
+ else
+ snprintf(utp->utp_name, 8, "utp%d", index);
+
+ utp->utp_dev.name = utp->utp_name;
+ return misc_register(&utp->utp_dev);
+}
+
+static void utp_exit(struct fsg_dev *fsg)
+{
+ struct utp_context *utp;
+ utp = UTP_CTX(fsg);
+
+ misc_deregister(&utp->utp_dev);
+
+ vfree(utp->buffer);
+ vfree(utp);
+}
+
+static struct utp_user_data *utp_user_data_alloc(size_t size)
+{
+ struct utp_user_data *uud;
+
+ uud = vmalloc(size + sizeof(*uud));
+ if (!uud)
+ return uud;
+ memset(uud, 0, size + sizeof(*uud));
+ uud->data.size = size + sizeof(uud->data);
+ INIT_LIST_HEAD(&uud->link);
+ return uud;
+}
+
+static void utp_user_data_free(struct utp_context *utp, struct utp_user_data *uud)
+{
+ mutex_lock(&utp->lock);
+ list_del(&uud->link);
+ mutex_unlock(&utp->lock);
+ vfree(uud);
+}
+
+/* Get the number of element for list */
+static u32 count_list(struct utp_context *utp, struct list_head *l)
+{
+ u32 count = 0;
+ struct list_head *tmp;
+
+ mutex_lock(&utp->lock);
+ list_for_each(tmp, l) {
+ count++;
+ }
+ mutex_unlock(&utp->lock);
+
+ return count;
+}
+
+/* The routine will not go on if utp_context.queue is empty */
+#define WAIT_ACTIVITY(utp, queue) \
+ wait_event_interruptible(utp->wq, !list_empty(&utp->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;
+
+ struct miscdevice *misc = (struct miscdevice *)file->private_data;
+ struct utp_context *utp = container_of(misc, struct utp_context, utp_dev);
+
+ WAIT_ACTIVITY(utp, read);
+
+ mutex_lock(&utp->lock);
+ uud = list_first_entry(&utp->read, struct utp_user_data, link);
+ mutex_unlock(&utp->lock);
+ size_to_put = uud->data.size;
+
+ if (size >= size_to_put)
+ free = !0;
+ if (copy_to_user(buf, &uud->data, size_to_put)) {
+ printk(KERN_INFO "[ %s ] copy error\n", __func__);
+ return -EACCES;
+ }
+ if (free)
+ utp_user_data_free(utp, uud);
+ else {
+ pr_info("sizeof = %zd, size = %zd\n",
+ sizeof(uud->data),
+ uud->data.size);
+
+ pr_err("Will not free utp_user_data, because buffer size = %zd need to put %zd\n",
+ size, size_to_put);
+ }
+
+ /*
+ * The user program has already finished data process,
+ * go on getting data from the host
+ */
+ wake_up(&utp->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;
+
+ struct miscdevice *misc = (struct miscdevice *)file->private_data;
+ struct utp_context *utp = container_of(misc, struct utp_context, utp_dev);
+
+ if (size < sizeof(uud->data))
+ return -EINVAL;
+ uud = utp_user_data_alloc(size);
+ if (uud == NULL)
+ return -ENOMEM;
+ if (copy_from_user(&uud->data, buf, size)) {
+ printk(KERN_INFO "[ %s ] copy error!\n", __func__);
+ vfree(uud);
+ return -EACCES;
+ }
+ mutex_lock(&utp->lock);
+ list_add_tail(&uud->link, &utp->write);
+ /* Go on EXEC routine process */
+ wake_up(&utp->wq);
+ mutex_unlock(&utp->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 %zd\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, true, bh);
+ 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 %zd\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);
+
+ 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 */
+ set_bulk_out_req_length(fsg->common, bh, amount);
+ bh->outreq->short_not_ok = 1;
+ start_transfer(fsg, fsg->bulk_out, bh->outreq);
+ 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, true, bh);
+ 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 if (uud->data.flags & UTP_FLAG_REPORT_BUSY) {
+ UTP_SS_BUSY(fsg, --ctx->counter);
+ } else {
+ printk("%s: pass returned.\n", __func__);
+ UTP_SS_PASS(fsg);
+ }
+ utp_user_data_free(ctx, uud);
+ } else {
+ if (ctx->cur_state & UTP_FLAG_DATA) {
+ if (count_list(ctx, &ctx->read) < 7) {
+ pr_debug("%s: pass returned in POLL stage. \n", __func__);
+ UTP_SS_PASS(fsg);
+ ctx->cur_state = 0;
+ return;
+ }
+ }
+ 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);
+
+ ctx->counter = 0xFFFF;
+ uud2r = utp_user_data_alloc(cmdsize + 1);
+ if (!uud2r)
+ return -ENOMEM;
+ 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(ctx, 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
+ } else {
+ pr_err("UTP write list is empty.\n");
+ mutex_unlock(&ctx->lock);
+ return 0;
+ }
+ 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) {
+ 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 returned in EXEC stage. \n", __func__);
+ UTP_SS_PASS(fsg);
+ }
+ utp_user_data_free(ctx, 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, true, bh);
+ 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);
+ 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 = vmalloc(fsg->common->data_size);
+ memset(data, 0, fsg->common->data_size);
+ /* 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);
+ vfree(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:
+ UTP_CTX(fsg)->cur_state = UTP_FLAG_DATA;
+ pr_debug("%s: PUT, Received %d bytes\n", __func__, fsg->common->data_size);/* data from host to device */
+ uud2r = utp_user_data_alloc(fsg->common->data_size);
+ if (!uud2r)
+ return -ENOMEM;
+ 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
+ if (count_list(UTP_CTX(fsg), &UTP_CTX(fsg)->read) < 7) {
+ UTP_CTX(fsg)->cur_state = 0;
+ UTP_SS_PASS(fsg);
+ } else
+ UTP_SS_BUSY(fsg, UTP_CTX(fsg)->counter);
+
+ break;
+ }
+
+ utp_send_status(fsg);
+ return -1;
+}
diff --git a/drivers/usb/gadget/function/fsl_updater.h b/drivers/usb/gadget/function/fsl_updater.h
new file mode 100644
index 000000000000..044beb0e2114
--- /dev/null
+++ b/drivers/usb/gadget/function/fsl_updater.h
@@ -0,0 +1,152 @@
+/*
+ * Freescale UUT driver
+ *
+ * Copyright 2008-2016 Freescale Semiconductor, Inc.
+ * Copyright 2008-2009 Embedded Alley Solutions, Inc All Rights Reserved.
+ * Copyright 2017 NXP
+ */
+
+/*
+ * 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 <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/vmalloc.h>
+#include <linux/ioctl.h>
+/* #include <mach/hardware.h> */
+struct utp_context;
+
+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 bool is_utp_device(struct fsg_dev *fsg);
+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_context *utp, 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_IDVENDOR 0x066F
+#define UTP_IDPRODUCT 0x37FF
+
+#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,
+};
+
+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;
+ u32 cur_state;
+ struct miscdevice utp_dev;
+ char utp_name[8];
+};
+
+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,
+};
+
+#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 */
+