diff options
Diffstat (limited to 'drivers/usb/gadget')
-rw-r--r-- | drivers/usb/gadget/Kconfig | 6 | ||||
-rw-r--r-- | drivers/usb/gadget/function/f_mass_storage.c | 70 | ||||
-rw-r--r-- | drivers/usb/gadget/function/fsl_updater.c | 644 | ||||
-rw-r--r-- | drivers/usb/gadget/function/fsl_updater.h | 152 |
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 */ + |