| /* |
| * 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 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 = 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_user_data *uud) |
| { |
| mutex_lock(&utp_context.lock); |
| list_del(&uud->link); |
| mutex_unlock(&utp_context.lock); |
| vfree(uud); |
| } |
| |
| /* Get the number of element for list */ |
| static u32 count_list(struct list_head *l) |
| { |
| u32 count = 0; |
| struct list_head *tmp; |
| |
| mutex_lock(&utp_context.lock); |
| list_for_each(tmp, l) { |
| count++; |
| } |
| mutex_unlock(&utp_context.lock); |
| |
| 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)) { |
| printk(KERN_INFO "[ %s ] copy error\n", __func__); |
| return -EACCES; |
| } |
| if (free) |
| utp_user_data_free(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_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 (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_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 %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); |
| 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, |
| &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 */ |
| set_bulk_out_req_length(fsg->common, bh, 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, true); |
| 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(uud); |
| } else { |
| if (utp_context.cur_state & UTP_FLAG_DATA) { |
| if (count_list(&ctx->read) < 7) { |
| pr_debug("%s: pass returned in POLL stage. \n", __func__); |
| UTP_SS_PASS(fsg); |
| utp_context.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(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(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); |
| 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 = 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_context.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)->read) < 7) { |
| utp_context.cur_state = 0; |
| UTP_SS_PASS(fsg); |
| } else |
| UTP_SS_BUSY(fsg, UTP_CTX(fsg)->counter); |
| |
| break; |
| } |
| |
| utp_send_status(fsg); |
| return -1; |
| } |