|
@@ -0,0 +1,457 @@
|
|
|
|
+/*
|
|
|
|
+ * 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_common *common)
|
|
|
|
+{
|
|
|
|
+ /* the max message is 64KB */
|
|
|
|
+ utp_context.buffer = vmalloc(0x10000);
|
|
|
|
+ if (!utp_context.buffer)
|
|
|
|
+ return -EIO;
|
|
|
|
+ utp_context.utp_version = 0x1ull;
|
|
|
|
+ common->fsg->utp = &utp_context;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void utp_exit(struct fsg_common *common)
|
|
|
|
+{
|
|
|
|
+ vfree(utp_context.buffer);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+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);
|
|
|
|
+ return uud;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void utp_user_data_free(struct utp_user_data *uud)
|
|
|
|
+{
|
|
|
|
+ kfree(uud);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* 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))
|
|
|
|
+
|
|
|
|
+/* Will be called when the host wants to get the sense data */
|
|
|
|
+static int utp_get_sense(struct fsg_commmon *common)
|
|
|
|
+{
|
|
|
|
+ struct fsg_dev *fsg;
|
|
|
|
+
|
|
|
|
+ fsg = common->fsg;
|
|
|
|
+ if (UTP_CTX(fsg)->processed == 0)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ UTP_CTX(fsg)->processed = 0;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int utp_do_read(struct fsg_common *common, void *data, size_t size)
|
|
|
|
+{
|
|
|
|
+ struct fsg_buffhd *bh;
|
|
|
|
+ struct fsg_dev *fsg;
|
|
|
|
+ int rc;
|
|
|
|
+ u32 amount_left;
|
|
|
|
+ unsigned int amount;
|
|
|
|
+
|
|
|
|
+ /* Get the starting Logical Block Address and check that it's
|
|
|
|
+ * not too big */
|
|
|
|
+
|
|
|
|
+ fsg = common->fsg;
|
|
|
|
+ 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_common *common, void *data, size_t size)
|
|
|
|
+{
|
|
|
|
+ struct fsg_buffhd *bh;
|
|
|
|
+ struct fsg_dev *fsg;
|
|
|
|
+ int get_some_more;
|
|
|
|
+ u32 amount_left_to_req, amount_left_to_write;
|
|
|
|
+ unsigned int amount;
|
|
|
|
+ int rc;
|
|
|
|
+ loff_t offset;
|
|
|
|
+
|
|
|
|
+ fsg = common->fsg;
|
|
|
|
+ /* 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) {
|
|
|
|
+ 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_common *common, u16 code, u64 reply)
|
|
|
|
+{
|
|
|
|
+ struct fsg_dev *fsg;
|
|
|
|
+
|
|
|
|
+ fsg = common->fsg;
|
|
|
|
+ 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;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#if 0
|
|
|
|
+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);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
|
|
+static int utp_exec(struct fsg_common *common,
|
|
|
|
+ char *command,
|
|
|
|
+ int cmdsize,
|
|
|
|
+ unsigned long long payload)
|
|
|
|
+{
|
|
|
|
+ struct fsg_dev *fsg = common->fsg;
|
|
|
|
+ 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);
|
|
|
|
+
|
|
|
|
+ uud = utp_interpret_message(uud2r); /* TODO */
|
|
|
|
+
|
|
|
|
+ if (command[0] == '!') /* there will be no response */
|
|
|
|
+ return 0;
|
|
|
|
+#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
|
|
|
|
+
|
|
|
|
+ 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_common *common)
|
|
|
|
+{
|
|
|
|
+ struct fsg_buffhd *bh;
|
|
|
|
+ struct fsg_dev *fsg = common->fsg;
|
|
|
|
+ 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_common *common,
|
|
|
|
+ char *cdb_data,
|
|
|
|
+ int default_reply)
|
|
|
|
+{
|
|
|
|
+ struct utp_msg *m = (struct utp_msg *)cdb_data;
|
|
|
|
+ struct fsg_dev *fsg = common->fsg;
|
|
|
|
+ void *data = NULL;
|
|
|
|
+ int r;
|
|
|
|
+ struct utp_user_data *uud2r, *uud;
|
|
|
|
+ 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 */
|
|
|
|
+ uud = utp_interpret_message(uud2r); /* TODO */
|
|
|
|
+ /*
|
|
|
|
+ * 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);
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ utp_send_status(fsg);
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
+
|