/* * Freescale UUT driver * * Copyright 2008-2010 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; } /* 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, mod_data.buflen); /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); 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->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->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->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, mod_data.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->next_buffhd_to_fill = bh->next; continue; } /* Write the received data to the backing file */ bh = fsg->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->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->residue -= amount; /* Did the host decide to stop early? */ if (bh->outreq->actual != bh->outreq->length) { fsg->short_packet_received = 1; break; } continue; } /* Wait for something to happen */ rc = sleep_thread(fsg); 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 = USB_STATUS_PASS; struct bulk_cs_wrap *csw; int rc; /* Wait for the next buffer to become available */ bh = fsg->next_buffhd_to_fill; while (bh->state != BUF_STATE_EMPTY) { rc = sleep_thread(fsg); if (rc) return rc; } if (fsg->phase_error) { DBG(fsg, "sending phase-error status\n"); status = USB_STATUS_PHASE_ERROR; } else if ((UTP_CTX(fsg)->sd & 0xFFFF) != UTP_REPLY_PASS) { status = USB_STATUS_FAIL; } csw = bh->buf; /* Store and send the Bulk-only CSW */ csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); csw->Tag = fsg->tag; csw->Residue = cpu_to_le32(fsg->residue); csw->Status = status; bh->inreq->length = USB_BULK_CS_WRAP_LEN; bh->inreq->zero = 0; start_transfer(fsg, fsg->bulk_in, bh->inreq, &bh->inreq_busy, &bh->state); fsg->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->data_size, GFP_KERNEL); /* copy data from usb buffer to utp buffer */ utp_do_write(fsg, data, fsg->data_size); utp_exec(fsg, data, fsg->data_size, param); kfree(data); break; case UTP_GET: /* data from device to host */ pr_debug("%s: GET, %d bytes\n", __func__, fsg->data_size); r = utp_do_read(fsg, UTP_CTX(fsg)->buffer, fsg->data_size); UTP_SS_PASS(fsg); break; case UTP_PUT: /* data from host to device */ pr_debug("%s: PUT, %d bytes\n", __func__, fsg->data_size); uud2r = utp_user_data_alloc(fsg->data_size); uud2r->data.bufsize = fsg->data_size; uud2r->data.flags = UTP_FLAG_DATA; utp_do_write(fsg, uud2r->data.data, fsg->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; }