* [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure
@ 2011-12-04 20:12 Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 2/3] uas: MS UAS Gadget driver - Implementation Shimrit Malichi
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: Shimrit Malichi @ 2011-12-04 20:12 UTC (permalink / raw)
To: greg
Cc: linux-usb, linux-arm-msm, balbi, ablay, Shimrit Malichi,
Tatyana Brokhman, open list
This patch implements the infrastructure for the UAS gadget driver.
The UAS gadget driver registers as a second configuration of the MS
gadet driver.
A new module parameter was added to the mass_storage module:
bool use_uasp. (default = 0)
If this parameter is set to true, the mass_storage module will register
with the UAS configuration as the devices first configuration and
operate according to the UAS protocol.
The number of buffers used by the mass_storage device was increased
according to the number of supported streams.
It defines the API for COMMAND/TASK MANAGEMENT IU implementation.
Change-Id: I86ec7f23b15e602b0f46934adbf5824472e59a1f
Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
---
drivers/usb/gadget/f_mass_storage.c | 26 +-
drivers/usb/gadget/f_uasp.c | 2393 +++++++++++++++++++++++++++++++++++
drivers/usb/gadget/f_uasp.h | 430 +++++++
drivers/usb/gadget/mass_storage.c | 67 +-
drivers/usb/gadget/storage_common.c | 24 +-
drivers/usb/gadget/uasp_cmdiu.c | 514 ++++++++
drivers/usb/gadget/uasp_tmiu.c | 277 ++++
7 files changed, 3708 insertions(+), 23 deletions(-)
create mode 100644 drivers/usb/gadget/f_uasp.c
create mode 100644 drivers/usb/gadget/f_uasp.h
create mode 100644 drivers/usb/gadget/uasp_cmdiu.c
create mode 100644 drivers/usb/gadget/uasp_tmiu.c
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index c39d588..b777d72 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -2703,7 +2703,8 @@ static inline void fsg_common_put(struct fsg_common *common)
static struct fsg_common *fsg_common_init(struct fsg_common *common,
struct usb_composite_dev *cdev,
- struct fsg_config *cfg)
+ struct fsg_config *cfg,
+ int start_thread)
{
struct usb_gadget *gadget = cdev->gadget;
struct fsg_buffhd *bh;
@@ -2866,12 +2867,14 @@ buffhds_first_it:
kref_init(&common->ref);
/* Tell the thread to start working */
- common->thread_task =
- kthread_create(fsg_main_thread, common,
+ if (start_thread) {
+ common->thread_task =
+ kthread_create(fsg_main_thread, common,
cfg->thread_name ?: "file-storage");
- if (IS_ERR(common->thread_task)) {
- rc = PTR_ERR(common->thread_task);
- goto error_release;
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
+ }
}
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->fsg_wait);
@@ -2902,10 +2905,11 @@ buffhds_first_it:
}
kfree(pathbuf);
- DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
-
- wake_up_process(common->thread_task);
-
+ if (start_thread) {
+ DBG(common, "I/O thread pid: %d\n",
+ task_pid_nr(common->thread_task));
+ wake_up_process(common->thread_task);
+ }
return common;
error_luns:
@@ -3196,6 +3200,6 @@ fsg_common_from_params(struct fsg_common *common,
{
struct fsg_config cfg;
fsg_config_from_params(&cfg, params);
- return fsg_common_init(common, cdev, &cfg);
+ return fsg_common_init(common, cdev, &cfg, 1);
}
diff --git a/drivers/usb/gadget/f_uasp.c b/drivers/usb/gadget/f_uasp.c
new file mode 100644
index 0000000..af1569e
--- /dev/null
+++ b/drivers/usb/gadget/f_uasp.c
@@ -0,0 +1,2393 @@
+/*
+ * f_uasp.c -- Mass Storage USB UASP Composite Function
+ *
+ * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/*
+ * The UASP Function acts as a USB Mass Storage device, appearing to the
+ * host as a disk drive or as a CD-ROM drive. In contrary to
+ * f_mass_storage function that implements the BOT protocol, the UASP
+ * function implements the UAS Protocol.
+ * It's operational both in High and Super connection speeds.
+ * Streaming support depends on the DCD streaming capabilities.
+ *
+ * The Function supports multiple logical units (LUNs). Backing storage
+ * for each LUN is provided by a regular file or a block device. Access
+ * for each LUN can be limited to read-only. Moreover, the function can
+ * indicate that LUN is removable and/or CD-ROM. (The later implies
+ * read-only access.)
+ *
+ * Requirements from the system are:
+ * - 2 bulk-in and 2 bulk-out endpoints are needed.
+ * - The number of buffers used by the Function depends on whether
+ * streaming is supported by the DCD or not. If streaming is not
+ * supported then the minimum number of buffers used by the UASP
+ * function is 4 - one for each endpoint, when the buffer for the
+ * command endpoint is allocated statically and is dedicated to the
+ * command endpoint only.
+ * If streaming is supported then the number of required buffers
+ * equals num_of_streams * 4.
+ * The size of each buffer is 16K by default and is configurable
+ * by a parameter.
+ *
+ * Note that the driver is slightly non-portable in that it assumes that
+ * the same memory/DMA buffer my be used both for bulk-in and bulk-out
+ * endpoints. With most device controllers this isn't an issue, but there
+ * may be some with hardware restrictions that prevent a buffer from being
+ * used by more than one endpoint.
+ *
+ * This function is heavily based on "Mass Storage USB Composite Function" by
+ * Michal Nazarewicz which is based based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell. The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ * Driver Design
+ *
+ * The UASP Function driver registers as a second configuration to the
+ * mass_storage module. In the enumeration if the host wishes to use the
+ * UAS protocol it sends a SET_CONFIGURATION command and chooses the UASP
+ * configuration.
+ * The UASP function driver inherits from the Mass Storage Function
+ * driver and extends it according to UASP requirements.
+ *
+ * All of the control/status transfers in UASP are performed in form of
+ * IUs (Information Units. See section 6.2 of the UASP Spec). The
+ * command/data/status parts of the SCSI protocol are replaced by:
+ * - command IU / task management IU
+ * - data phase
+ * - sense IU / response IU
+ * Each command / task management is handled by the main thread (if not
+ * LUN specific) or by the specific LUN thread, if such LUN exists. Each
+ * of the threads (generic and LUN specific) implements the above
+ * host/device interaction.
+ *
+ * The guiding line through the design was to inherit as much as possible
+ * from already existing f_mass_storage driver since the UASP protocol
+ * extends the already existing BOT protocol. Re-using already
+ * implemented (by the f_mass_storage driver) functions as is wasn't
+ * always possible and a code duplication was forced. In order to clean
+ * this up all the SCSI command handling should be taken out to a
+ * different file both from the UASP driver and from the f_mass_storage
+ * driver, leaving the later two to handle just the UASP/BOT protocols
+ * and not the SCSI protocol. By doing so code duplication will be spared.
+ *
+ * An alternative design would have been to implement the USP driver from
+ * scratch, without the inheritance from f_mass_storage. The pros of this
+ * approach would have been that the existing f_mass_storage driver would
+ * remain as is (without any modifications whatsoever). On the other hand
+ * the cons were:
+ * 1. A separate mechanism would be required to indicate which one of the
+ * drivers to load when connecting to a host according to the hosts
+ * capability to support UASP. In the chosen approach this decision is
+ * left to the host to choose the configuration it wishes to operate
+ * in.
+ * 2. Code/data structures duplication. As already mentioned, the UASP
+ * protocol extends the BOT protocol implemented by the f_mass_storage
+ * driver, thus the two are similar in their data structures and basic
+ * functionality.
+ * We decided to leave this to a second phase of the development in order
+ * to leave the existing f_mass_storage driver with as less changes as
+ * possible.
+ *
+ * The handling of command IUs and task management IUs was divided into
+ * two separate files that are both included by the f_uasp driver.
+ *
+ * Several kernel threads are created as part of the init sequence:
+ * - UASP main thread
+ * - A thread for each of the existing LUNs
+ * The UASP main thread handles all of the generic commands/task
+ * management requests and routes LUN specific requests to be handled by
+ * the appropriate LUNs task.
+ * The approach of "task per LUN" was chosen due to the UAS protocol
+ * enhancement over the BOT protocol. The main retouch of the UAS
+ * protocol of the BOT protocol is the fact that independent commands can
+ * be performed in parallel. For example a READ command for two different
+ * LUNS. Thus in order to implement this concurrency a separate thread is
+ * needed for each of the existing LUNS.
+ * As long as the LUN threads are alive they keep an open reference to the
+ * backing file. This prevents the unmounting of the backing file's
+ * underlying file system and cause problems during system shutdown.
+ *
+ * In the existing f_mass_storage common data structures a single lock is
+ * used for protecting the state of the driver USB requests handled by it.
+ * Since a separate thread was created for each LUN, allowing it to handle
+ * requests addressed to it, the same protection mechanism was required.
+ * Thus a lock was added to each of the LUNS to protect the LUNs state and
+ * the IUs (USB requests) handled by that LUN.
+ *
+ * Interrupt routines field callbacks from controller driver:
+ * - bulk-in, bulk-out, command and status request notifications
+ * - disconnect events
+ * Completion requests are passed to the appropriate thread by wake up
+ * calls. Most of the ep0 requests are handled at interrupt time except
+ * for the following:
+ * - SetInterface
+ * - SetConfiguration
+ * - Device reset
+ * The above are handled by the main thread and are passed to it in form
+ * of "exceptions" using SIGUSR1 signal (since they should interrupt any
+ * ongoing I/O operations).
+ *
+ * In normal operation the main thread is created during UASP_bind but
+ * started only when the UASP configuration is choosen. This is necessary
+ * since msg main thread is also created during msg_bind but since UASP
+ * Function inherits from the Mass Storage Function, the running thread
+ * (UASP or msg) will be saved in a data structure that is shared by UASP
+ * and msg.
+ * The main thread is stopped during unbind but can also be stopped when
+ * it receives a signal. There is no point in leaving the gadget if the
+ * main thread is dead but this is not implemented yet. Maybe a callback
+ * function is needed.
+ *
+ * To provide maximum throughput the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd in which each of the buffers is linked
+ * in a 1:1 connection to an element of struct uasp_buf). Each buffer head
+ * contains a bulk-in and bulk-out requests and thus can be used both for
+ * IN and OUT transfers.
+ * The usage of the pipe line is similar to it's usage by the Mass Storage
+ * Function.
+ */
+
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/string.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/kernel.h>
+#include <linux/usb/storage.h>
+
+#include "uasp_cmdiu.c"
+#include "uasp_tmiu.c"
+
+/* Descriptors */
+
+/* There is only one interface. */
+static struct usb_interface_descriptor
+uasp_intf_desc = {
+ .bLength = sizeof uasp_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 4,
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI,
+ .bInterfaceProtocol = USB_PR_UAS,
+ .iInterface = FSG_STRING_INTERFACE,
+};
+
+/* BULK-in pipe descriptors */
+static struct usb_endpoint_descriptor
+uasp_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+struct usb_pipe_usage_descriptor
+uasp_bulk_in_pipe_usg_desc = {
+ .bLength = sizeof uasp_bulk_in_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_DATA_IN,
+ .Reserved = 0,
+};
+
+struct usb_endpoint_descriptor
+uasp_ss_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+struct usb_ss_ep_comp_descriptor
+uasp_bulk_in_ep_comp_desc = {
+ .bLength = sizeof uasp_bulk_in_ep_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0, /*
+ * Doesn't support burst. Maybe update later?
+ * Should it be HW dependent?
+ */
+ .bmAttributes = UASP_SS_EP_COMP_NUM_STREAMS,
+ .wBytesPerInterval = 0,
+};
+
+/* BULK-out pipe descriptors */
+struct usb_endpoint_descriptor
+uasp_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+struct usb_pipe_usage_descriptor
+uasp_bulk_out_pipe_usg_desc = {
+ .bLength = sizeof uasp_bulk_out_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_DATA_OUT,
+ .Reserved = 0,
+};
+
+struct usb_endpoint_descriptor
+uasp_ss_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+struct usb_ss_ep_comp_descriptor
+uasp_bulk_out_ep_comp_desc = {
+ .bLength = sizeof uasp_bulk_out_ep_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0, /*
+ * Doesn't support burst. Maybe update later?
+ * Should it be HW dependent?
+ */
+ .bmAttributes = UASP_SS_EP_COMP_NUM_STREAMS,
+ .wBytesPerInterval = 0,
+};
+
+/* Status pipe - descriptors */
+struct usb_endpoint_descriptor
+uasp_status_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+struct usb_pipe_usage_descriptor
+uasp_status_in_pipe_usg_desc = {
+ .bLength = sizeof uasp_status_in_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_STS,
+ .Reserved = 0,
+};
+
+struct usb_endpoint_descriptor
+uasp_ss_status_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+struct usb_ss_ep_comp_descriptor
+uasp_status_in_ep_comp_desc = {
+ .bLength = sizeof uasp_status_in_ep_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0, /*
+ * Doesn't support burst. Maybe update later?
+ * Should it be HW dependent?
+ */
+ .bmAttributes = UASP_SS_EP_COMP_NUM_STREAMS,
+ .wBytesPerInterval = 0,
+};
+
+/* Command pipe descriptors */
+struct usb_endpoint_descriptor
+uasp_command_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+struct usb_pipe_usage_descriptor
+uasp_command_out_pipe_usg_desc = {
+ .bLength = sizeof uasp_command_out_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_CMD,
+ .Reserved = 0,
+};
+
+struct usb_endpoint_descriptor
+uasp_ss_command_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(0x400),
+};
+
+struct usb_ss_ep_comp_descriptor
+uasp_command_out_ep_comp_desc = {
+ .bLength = sizeof uasp_command_out_ep_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+ .bMaxBurst = 0, /*
+ * Doesn't support burst. Maybe update later?
+ * Should it be HW dependent?
+ */
+ .bmAttributes = 0, /* No streams on command endpoint */
+ .wBytesPerInterval = 0,
+};
+
+/* HS configuration function descriptors */
+struct usb_descriptor_header *uasp_hs_function_desc[] = {
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_in_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_out_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_out_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_command_out_desc,
+ (struct usb_descriptor_header *) &uasp_command_out_pipe_usg_desc,
+ NULL,
+};
+
+/* SS configuration function descriptors */
+struct usb_descriptor_header *uasp_ss_function_desc[] = {
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bulk_in_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_in_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_ss_bulk_out_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_out_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_out_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_ss_status_in_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_ss_command_out_desc,
+ (struct usb_descriptor_header *) &uasp_command_out_ep_comp_desc,
+ (struct usb_descriptor_header *) &uasp_command_out_pipe_usg_desc,
+ NULL,
+};
+
+/*--------------------------------------------------------------------------*/
+static inline struct uasp_dev *uaspd_from_func(struct usb_function *f)
+{
+ struct fsg_dev *fsg_dev = fsg_from_func(f);
+ return container_of(fsg_dev, struct uasp_dev, fsg_dev);
+}
+
+static void uasp_common_release(struct kref *ref)
+{
+ struct uasp_common *ucommon =
+ container_of(ref, struct uasp_common, ref);
+ struct uasp_lun *ulun;
+ int i;
+
+ /* First stop all lun threads */
+ run_lun_threads(ucommon->udev, LUN_STATE_EXIT);
+ for (i = 0; i < ucommon->common->nluns; i++) {
+ ulun = &(ucommon->uluns[i]);
+ if (ulun->lun_state != LUN_STATE_TERMINATED) {
+ wait_for_completion(&ulun->thread_notifier);
+ /* The cleanup routine waits for this completion also */
+ complete(&ulun->thread_notifier);
+ }
+ }
+ fsg_common_release(&(ucommon->common->ref));
+ kfree(ucommon->uluns);
+ kfree(ucommon);
+}
+
+
+static inline void uasp_common_put(struct uasp_common *common)
+{
+ kref_put(&(common->ref), uasp_common_release);
+}
+
+static struct uasp_lun *find_lun_by_id(struct uasp_dev *udev, __u8 *lun_id)
+{
+ int i;
+ struct uasp_lun *curlun;
+
+ DBG(udev->ucommon->common, "%s() - Enter.\n", __func__);
+
+ for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ curlun = &udev->ucommon->uluns[i];
+
+ if (memcmp(lun_id, curlun->lun_id, 8) == 0) {
+ DBG(udev->ucommon->common, "%s() - LUN found\n",
+ __func__);
+ return curlun;
+ }
+ }
+ DBG(udev->ucommon->common, "%s() - LUN not found\n", __func__);
+ return 0;
+}
+
+/**
+ * wakeup_lun_thread() - Wakes up the given LUn thread
+ * @lun: the LUN which thread needs wakening
+ *
+ * NOTE: Caller must hold uasp_lun->lock
+ *
+ */
+static void wakeup_lun_thread(struct uasp_lun *lun)
+{
+ /* Tell the lun thread that something has happened */
+ lun->thread_wakeup_needed = 1;
+ if (lun->lun_thread_task)
+ wake_up_process(lun->lun_thread_task);
+}
+
+/**
+ * command_complete() - Callback function for the command endpoint
+ * @ep: pointer to the usb_ep (command endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the command endpoint.
+ * If the request completed without errors the function marks the state of the
+ * command buffer as full and wakes up the uasp main thread.
+ */
+static void command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct uasp_dev *udev = (struct uasp_dev *) ep->driver_data;
+ unsigned long flags;
+
+ if (req->actual > 0)
+ dump_msg(udev->ucommon->common, "command", req->buf,
+ req->actual);
+ DBG(udev->ucommon->common, "%s() - Enter", __func__);
+
+ if (req != udev->cmd_buff.outreq) {
+ ERROR(udev->ucommon->common, "(%s) req(%p) != "
+ "cmd_buff.outreq(%p), udev=%p,"
+ " common->state = %d\n",
+ __func__, req, udev->cmd_buff.outreq, udev,
+ udev->ucommon->common->state);
+ }
+
+ if (req->status == -ECONNRESET) {
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ usb_ep_fifo_flush(ep);
+ return;
+ }
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ udev->cmd_buff.state = BUF_STATE_FULL;
+ wakeup_thread(udev->ucommon->common);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+}
+
+/**
+ * status_complete() - Callback function for the status endpoint
+ * @ep: pointer to the usb_ep (status endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the status endpoint.
+ * If the request completion status isn't ECONNRESET the requests tmiu/cmdiu
+ * state is updated to aborted/completed/failed (according to the completion
+ * status of the usb request). If the tmiu/cmdiu was LUN specific, the
+ * corresponding LUN thread is awaken. If it was general, uasp main thread is
+ * awaken.
+ */
+void status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct uasp_dev *udev = (struct uasp_dev *) ep->driver_data;
+ struct uasp_lun *curlun = NULL;
+ struct tm_iu *tmiu;
+ struct cmd_iu *cmdiu;
+ uint8_t cmd_id = ((uint8_t *)req->context)[0];
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter", __func__);
+
+ if (req->status == -ECONNRESET)
+ usb_ep_fifo_flush(ep);
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ /* If Sense IU is filled for TM FUNCTION IU */
+ if (cmd_id == IU_ID_TASK_MANAGEMENT) {
+ tmiu = (struct tm_iu *)req->context;
+ if (tmiu->state != COMMAND_STATE_FAILED &&
+ tmiu->state != COMMAND_STATE_ABORTED) {
+ if (req->status == -ERESTART)
+ tmiu->state = COMMAND_STATE_ABORTED;
+ else if (req->status) {
+ DBG(udev->ucommon->common,
+ "%s() - TMIU FAILED!!! Status = %d",
+ __func__, req->status);
+ tmiu->state = COMMAND_STATE_FAILED;
+ } else
+ tmiu->state = COMMAND_STATE_COMPLETED;
+ }
+ DBG(udev->ucommon->common,
+ "%s() - received IU_ID_TASK_MANAGEMENT "
+ "(Code = %02x tmiu->state = %d)\n",
+ __func__, tmiu->tm_function, tmiu->state);
+ tmiu->bh->inreq_busy = 0;
+ curlun = find_lun_by_id(udev, tmiu->lun);
+ }
+ /* If Sense IU is filled for COMMAND IU */
+ else if (cmd_id == IU_ID_COMMAND) {
+ cmdiu = (struct cmd_iu *)req->context;
+ if (cmdiu->state != COMMAND_STATE_FAILED &&
+ cmdiu->state != COMMAND_STATE_ABORTED) {
+ if (req->status == -ERESTART)
+ cmdiu->state = COMMAND_STATE_ABORTED;
+ else if (req->status) {
+ DBG(udev->ucommon->common,
+ "%s() - CMDIU FAILED!!! Status = %d",
+ __func__, req->status);
+ cmdiu->state = COMMAND_STATE_FAILED;
+ } else if (cmdiu->state == COMMAND_STATE_STATUS)
+ cmdiu->state = COMMAND_STATE_COMPLETED;
+ }
+ DBG(udev->ucommon->common, "%s() - received IU_ID_COMMAND"
+ " (OpCode = %02x, smdiu->state = %d)\n",
+ __func__, cmdiu->cdb[0], cmdiu->state);
+ cmdiu->req_sts = CMD_REQ_COMPLETED;
+ cmdiu->bh->inreq_busy = 0;
+
+ curlun = find_lun_by_id(udev, cmdiu->lun);
+ } else {
+ ERROR(udev->ucommon->common,
+ "%s() - received invalid IU (iu_id = %02x)!\n",
+ __func__, cmd_id);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ return;
+ }
+
+ if (curlun) {
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->pending_requests++;
+ curlun->active_requests--;
+ wakeup_lun_thread(curlun);
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->common->lock),
+ flags);
+ } else {
+ udev->pending_requests++;
+ udev->active_requests--;
+ wakeup_thread(udev->ucommon->common);
+ }
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+}
+
+/**
+ * uasp_bulk_in_complete() - Callback function for the bulk IN endpoint
+ * @ep: pointer to the usb_ep (bulk IN endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the bulk IN endpoint.
+ * The requests cmdiu state is updated according to the completion status of
+ * the usb request. If the cmdiu was LUN specific, the corresponding LUN
+ * thread is awaken. If it was general, uasp main thread is awaken.
+ */
+void uasp_bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct uasp_dev *udev = (struct uasp_dev *) ep->driver_data;
+ struct uasp_lun *curlun;
+ struct cmd_iu *cmdiu;
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (req->status == -ECONNRESET)
+ usb_ep_fifo_flush(ep);
+
+ cmdiu = (struct cmd_iu *)req->context;
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ if (cmdiu->state != COMMAND_STATE_ABORTED &&
+ cmdiu->state != COMMAND_STATE_FAILED) {
+ if (req->status == -ERESTART)
+ cmdiu->state = COMMAND_STATE_ABORTED;
+ else if (req->status != 0)
+ cmdiu->state = COMMAND_STATE_FAILED;
+ }
+
+ cmdiu->req_sts = CMD_REQ_COMPLETED;
+ cmdiu->bh->inreq_busy = 0;
+
+
+ curlun = find_lun_by_id(udev, cmdiu->lun);
+ if (curlun) {
+ spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_lock_irqsave(&curlun->lock, flags);
+ curlun->pending_requests++;
+ curlun->active_requests--;
+ wakeup_lun_thread(curlun);
+ spin_unlock_irqrestore(&curlun->lock, flags);
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ } else {
+ udev->pending_requests++;
+ udev->active_requests--;
+ wakeup_thread(udev->ucommon->common);
+ }
+ spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+}
+
+
+/**
+ * uasp_bulk_out_complete() - Callback function for the bulk OUT endpoint
+ * @ep: pointer to the usb_ep (bulk OUT endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the bulk OUT endpoint.
+ * The requests cmdiu state is updated according to the completion status of
+ * the usb request. If the cmdiu was LUN specific, the corresponding LUN
+ * thread is awaken. If it was general, uasp main thread is awaken.
+ */
+void uasp_bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct uasp_dev *udev = (struct uasp_dev *) ep->driver_data;
+ struct uasp_lun *curlun;
+ struct cmd_iu *cmdiu;
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (req->status == -ECONNRESET)
+ usb_ep_fifo_flush(ep);
+
+ spin_lock_irqsave(&udev->ucommon->common->lock, flags);
+ cmdiu = (struct cmd_iu *)req->context;
+
+ if (cmdiu->state != COMMAND_STATE_ABORTED &&
+ cmdiu->state != COMMAND_STATE_FAILED) {
+ if (req->status == -ERESTART)
+ cmdiu->state = COMMAND_STATE_ABORTED;
+ else if (req->status != 0)
+ cmdiu->state = COMMAND_STATE_FAILED;
+ }
+
+ cmdiu->req_sts = CMD_REQ_COMPLETED;
+ cmdiu->bh->outreq_busy = 0;
+
+ curlun = find_lun_by_id(udev, cmdiu->lun);
+ if (curlun) {
+ spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_lock_irqsave(&curlun->lock, flags);
+ curlun->pending_requests++;
+ curlun->active_requests--;
+ wakeup_lun_thread(curlun);
+ spin_unlock_irqrestore(&curlun->lock, flags);
+ spin_lock_irqsave(&udev->ucommon->common->lock, flags);
+ } else {
+ udev->pending_requests++;
+ udev->active_requests--;
+ wakeup_thread(udev->ucommon->common);
+ }
+ spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+}
+
+/**
+ * remove_completed_commands() - removes all completed UIs
+ * @udev: Programming view of uasp device
+ * @cmd_queue: pointer to the command IUs queue to go over
+ * @tm_func_queue: pointer to the tm IUs queue to go over
+ *
+ * This function goes over the command IUs queue and TM IUs queue and removes
+ * all completed IUs
+ */
+static void remove_completed_commands(struct uasp_dev *udev,
+ struct list_head *cmd_queue,
+ struct list_head *tm_func_queue)
+{
+ struct cmd_iu *cmdiu;
+ struct cmd_iu *tmp_cmdiu;
+ struct tm_iu *tmiu;
+ struct tm_iu *tmp_tmiu;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Remove completed, aborted or failed commands from cmd_queue */
+ list_for_each_entry_safe(cmdiu, tmp_cmdiu, cmd_queue, node) {
+ DBG(udev->ucommon->common, "%s() - cmd_queue cycle"
+ " cmdiu->state=%d "
+ " cmdiu->req_sts=%d\n",
+ __func__, cmdiu->state, cmdiu->req_sts);
+
+ /* Do not touch incompleted commands !!! */
+ if (cmdiu->state != COMMAND_STATE_ABORTED &&
+ cmdiu->state != COMMAND_STATE_COMPLETED &&
+ cmdiu->state != COMMAND_STATE_FAILED)
+ continue;
+
+ if (cmdiu->state == COMMAND_STATE_ABORTED ||
+ cmdiu->state == COMMAND_STATE_FAILED) {
+ if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
+ if (cmdiu->bh->inreq_busy &&
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->inreq)) {
+ cmdiu->req_sts = CMD_REQ_COMPLETED;
+ cmdiu->bh->inreq_busy = 0;
+ }
+ if (cmdiu->bh->outreq_busy &&
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->outreq)) {
+ cmdiu->req_sts = CMD_REQ_COMPLETED;
+ cmdiu->bh->outreq_busy = 0;
+ }
+ }
+
+ if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ continue;
+ }
+ DBG(udev->ucommon->common, "%s() - deleted cmdiu: "
+ "cmdiu[0] = %d, cmdiu->state = %d,"
+ "cmdiu->tag = %d\n",
+ __func__, cmdiu->cdb[0], cmdiu->state, cmdiu->tag);
+ list_del(&cmdiu->node);
+ if (cmdiu->bh) {
+ DBG(udev->ucommon->common, "%s() - Freeing the "
+ "cmdiu->bh\n", __func__);
+ cmdiu->bh->state = BUF_STATE_EMPTY;
+ }
+ kfree(cmdiu);
+ }
+
+ /* Remove completed, aborted or failed commands from tm_func_queue */
+ list_for_each_entry_safe(tmiu, tmp_tmiu, tm_func_queue, node) {
+ /* Do not touch incompleted commands !!! */
+ if (tmiu->state != COMMAND_STATE_ABORTED &&
+ tmiu->state != COMMAND_STATE_COMPLETED &&
+ tmiu->state != COMMAND_STATE_FAILED)
+ continue;
+
+ DBG(udev->ucommon->common, "%s() - deleted tmiu\n", __func__);
+ list_del(&tmiu->node);
+ if (tmiu->bh) {
+ DBG(udev->ucommon->common, "%s() - Freeing the "
+ "tmiu->bh\n", __func__);
+ tmiu->bh->state = BUF_STATE_EMPTY;
+ }
+ kfree(tmiu);
+ }
+ if (list_empty(cmd_queue) && list_empty(tm_func_queue))
+ DBG(udev->ucommon->common, "%s() - both lists are empty\n",
+ __func__);
+ DBG(udev->ucommon->common, "%s() - exit\n", __func__);
+}
+
+/**
+ * do_uasp_set_interface() - Enables/disables the UASP FD.
+ * @uaspd: pointer to the uasp device structure
+ * @new_fsg: pointer to fsg_dev for the new configuration
+ *
+ * Returns 0 on success negative error code otherwise.
+ *
+ * Initiates all endpoints and enables them. Allocates buffers and requests.
+ */
+static int do_uasp_set_interface(struct uasp_dev *uaspd,
+ struct fsg_dev *new_fsg)
+{
+ int rc = 0;
+ int i;
+ struct fsg_dev *fsgd;
+ struct fsg_common *fcommon;
+ unsigned long flags;
+
+ if (!uaspd || !uaspd->ucommon || !uaspd->ucommon->common)
+ return -EIO;
+
+ DBG(uaspd->ucommon->common, "%s()- Enter\n", __func__);
+
+ fcommon = uaspd->ucommon->common;
+ if (uaspd->ucommon->common->running)
+ DBG(uaspd->ucommon->common, "reset inteface\n");
+
+reset_uasp:
+ /* Deallocate the requests */
+ if (uaspd->ucommon->common->fsg) {
+ fsgd = fcommon->fsg;
+
+ abort_commands(uaspd, &uaspd->cmd_queue, &uaspd->tm_func_queue,
+ &(uaspd->ucommon->common->lock));
+ remove_completed_commands(uaspd, &uaspd->cmd_queue,
+ &uaspd->tm_func_queue);
+ uaspd->pending_requests = 0;
+
+ for (i = 0; i < uaspd->ucommon->common->nluns; i++) {
+ struct uasp_lun *ulun = &uaspd->ucommon->uluns[i];
+ abort_commands(uaspd, &ulun->cmd_queue,
+ &ulun->tm_func_queue, &(ulun->lock));
+ remove_completed_commands(uaspd, &ulun->cmd_queue,
+ &ulun->tm_func_queue);
+ spin_lock_irqsave(&(ulun->lock), flags);
+ ulun->pending_requests = 0;
+ ulun->lun->prevent_medium_removal = 0;
+ ulun->lun->sense_data = SS_NO_SENSE;
+ ulun->lun->unit_attention_data = SS_NO_SENSE;
+ ulun->lun->sense_data_info = 0;
+ ulun->lun->info_valid = 0;
+ spin_unlock_irq(&(ulun->lock));
+ }
+ /* Clear out the controller's fifos */
+ if (fcommon->fsg->bulk_in_enabled)
+ usb_ep_fifo_flush(fcommon->fsg->bulk_in);
+ if (fcommon->fsg->bulk_out_enabled)
+ usb_ep_fifo_flush(fcommon->fsg->bulk_out);
+ usb_ep_fifo_flush(uaspd->ucommon->udev->status);
+ usb_ep_fifo_flush(uaspd->ucommon->udev->command);
+
+ spin_lock_irq(&fcommon->lock);
+ /* Reset the I/O buffer states and pointers */
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct fsg_buffhd *bh = &fcommon->buffhds[i];
+ if (bh->inreq) {
+ usb_ep_free_request(fsgd->bulk_in, bh->inreq);
+ bh->inreq = NULL;
+ }
+ if (bh->outreq) {
+ usb_ep_free_request(fsgd->bulk_out, bh->outreq);
+ bh->outreq = NULL;
+ }
+ bh->state = BUF_STATE_EMPTY;
+ }
+
+ /* Deallocate command and status requests */
+ if (uaspd->cmd_buff.inreq) {
+ ERROR(uaspd->ucommon->common,
+ "%s(): uaspd->cmd_buff.inreq isn't NULL. "
+ "How can that be???",
+ __func__);
+ usb_ep_free_request(uaspd->command,
+ uaspd->cmd_buff.inreq);
+ uaspd->cmd_buff.inreq = NULL;
+ }
+ if (uaspd->cmd_buff.outreq) {
+ usb_ep_free_request(uaspd->command,
+ uaspd->cmd_buff.outreq);
+ uaspd->cmd_buff.outreq = NULL;
+ }
+ uaspd->cmd_buff.state = BUF_STATE_EMPTY;
+ spin_unlock_irq(&fcommon->lock);
+
+ /* Disable the endpoints */
+ if (fsgd->bulk_in_enabled) {
+ usb_ep_disable(fsgd->bulk_in);
+ fsgd->bulk_in_enabled = 0;
+ }
+ if (fsgd->bulk_out_enabled) {
+ usb_ep_disable(fsgd->bulk_out);
+ fsgd->bulk_out_enabled = 0;
+ }
+ fsgd->bulk_in->desc = NULL;
+ fsgd->bulk_out->desc = NULL;
+
+ if (uaspd->cmd_enabled) {
+ usb_ep_disable(uaspd->command);
+ uaspd->cmd_enabled = 0;
+ }
+ if (uaspd->status_enabled) {
+ usb_ep_disable(uaspd->status);
+ uaspd->status_enabled = 0;
+ }
+ uaspd->command->desc = NULL;
+ uaspd->status->desc = NULL;
+ DBG(uaspd->ucommon->common, "%s()- disabled endpoints\n",
+ __func__);
+
+ fcommon->fsg = NULL;
+ wake_up(&fcommon->fsg_wait);
+ }
+
+ fcommon->running = 0;
+ if (!new_fsg || rc)
+ return rc;
+
+ fcommon->fsg = new_fsg;
+ fsgd = fcommon->fsg;
+
+ /* Enable the endpoints */
+ config_ep_by_speed(fcommon->gadget, &fsgd->function, fsgd->bulk_in);
+ rc = usb_ep_enable(fsgd->bulk_in);
+ if (rc)
+ goto reset_uasp;
+ fsgd->bulk_in->driver_data = uaspd;
+ fsgd->bulk_in_enabled = 1;
+
+ config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
+ fsgd->bulk_out);
+ rc = usb_ep_enable(fsgd->bulk_out);
+ if (rc)
+ goto reset_uasp;
+ fsgd->bulk_out->driver_data = uaspd;
+ fsgd->bulk_out_enabled = 1;
+
+ fsgd->common->bulk_out_maxpacket =
+ le16_to_cpu(fsgd->bulk_out->maxpacket);
+ clear_bit(IGNORE_BULK_OUT, &fsgd->atomic_bitflags);
+
+ config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
+ uaspd->command);
+ rc = usb_ep_enable(uaspd->command);
+ if (rc)
+ goto reset_uasp;
+ uaspd->command->driver_data = uaspd;
+ uaspd->cmd_enabled = 1;
+
+ config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
+ uaspd->status);
+ rc = usb_ep_enable(uaspd->status);
+ if (rc)
+ goto reset_uasp;
+ uaspd->status->driver_data = uaspd;
+ uaspd->status_enabled = 1;
+
+ /* Allocate the data - requests */
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct uasp_buff *buff = &uaspd->ucommon->ubufs[i];
+
+ buff->fsg_buff->inreq = usb_ep_alloc_request(fsgd->bulk_in,
+ GFP_ATOMIC);
+ if (!buff->fsg_buff->inreq)
+ goto reset_uasp;
+
+ buff->fsg_buff->outreq = usb_ep_alloc_request(fsgd->bulk_out,
+ GFP_ATOMIC);
+ if (!buff->fsg_buff->outreq)
+ goto reset_uasp;
+
+ buff->fsg_buff->inreq->buf =
+ buff->fsg_buff->outreq->buf =
+ buff->fsg_buff->buf;
+ buff->fsg_buff->inreq->context =
+ buff->fsg_buff->outreq->context =
+ buff->fsg_buff;
+ }
+
+ /* Allocate command ep request */
+ uaspd->cmd_buff.outreq = usb_ep_alloc_request(uaspd->command,
+ GFP_ATOMIC);
+ if (!uaspd->cmd_buff.outreq) {
+ ERROR(uaspd->ucommon->common, "failed allocating outreq for "
+ "command buffer\n");
+ goto reset_uasp;
+ }
+
+ DBG(uaspd->ucommon->common, "%s() allocated command request = %p, "
+ "udev=%p\n", __func__,
+ uaspd->cmd_buff.outreq, uaspd);
+ uaspd->cmd_buff.outreq->buf = &(uaspd->cmd_buff.buf);
+ uaspd->cmd_buff.inreq = NULL;
+ uaspd->cmd_buff.state = BUF_STATE_EMPTY;
+
+ fcommon->running = 1;
+ for (i = 0; i < fsgd->common->nluns; ++i)
+ fsgd->common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ return 0;
+}
+
+static void handle_uasp_exception(struct uasp_common *ucommon)
+{
+ siginfo_t info;
+ int sig;
+ int i;
+ struct fsg_buffhd *bh;
+ enum fsg_state old_state;
+ int rc;
+
+ struct fsg_common *fcommon = ucommon->common;
+
+ DBG(ucommon->common, "%s()- Enter\n", __func__);
+
+ /*
+ * Clear the existing signals. Anything but SIGUSR1 is converted
+ * into a high-priority EXIT exception.
+ */
+ for (;;) {
+ sig = dequeue_signal_lock(current, ¤t->blocked, &info);
+ if (!sig)
+ break;
+ if (sig != SIGUSR1) {
+ if (fcommon->state < FSG_STATE_EXIT)
+ DBG(fcommon, "Main thread exiting on signal\n");
+ fcommon->state = FSG_STATE_EXIT;
+ }
+ }
+
+ /*
+ * Reset the I/O buffer states and pointers, the SCSI state, and the
+ * exception. Then invoke the handler.
+ */
+ spin_lock_irq(&fcommon->lock);
+
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &fcommon->buffhds[i];
+ bh->state = BUF_STATE_EMPTY;
+ }
+ old_state = fcommon->state;
+ fcommon->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&fcommon->lock);
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state) {
+ case FSG_STATE_ABORT_BULK_OUT:
+ case FSG_STATE_RESET:
+ /* TODO */
+ break;
+
+ case FSG_STATE_CONFIG_CHANGE:
+ if (fcommon->fsg == fcommon->new_fsg) {
+ DBG(fcommon, "nothing to do. same config\n");
+ break;
+ }
+ /* Enable/disable the interface according to the new_config */
+ rc = do_uasp_set_interface(ucommon->udev, fcommon->new_fsg);
+ if (rc != 0)
+ fcommon->fsg = NULL; /* Reset on errors */
+ break;
+ case FSG_STATE_EXIT:
+ case FSG_STATE_TERMINATED:
+ /* Free resources */
+ (void)do_uasp_set_interface(ucommon->udev, NULL);
+ spin_lock_irq(&fcommon->lock);
+ fcommon->state = FSG_STATE_TERMINATED; /* Stop the thread*/
+ spin_unlock_irq(&fcommon->lock);
+ break;
+
+ case FSG_STATE_INTERFACE_CHANGE:
+ case FSG_STATE_DISCONNECT:
+ case FSG_STATE_COMMAND_PHASE:
+ case FSG_STATE_DATA_PHASE:
+ case FSG_STATE_STATUS_PHASE:
+ case FSG_STATE_IDLE:
+ break;
+ }
+ DBG(ucommon->common, "%s()- Exit\n", __func__);
+}
+
+/**
+ * uasp_command_check() - Verifies the data received via command endpoint for
+ * accuracy.
+ * @udev: Programming view of uasp device.
+ * @command: Pointer to the received data buffer.
+ *
+ * Return 0 - if no error condition and the command is a
+ * COMMAND IU
+ * 1 - if no error condition and the command is a TASK
+ * MANAGEMENT IU
+ * negative value on error.
+ *
+ * If the command is valid returns it by reference in command
+ * param. The following steps are implemented in this function:
+ * - Checking that the received data is a TM FUNCTION or COMMAND IU.
+ * - Chekcing that the length of the received data is correct
+ * (16 bytes for TM FUNCTION IU and 36 bytes for COMMAND IU).
+ * - Checking that there is no overlapped tag.
+ */
+static int uasp_command_check(struct uasp_dev *udev, void **command)
+{
+ int i = 0;
+ int rc = 0;
+ unsigned long flags;
+ __be16 tag;
+ uint8_t cmd_id;
+ struct uasp_lun *curlun;
+ struct cmd_iu *cmdiu, *tmp_cmdiu;
+ struct tm_iu *tmiu, *tmp_tmiu;
+ struct fsg_buffhd *bh;
+ struct usb_request *req;
+
+ bh = &(udev->cmd_buff);
+ bh->state = BUF_STATE_EMPTY;
+ req = udev->cmd_buff.outreq;
+ *command = NULL;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Id of the received command (tmiu or cmdiu) */
+ cmd_id = ((uint8_t *)req->buf)[0];
+
+ /* tag of the received command */
+ tag = ((__be16 *)req->buf)[1];
+
+ /* Invalid completion status */
+ if (req->status) {
+ ERROR(udev->ucommon->common,
+ "%s() - Invalid completion status for command "
+ "request = -%d\n", __func__, req->status);
+ return -EINVAL;
+ }
+
+ /* Check is the data received via command endpoint is a command */
+ if (cmd_id != IU_ID_TASK_MANAGEMENT && cmd_id != IU_ID_COMMAND) {
+ ERROR(udev->ucommon->common,
+ "%s() - Invalid data is received\n", __func__);
+ /* TODO: something needs to be done (e.g. halt endpoints) */
+ return -EINVAL;
+ }
+
+ /* Invalid count of bytes received for tmiu */
+ if (cmd_id == IU_ID_TASK_MANAGEMENT && req->actual != 16) {
+ ERROR(udev->ucommon->common,
+ "%s() - Invalid byte count for tmiu is received = %d\n",
+ __func__, req->actual);
+ /* TODO: something needs to be done (e.g. halt endpoints) */
+ return -EINVAL;
+ }
+
+ /* Invalid count of bytes received for cmdiu */
+ if (cmd_id == IU_ID_COMMAND && req->actual < 32) {
+ ERROR(udev->ucommon->common,
+ "%s() - Invalid byte count for cmdiu is received = %d\n",
+ __func__, req->actual);
+ /* TODO: something needs to be done (e.g. halt endpoints) */
+ return -EINVAL;
+ }
+
+ /* Try to allocate memory for received command */
+ tmiu = NULL;
+ cmdiu = NULL;
+
+ if (cmd_id == IU_ID_TASK_MANAGEMENT) {
+ tmiu = kmalloc(sizeof(struct tm_iu), GFP_KERNEL);
+
+ if (!tmiu) {
+ ERROR(udev->ucommon->common,
+ "%s() - No memory for tmiu\n", __func__);
+ return -ENOMEM;
+ }
+ *command = tmiu;
+ memcpy(*command, req->buf, 16);
+ } else {
+ cmdiu = kmalloc(sizeof(struct cmd_iu), GFP_KERNEL);
+
+ if (!cmdiu) {
+ ERROR(udev->ucommon->common,
+ "%s() - No memory for cmdiu\n", __func__);
+ return -ENOMEM;
+ }
+ *command = cmdiu;
+ memcpy(*command, req->buf, req->actual);
+ }
+
+ /* Check for overlapping tag */
+ /* Check for tag overlapping over all cmd an tm_func queues */
+ for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ curlun = &udev->ucommon->uluns[i];
+ spin_lock_irqsave(&(curlun->lock), flags);
+
+ list_for_each_entry(tmp_cmdiu, &curlun->cmd_queue, node) {
+ if (tmp_cmdiu->state != COMMAND_STATE_IDLE &&
+ tmp_cmdiu->state != COMMAND_STATE_DATA &&
+ tmp_cmdiu->state != COMMAND_STATE_STATUS) {
+ continue;
+ }
+ /* Overlapped tag found */
+ if (tmp_cmdiu->tag == tag) {
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ goto overlapped_tag;
+ }
+ }
+
+ list_for_each_entry(tmp_tmiu, &curlun->tm_func_queue, node) {
+
+ if (tmp_tmiu->state != COMMAND_STATE_IDLE &&
+ tmp_tmiu->state != COMMAND_STATE_STATUS)
+ continue;
+ /* Overlapped tag found */
+ if (tmp_tmiu->tag == tag)
+ goto overlapped_tag;
+ }
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ }
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ list_for_each_entry(tmp_cmdiu, &udev->cmd_queue, node) {
+ if (tmp_cmdiu->state != COMMAND_STATE_IDLE &&
+ tmp_cmdiu->state != COMMAND_STATE_DATA &&
+ tmp_cmdiu->state != COMMAND_STATE_STATUS)
+ continue;
+
+ /* Overlapped tag found */
+ if (tmp_cmdiu->tag == tag) {
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+ goto overlapped_tag;
+ }
+ }
+
+ list_for_each_entry(tmp_tmiu, &udev->tm_func_queue, node) {
+ if (tmp_tmiu->state != COMMAND_STATE_IDLE &&
+ tmp_tmiu->state != COMMAND_STATE_STATUS)
+ continue;
+
+ /* Overlapped tag found */
+ if (tmp_tmiu->tag == tag) {
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+ goto overlapped_tag;
+ }
+ }
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ /* No overlapped tag */
+ if (cmd_id == IU_ID_TASK_MANAGEMENT)
+ return 0;
+
+ return 1;
+
+overlapped_tag:
+ ERROR(udev->ucommon->common, "%s() - Overlapped tag found. "
+ "Aborting all\n", __func__);
+
+ run_lun_threads(udev, LUN_STATE_OVERLAPPED_TAG);
+
+ /* Wait for luns abort completion. Sleep if luns are in processing */
+ while (!all_lun_state_non_processing(udev)) {
+ DBG(udev->ucommon->common,
+ "%s() - Luns are in process. Going to sleep\n", __func__);
+ rc = sleep_thread(udev->ucommon->common);
+ if (rc) {
+ ERROR(udev->ucommon->common,
+ "%s() - sleep_thread failed! (%d)", __func__, rc);
+ return -EINVAL;
+ }
+ DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+ rc = 0;
+ }
+
+ /* Abort none-lun commands */
+ abort_commands(udev, &udev->cmd_queue, &udev->tm_func_queue,
+ &(udev->ucommon->common->lock));
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ udev->pending_requests = 0;
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ if (cmd_id == IU_ID_TASK_MANAGEMENT) {
+ tmiu = *command;
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ tmiu->bh = get_buffhd(udev->ucommon->common->buffhds);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ if (!tmiu->bh) {
+ ERROR(udev->ucommon->common,
+ "%s(): didnt manage to get buffers for tmiu!\n",
+ __func__);
+ return -EINVAL;
+ }
+ fill_response_iu(udev, (struct response_iu *)tmiu->bh->buf,
+ tmiu->tag, 0,
+ RESPONSE_OVERLAPPED_TAG_ATTEMPTED);
+ fill_usb_request(tmiu->bh->inreq, tmiu->bh->buf,
+ UASP_SIZEOF_RESPONSE_IU, 0,
+ (void *)tmiu, 0,
+ be16_to_cpup(&tmiu->tag), status_complete);
+
+ tmiu->ep = udev->status;
+ tmiu->bh->inreq_busy = 1;
+ if (usb_ep_queue(tmiu->ep, tmiu->bh->inreq, 0))
+ tmiu->state = COMMAND_STATE_FAILED;
+ else
+ tmiu->state = COMMAND_STATE_STATUS;
+ list_add_tail(&tmiu->node, &(udev->tm_func_queue));
+ } else {
+ cmdiu = *command;
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ cmdiu->bh = get_buffhd(udev->ucommon->common->buffhds);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ if (!cmdiu->bh) {
+ ERROR(udev->ucommon->common,
+ "%s(): didnt manage to get buffers for cmdiu!\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ fill_sense_iu(udev, (struct sense_iu *)cmdiu->bh->buf,
+ cmdiu->tag, STATUS_CHECK_CONDITION,
+ SS_OVERLAPPED_COMMANDS_ATTEMPTED);
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
+ UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete);
+ cmdiu->ep = udev->status;
+ cmdiu->bh->inreq_busy = 1;
+ if (usb_ep_queue(cmdiu->ep, cmdiu->bh->inreq, 0))
+ cmdiu->state = COMMAND_STATE_FAILED;
+ else
+ cmdiu->state = COMMAND_STATE_STATUS;
+ list_add_tail(&cmdiu->node, &(udev->cmd_queue));
+ }
+ return -EINVAL;
+}
+
+/**
+ * insert_tm_func_to_list() - Insert the tmiu to the appropriate queue
+ * @udev: Programming view of uasp device.
+ * @tmiu: the tmiu to place in the appropriate queue
+ *
+ * This function tries to allocate the LUN corresponding to the LUN id in the
+ * received tmiu. If such LUN is found the tmiu is placed in it's tm_func_queue.
+ * If the LUN wasn't found then the tmiu will be placed in the general
+ * tm_func_queue .
+ *
+ * TODO: Should this be protected by locks?
+ */
+static void insert_tm_func_to_list(struct uasp_dev *udev, struct tm_iu *tmiu)
+{
+ struct tm_iu *tmiu1;
+ struct uasp_lun *curlun;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ curlun = find_lun_by_id(udev, tmiu->lun);
+ tmiu->state = COMMAND_STATE_IDLE;
+
+ if (tmiu->tm_function == TM_FUNCTION_IT_NEXUS_RESET) {
+ list_add(&tmiu->node, &udev->tm_func_queue);
+ return;
+ }
+
+ if (!curlun) {
+ list_add_tail(&tmiu->node, &udev->tm_func_queue);
+ return;
+ }
+
+ /* tmiu should be handled by curlun */
+
+ if (tmiu->tm_function == TM_FUNCTION_RESET_LUN) {
+ list_add(&tmiu->node, &curlun->tm_func_queue);
+ return;
+ }
+
+ /*
+ * Insert tmiu to queue acording to the folowing priority:
+ * 1.TM_FUNCTION_RESET_LUN
+ * 2. TM_FUNCTION_ABORT_TASK_SET or TM_FUNCTION_CLEAR_TASK_SET
+ * 3. TM_FUNCTION_ABORT_TASK
+ * 4. TM_FUNCTION_QUERY_ASYNC_EVENT
+ * All other...
+ */
+ list_for_each_entry(tmiu1, &curlun->tm_func_queue, node) {
+ if (tmiu1->tm_function == TM_FUNCTION_RESET_LUN)
+ continue;
+
+ if (tmiu->tm_function == TM_FUNCTION_ABORT_TASK_SET ||
+ tmiu->tm_function == TM_FUNCTION_CLEAR_TASK_SET) {
+ list_add(&tmiu->node, &tmiu1->node);
+ return;
+ }
+
+ if (tmiu1->tm_function == TM_FUNCTION_ABORT_TASK_SET ||
+ tmiu1->tm_function == TM_FUNCTION_CLEAR_TASK_SET)
+ continue;
+
+ if (tmiu->tm_function == TM_FUNCTION_ABORT_TASK) {
+ list_add(&tmiu->node, &tmiu1->node);
+ return;
+ }
+
+ if (tmiu1->tm_function == TM_FUNCTION_ABORT_TASK)
+ continue;
+
+ if (tmiu->tm_function == TM_FUNCTION_QUERY_ASYNC_EVENT) {
+ list_add(&tmiu->node, &tmiu1->node);
+ return;
+ }
+
+ if (tmiu1->tm_function == TM_FUNCTION_QUERY_ASYNC_EVENT)
+ continue;
+
+ list_add_tail(&tmiu->node, &tmiu1->node);
+ return;
+ }
+
+ list_add_tail(&tmiu->node, &tmiu1->node);
+}
+
+/**
+ * insert_cmd_to_list() - Insert the tmiu to the appropriate queue
+ * @udev: Programming view of uasp device.
+ * @cmdiu: the cmdiu to place in the appropriate queue
+ *
+ * This function tries to locate the LUN corresponding to the LUN id in the
+ * received cmdiu. If such LUN is found the cmdiu is placed in it's cmd_queue.
+ * If the LUN wasn't found then the cmdiu will be placed in the general
+ * cmd_queue.
+ *
+ * TODO: Should this be protected by locks?
+ */
+static void insert_cmd_to_list(struct uasp_dev *udev, struct cmd_iu *cmdiu)
+{
+ struct list_head *link;
+ struct cmd_iu *cmdiu1;
+ struct uasp_lun *curlun;
+
+ DBG(udev->ucommon->common, "%s(): cmdiu->lun = %p\n", __func__,
+ cmdiu->lun);
+
+ DBG(udev->ucommon->common, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ cmdiu->lun[0], cmdiu->lun[1], cmdiu->lun[2], cmdiu->lun[3],
+ cmdiu->lun[4], cmdiu->lun[5], cmdiu->lun[6], cmdiu->lun[7]);
+
+ curlun = find_lun_by_id(udev, cmdiu->lun);
+ cmdiu->state = COMMAND_STATE_IDLE;
+
+ if (!curlun)
+ link = &udev->cmd_queue;
+ else
+ link = &curlun->cmd_queue;
+
+ /* Place cmdiu in the queue, in the right place */
+ if (cmdiu->forth_byte.task_attribute == TASK_ATTR_ACA) {
+ list_add(&cmdiu->node, link);
+ return;
+ }
+
+ list_for_each_entry(cmdiu1, link, node) {
+ /* ACA should be in the head of the queue */
+ if (cmdiu1->forth_byte.task_attribute == TASK_ATTR_ACA)
+ continue;
+
+ /* The new HEAD OF QUEUE should be placed after ACA */
+ if (cmdiu1->forth_byte.task_attribute ==
+ TASK_ATTR_HEAD_OF_QUEUE) {
+ list_add(&cmdiu->node, &cmdiu1->node);
+ return;
+ }
+
+ /* If ORDERED or SIMPLE, place at the end of the queue */
+ list_add_tail(&cmdiu->node, link);
+ return;
+ }
+
+ /* In the case when the queue is empty */
+ list_add_tail(&cmdiu->node, link);
+ DBG(udev->ucommon->common,
+ "%s() - Cmdiu is added to the tail of the queue\n", __func__);
+}
+
+/**
+ * get_command() - Gets the next command from command emdpoint
+ * @udev: Programming view of uasp device.
+ *
+ * Return 0 if no error condition, negative value otherwise.
+ *
+ * This function is responsible for:
+ * - Utilizing the command endpoint.
+ * - Checking of the received data for accuracy. For more details please see
+ * the description of the uasp_command_check() function.
+ * - Adding the received TM FUNCTION or COMMAND IU to the appropriate queue.
+ */
+static int get_command(struct uasp_dev *udev)
+{
+ int rc = 0;
+ void *command = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+queue_cmd_ep:
+ /* If command endpoint is not active, activate */
+ if (udev->cmd_buff.state == BUF_STATE_EMPTY) {
+ udev->cmd_buff.state = BUF_STATE_BUSY;
+ /* Queue a request to read the next command */
+ udev->cmd_buff.outreq->buf = udev->cmd_buff.buf;
+ udev->cmd_buff.outreq->complete = command_complete;
+ udev->cmd_buff.outreq->length =
+ (FSG_BUFLEN < udev->command->maxpacket ? FSG_BUFLEN :
+ udev->command->maxpacket);
+ udev->cmd_buff.outreq->short_not_ok = 1;
+ rc = usb_ep_queue(udev->command, udev->cmd_buff.outreq, 0);
+ if (rc) {
+ ERROR(udev->ucommon->common,
+ "%s()usb_ep_queue failed = %d\n", __func__, rc);
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
+ }
+ DBG(udev->ucommon->common, "%s() queued command request = %p\n",
+ __func__, udev->cmd_buff.outreq);
+ return rc;
+ }
+ /* If command endpoint is busy, do nothing */
+ else if (udev->cmd_buff.state == BUF_STATE_BUSY)
+ return rc;
+
+ rc = uasp_command_check(udev, &command);
+
+ if (rc == 0) {
+ DBG(udev->ucommon->common, "%s() - Received a TMC IU\n",
+ __func__);
+ insert_tm_func_to_list(udev, (struct tm_iu *)command);
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
+ goto queue_cmd_ep;
+ } else if (rc == 1) {
+ DBG(udev->ucommon->common, "%s() -Received a CMD IU\n",
+ __func__);
+ insert_cmd_to_list(udev, (struct cmd_iu *)command);
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
+ goto queue_cmd_ep;
+ }
+
+ return rc;
+}
+
+/**
+ * all_lun_state_non_processing() - Returns 1, if all luns are in
+ * none-processing state
+ * @udev: Programming view of uasp device
+ *
+ */
+int all_lun_state_non_processing(struct uasp_dev *udev)
+{
+ struct uasp_lun *curlun;
+ int i;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ curlun = &udev->ucommon->uluns[i];
+ if (curlun->lun_state > LUN_STATE_IDLE)
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * pending_cmd_in_lun() - Checks for pending IUs in all LUNs queues
+ * @data: Programming view of uasp device
+ *
+ * Returns 1 if all luns are in non-processing state and and if are incomplete
+ * or pending commands in one of the luns.
+ */
+static int pending_cmd_in_lun(void *data)
+{
+ struct uasp_dev *udev = (struct uasp_dev *)data;
+ struct uasp_lun *curlun;
+ int i;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ curlun = &udev->ucommon->uluns[i];
+ if (curlun->pending_requests)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int sleep_lun_thread(struct uasp_lun *lun)
+{
+ int rc = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (lun->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+ lun->thread_wakeup_needed = 0;
+ return rc;
+}
+
+/**
+ * run_lun_threads() - Wakeup all LUN threads with a given state
+ * @udev: Programming view of uasp device
+ * @state: The state to run the LUn in (from enum lun_state)
+ *
+ */
+void run_lun_threads(struct uasp_dev *udev, int state)
+{
+ struct uasp_lun *curlun;
+ int i;
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter. State = %d\n",
+ __func__, state);
+
+ for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ curlun = &udev->ucommon->uluns[i];
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->lun_state = state;
+ wakeup_lun_thread(curlun);
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ }
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+}
+
+/**
+ * abort_commands() - Aborts all IUs on given queues
+ * @udev: Programming view of uasp device
+ * @cmd_queue: pointer to the cmd IUs queue to abort IUs from
+ * @tm_func_queue: pointer to the tm IUs queue to abort IUs from
+ * @lock: pointer to spinlock_t to lock when performing the abort.
+ * Can be udev->lock if the cmd_queue and the tm_func_queue are general,
+ * or curlun->lock if they belong to a specific LUN
+ *
+ * TODO: Add wait mechanism using curlun->active_requests or
+ * udev->active_requests
+ */
+void abort_commands(struct uasp_dev *udev,
+ struct list_head *cmd_queue,
+ struct list_head *tm_func_queue,
+ spinlock_t *lock)
+{
+ unsigned long flags;
+ struct cmd_iu *cmdiu, *tmp_cmdiu;
+ struct tm_iu *tmiu, *tmp_tmiu;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (!cmd_queue)
+ goto tmiu_part;
+
+ spin_lock_irqsave(lock, flags);
+ list_for_each_entry_safe(cmdiu, tmp_cmdiu, cmd_queue, node) {
+ if (cmdiu->state == COMMAND_STATE_DATA) {
+ if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
+ spin_unlock_irqrestore(lock, flags);
+ if (cmdiu->bh->inreq_busy)
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->inreq);
+ if (cmdiu->bh->outreq_busy)
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->outreq);
+ spin_lock_irqsave(lock, flags);
+ }
+ } else if (cmdiu->state == COMMAND_STATE_STATUS) {
+ spin_unlock_irqrestore(lock, flags);
+ usb_ep_dequeue(cmdiu->ep, cmdiu->bh->inreq);
+ spin_lock_irqsave(lock, flags);
+ } else
+ cmdiu->state = COMMAND_STATE_ABORTED;
+ }
+ spin_unlock_irqrestore(lock, flags);
+
+tmiu_part:
+ if (!tm_func_queue)
+ return;
+
+ spin_lock_irqsave(lock, flags);
+ list_for_each_entry_safe(tmiu, tmp_tmiu, tm_func_queue, node) {
+ if (tmiu->state == COMMAND_STATE_STATUS) {
+ spin_unlock_irqrestore(lock, flags);
+ if (tmiu->bh->inreq_busy)
+ usb_ep_dequeue(tmiu->ep, tmiu->bh->inreq);
+ spin_lock_irqsave(lock, flags);
+ } else
+ tmiu->state = COMMAND_STATE_ABORTED;
+ }
+ spin_unlock_irqrestore(lock, flags);
+}
+
+/**
+ * do_uasp() - UASP main routine
+ * @udev: Programming view of uasp device
+ *
+ * This function is responsible for operating based on UASP protocol. It is
+ * responsible for:
+ * - Getting and initial processing of TM FUNCTION IU or COMMAND IU received
+ * from HOST side via command endpoint. For more details please
+ * see the description of get_command() function.
+ * - Processing of TM FUNCTION IUs addressed to IT_NEXUS. For more details
+ * please see the tmiu.c file.
+ * - Processing of COMMAND IUs addressed to IT_NEXUS. For more details
+ * please see the tmiu.c file.
+ * - Running the threads which are responsible for processing of TM FUNCTION
+ * and COMMAND IUs addressed to a certain LUN. For more details please see
+ * the description of fsg_lun_thread(void *_lun) function.
+ */
+void do_uasp(struct uasp_dev *udev)
+{
+ unsigned long flags;
+ struct fsg_dev *fsg = &(udev->fsg_dev);
+ struct uasp_common *ucommon = udev->ucommon;
+ int rc;
+
+ DBG(ucommon->common, "%s() - Enter\n", __func__);
+
+ spin_lock_irqsave(&(fsg->common->lock), flags);
+ if (!exception_in_progress(fsg->common))
+ fsg->common->state = FSG_STATE_COMMAND_PHASE;
+ spin_unlock_irqrestore(&(fsg->common->lock), flags);
+
+ if (exception_in_progress(fsg->common))
+ return;
+
+ if (get_command(udev))
+ return;
+
+ spin_lock_irqsave(&(fsg->common->lock), flags);
+ if (!exception_in_progress(fsg->common))
+ fsg->common->state = FSG_STATE_DATA_PHASE;
+ spin_unlock_irqrestore(&(fsg->common->lock), flags);
+
+ if (exception_in_progress(fsg->common))
+ return;
+
+ spin_lock_irqsave(&(ucommon->common->lock), flags);
+ udev->pending_requests = 0;
+ spin_unlock_irqrestore(&(ucommon->common->lock), flags);
+
+ do_tmiu(udev, NULL);
+ if (exception_in_progress(fsg->common))
+ return;
+
+ do_cmdiu(udev, NULL);
+ if (exception_in_progress(fsg->common))
+ return;
+
+ remove_completed_commands(udev, &udev->cmd_queue, &udev->tm_func_queue);
+
+ spin_lock_irqsave(&(fsg->common->lock), flags);
+ if (!exception_in_progress(fsg->common)) {
+ fsg->common->state = FSG_STATE_IDLE;
+ spin_unlock_irqrestore(&(fsg->common->lock), flags);
+ run_lun_threads(udev, LUN_STATE_PROCESSING);
+ } else
+ spin_unlock_irqrestore(&(fsg->common->lock), flags);
+
+ rc = 0;
+ while (!rc) {
+ /* If exception is in progress */
+ if (exception_in_progress(ucommon->common)) {
+ DBG(ucommon->common,
+ "%s() - Exception is in progress\n", __func__);
+ return;
+ }
+
+ /* Sleep if luns are in processing */
+ rc = all_lun_state_non_processing(udev);
+ if (!rc) {
+ DBG(ucommon->common,
+ "%s() - Luns are in process\n", __func__);
+ goto sleep;
+ }
+
+ /* Wake up if command is received */
+ if (udev->cmd_buff.state == BUF_STATE_FULL) {
+ DBG(ucommon->common,
+ "%s() - Command is received\n", __func__);
+ return;
+ }
+
+ /* Wake up if there are pending requests in luns */
+ if (pending_cmd_in_lun(udev)) {
+ DBG(ucommon->common,
+ "%s() - Pending requests in LUN\n", __func__);
+ return;
+ }
+
+ /* Wake up if there are pending requests */
+ if (udev->pending_requests) {
+ DBG(ucommon->common,
+ "%s() - Pending requests in device\n",
+ __func__);
+ return;
+ }
+sleep:
+ /* Try to sleep */
+ DBG(ucommon->common, "%s() - Going to sleep\n", __func__);
+ rc = sleep_thread(fsg->common);
+ if (rc)
+ return;
+ DBG(ucommon->common, "%s() - Wakes up\n", __func__);
+
+ rc = 0;
+ }
+}
+
+/**
+ * close_lun() - Close the backing file of the given LUN
+ * @ulun: pointer to the LUn to close
+ *
+ * This function should be called when fsg_common->filesem is taken!
+ */
+void close_lun(struct uasp_lun *ulun)
+{
+ struct fsg_lun *fsglun = ulun->lun;
+
+ if (!fsg_lun_is_open(fsglun))
+ return;
+
+ fsg_lun_close(fsglun);
+ fsglun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+}
+
+static int lun_exception_in_progress(struct uasp_lun *curlun)
+{
+ if (curlun->lun_state > LUN_STATE_PROCESSING)
+ return 1;
+
+ return 0;
+}
+
+/**
+ * handle_lun_exception() - Abort all TM and CMD IUs for a given LUN
+ * @udev: Programming view of file storage gadget.
+ * @curlun: Pointer to struct uasp_lun structure.
+ *
+ * This function is responsible for aborting the active TM FUNCTION and
+ * COMMAND IUs connected to the curlun, removing all TM FUNCTION and COMMAND
+ * IUs from appropriate queues, and keeping the exception data if there is a
+ * need.
+ */
+static void handle_lun_exception(struct uasp_dev *udev, struct uasp_lun *curlun)
+{
+ unsigned long flags;
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Abort all commands and remove them from lists */
+ abort_commands(udev, &curlun->cmd_queue, &curlun->tm_func_queue,
+ &(curlun->lock));
+ remove_completed_commands(udev, &curlun->cmd_queue,
+ &curlun->tm_func_queue);
+ curlun->pending_requests = 0;
+
+ spin_lock_irqsave(&(curlun->lock), flags);
+ switch (curlun->lun_state) {
+ case LUN_STATE_RESET:
+ curlun->lun->unit_attention_data = SS_RESET_OCCURRED;
+ case LUN_STATE_OVERLAPPED_TAG:
+ curlun->lun_state = LUN_STATE_PROCESSING;
+ break;
+ case LUN_STATE_EXIT:
+ curlun->lun_state = LUN_STATE_TERMINATED;
+ break;
+ default:
+ break;
+ }
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+}
+
+/**
+ * uasp_lun_thread() - UASP LUN main thread
+ * @param: pointer to the uasp LUN structure
+ *
+ * Returns 0 on success -1 otherwise
+ *
+ * This function is UASP LUN main thread. It consist of a while loop that
+ * performs the following as long as the LUN state isn't terminated:
+ * - handles LUN exceptions if such exist
+ * - handles LUN specific cmd IUs
+ * - handles LUN specific tm IUs
+ * - removes completed IUs from cmd and tm queues
+ */
+static int uasp_lun_thread(void *param)
+{
+ struct uasp_lun *ulun = (struct uasp_lun *)param;
+ struct uasp_dev *udev;
+ unsigned long flags;
+
+ if (!ulun)
+ return -1;
+ udev = ulun->dev;
+ DBG(udev->ucommon->common, "%s() - Enter for lun_id = %d\n", __func__,
+ ulun->lun_id[7]);
+
+ while (ulun->lun_state != LUN_STATE_TERMINATED) {
+ DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+
+ if (lun_exception_in_progress(ulun)) {
+ DBG(udev->ucommon->common,
+ "%s() - exception_in_progress!"
+ " ulun->lun_state=%d\n", __func__,
+ ulun->lun_state);
+ handle_lun_exception(udev, ulun);
+ continue;
+ }
+
+ /*
+ * If the main thread isn't running, no need to run lun threads
+ * as well.
+ */
+ if (!udev->ucommon->common->running) {
+ DBG(udev->ucommon->common,
+ "%s() - uasp thread main thread not running - "
+ "going to sleep...\n", __func__);
+ sleep_lun_thread(ulun);
+ continue;
+ }
+
+ spin_lock_irqsave(&(ulun->lock), flags);
+ ulun->pending_requests = 0;
+ spin_unlock_irqrestore(&(ulun->lock), flags);
+
+ do_tmiu(udev, ulun);
+ if (lun_exception_in_progress(ulun))
+ continue;
+
+ do_cmdiu(udev, ulun);
+ if (lun_exception_in_progress(ulun))
+ continue;
+
+ remove_completed_commands(udev, &ulun->cmd_queue,
+ &ulun->tm_func_queue);
+ if (lun_exception_in_progress(ulun))
+ continue;
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ if (!lun_exception_in_progress(ulun)) {
+ ulun->lun_state = LUN_STATE_IDLE;
+ wakeup_thread(udev->ucommon->common);
+ }
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ DBG(udev->ucommon->common, "%s() - Going to sleep\n", __func__);
+ sleep_lun_thread(ulun);
+ continue;
+ }
+
+ DBG(udev->ucommon->common, "uasp lun main loop: exiting\n");
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ ulun->lun_thread_task = NULL;
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ /* Let the unbind and cleanup routines know the thread has exited */
+ complete_and_exit(&ulun->thread_notifier, 0);
+ return 0;
+}
+
+/**
+ * uasp_main_thread() - UASP main thread
+ * @param: pointer to the uasp_common structure
+ *
+ * This function is UASP main thread. It consist of a while loop that performs
+ * the following as long as the state isn't terminated:
+ * - handles UASP device exceptions if such exist
+ * - calles do_uasp() (see do_uasp() function for more details)
+ * - when state is terminated closed all LUNS
+ */
+static int uasp_main_thread(void *param)
+{
+ struct uasp_common *ucommon = (struct uasp_common *)param;
+ struct fsg_common *common = ucommon->common;
+
+ /*
+ * Allow the thread to be killed by a signal, but set the signal mask
+ * to block everything but INT, TERM, KILL, and USR1.
+ */
+ allow_signal(SIGINT);
+ allow_signal(SIGTERM);
+ allow_signal(SIGKILL);
+ allow_signal(SIGUSR1);
+
+ /* 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.
+ */
+ set_fs(get_ds());
+
+ /* The main loop */
+ while (common->state != FSG_STATE_TERMINATED) {
+ DBG(common, "uasp main loop: continuing\n");
+ if (exception_in_progress(ucommon->common) ||
+ signal_pending(current)) {
+ DBG(common, "uasp thread main loop: exception\n");
+ handle_uasp_exception(ucommon);
+ continue;
+ }
+
+ if (!common->running) {
+ DBG(common, "uasp thread main loop: not running\n");
+ sleep_thread(ucommon->common);
+ continue;
+ }
+ do_uasp(ucommon->udev);
+ }
+
+ DBG(common, "uasp main loop: exiting\n");
+
+ spin_lock_irq(&common->lock);
+ common->thread_task = NULL;
+ spin_unlock_irq(&common->lock);
+
+ if (!common->ops || !common->ops->thread_exits ||
+ common->ops->thread_exits(common) < 0) {
+ struct uasp_lun *ulun = ucommon->uluns;
+ unsigned i ;
+ down_write(&common->filesem);
+ for (i = 0; i < common->nluns; i++, ulun++)
+ close_lun(ulun);
+ up_write(&common->filesem);
+ }
+
+ /* Let the unbind and cleanup routines know the thread has exited */
+ complete_and_exit(&common->thread_notifier, 0);
+
+ return 0;
+}
+
+/**
+ * uasp_common_init() - Init uasp_common data structure
+ * @common: pointer to inited fsg_common data structure
+ * @cdev: pointer to usb_composite device that the UASP function is a part of
+ * @cfg: pointer to fsg_config data structure
+ *
+ * This function should be called after (struct fsg_common) common was already
+ * initiated by fsg_common_init
+ */
+static struct uasp_common *uasp_common_init(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ struct fsg_config *cfg)
+{
+ struct fsg_lun *flun;
+ struct uasp_lun *ulun;
+ struct uasp_common *ucommon;
+ int nluns = common->nluns;
+ int i, rc;
+
+ if (!common || !cdev || !cfg)
+ return NULL;
+
+ DBG(common, "%s() - Enter\n", __func__);
+
+ ucommon = kzalloc(sizeof *ucommon, GFP_KERNEL);
+ if (unlikely(!ucommon))
+ return NULL;
+
+ /* Save reference to fsg_common structure in ucommon */
+ ucommon->common = common;
+
+ /* Allocate the uLUNs and init them according to fsg_common luns */
+ ulun = kzalloc(nluns * sizeof *ulun, GFP_KERNEL);
+ if (!ulun) {
+ kfree(ucommon);
+ return ERR_PTR(-ENOMEM);
+ }
+ ucommon->uluns = ulun;
+
+ /* Create the reference between ulun and fsg_lun */
+ for (i = 0, flun = common->luns; i < nluns;
+ ++i, ++flun, ++ulun)
+ ulun->lun = flun;
+
+ /*
+ * Buffers in ubufs are static -- no need for additional allocation.
+ * Connect each ubuf to fsg_buff from the buffhds cyclic list
+ */
+ for (i = 0; i < fsg_num_buffers; i++) {
+ ucommon->ubufs[i].fsg_buff = &(common->buffhds[i]);
+ ucommon->ubufs[i].ep = NULL;
+ ucommon->ubufs[i].stream_id = 0;
+ }
+
+ kref_init(&ucommon->ref);
+ /* Tell the thread to start working */
+ common->thread_task =
+ kthread_create(uasp_main_thread, (void *)ucommon,
+ cfg->thread_name ?: "file-storage-UASP");
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
+ }
+
+
+ /* Information */
+ INFO(common, UASP_DRIVER_DESC ", version: " UASP_DRIVER_VERSION "\n");
+ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+ wake_up_process(common->thread_task);
+
+ return ucommon;
+
+error_release:
+ common->state = FSG_STATE_TERMINATED; /* The thread is dead */
+ /* Call uasp_common_release() directly, ref might be not initialised */
+ uasp_common_release(&common->ref);
+ return ERR_PTR(rc);
+}
+
+/**
+ * finish_lun_init() - Finish the LUN structure inialization
+ * @udev: Programming view of file storage gadget.
+ *
+ * This function is used to init the uasp_lun fileds. It's called from uasp_add
+ * after the uasp_dev was allocated. It creates (and starts) all lun tasks
+ */
+static int finish_lun_init(struct uasp_dev *udev)
+{
+ int i, j, rc = 0;
+ struct uasp_lun *ulun = NULL;
+ char thread_name[20];
+
+ if (!udev)
+ return -EIO;
+
+ for (i = 0, ulun = udev->ucommon->uluns;
+ i < udev->ucommon->common->nluns; i++, ulun++) {
+ /* TODO: this is a workaround, fix later */
+ memset(ulun->lun_id, 0, 8);
+ ulun->lun_id[0] = i;
+ INIT_LIST_HEAD(&ulun->cmd_queue);
+ INIT_LIST_HEAD(&ulun->tm_func_queue);
+ ulun->lun_state = LUN_STATE_IDLE;
+ ulun->dev = udev;
+ ulun->pending_requests = ulun->active_requests = 0;
+
+ /* Create and start lun threads */
+ sprintf(thread_name, "uasp-lun-thread%d", i);
+ DBG(udev->ucommon->common,
+ "creating & starting lun thread: thread_name = %s\n",
+ thread_name);
+
+ ulun->lun_thread_task = kthread_create(uasp_lun_thread,
+ (void *)ulun,
+ thread_name);
+ if (IS_ERR(ulun->lun_thread_task)) {
+ rc = PTR_ERR(ulun->lun_thread_task);
+ goto err_lun_init;
+ }
+ init_completion(&ulun->thread_notifier);
+ wake_up_process(ulun->lun_thread_task);
+ }
+ INFO(udev->ucommon->common, "All lun threads are started\n");
+ return rc;
+
+err_lun_init:
+ for (j = 0, ulun = udev->ucommon->uluns ; j < i; j++, ulun++)
+ ulun->lun_state = LUN_STATE_EXIT;
+ return rc;
+}
+
+/*
+ * uasp_bind() - bind function
+ * @c: pointer to the usb configuration
+ * @f: pointer to the usb function
+ *
+ * Return 0 on succeed, error code on failure
+ *
+ * TODO: Add fall back to usb_ep_autoconfig() if usb_ep_autoconfig_ss() fails.
+ * In that case mark somehow that we can only operate in HS mode
+ */
+static int __init uasp_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct uasp_dev *uaspd = uaspd_from_func(f);
+ struct fsg_dev *fsgd = &(uaspd->fsg_dev);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int rc;
+ int i;
+ struct usb_ep *ep;
+
+ fsgd->common->gadget = gadget;
+
+ /* Allocate new interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ uasp_intf_desc.bInterfaceNumber = i;
+ fsgd->interface_number = i;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_in_desc,
+ &uasp_bulk_in_ep_comp_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = uaspd; /* claim the endpoint */
+ fsgd->bulk_in = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_out_desc,
+ &uasp_bulk_out_ep_comp_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = uaspd; /* claim the endpoint */
+ fsgd->bulk_out = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_in_desc,
+ &uasp_status_in_ep_comp_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = uaspd; /* claim the endpoint */
+ uaspd->status = ep;
+
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_command_out_desc,
+ &uasp_command_out_ep_comp_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = uaspd; /* claim the endpoint */
+ uaspd->command = ep;
+
+ /* Assume endpoint addresses are the same for both speeds */
+ uasp_bulk_in_desc.bEndpointAddress =
+ uasp_ss_bulk_in_desc.bEndpointAddress;
+ uasp_bulk_out_desc.bEndpointAddress =
+ uasp_ss_bulk_out_desc.bEndpointAddress;
+ uasp_status_in_desc.bEndpointAddress =
+ uasp_ss_status_in_desc.bEndpointAddress;
+ uasp_command_out_desc.bEndpointAddress =
+ uasp_ss_command_out_desc.bEndpointAddress;
+ f->ss_descriptors = uasp_ss_function_desc;
+
+ return 0;
+
+autoconf_fail:
+ ERROR(fsgd->common, "unable to autoconfigure all endpoints\n");
+ rc = -ENOTSUPP;
+ return rc;
+}
+
+static void uasp_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct uasp_dev *uaspd = uaspd_from_func(f);
+
+ DBG(uaspd->fsg_dev.common, "unbind\n");
+ if (uaspd->fsg_dev.common->fsg == &(uaspd->fsg_dev)) {
+ uaspd->fsg_dev.common->new_fsg = NULL;
+ raise_exception(uaspd->fsg_dev.common, FSG_STATE_CONFIG_CHANGE);
+ /* TODO: make interruptible or killable somehow? */
+ wait_event(uaspd->fsg_dev.common->fsg_wait,
+ !uaspd->ucommon->common->fsg);
+ }
+ uasp_common_put(uaspd->ucommon);
+ kfree(uaspd->cmd_buff.buf);
+ kfree(uaspd);
+}
+
+static int uasp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = fsg;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ return 0;
+}
+
+static void uasp_disable(struct usb_function *f)
+{
+ struct uasp_dev *udev = uaspd_from_func(f);
+
+ udev->fsg_dev.common->new_fsg = NULL;
+ raise_exception(udev->fsg_dev.common, FSG_STATE_CONFIG_CHANGE);
+}
+
+/**
+ * uasp_add() - Add the UASP function to the given configuration
+ * @cdev: pointer to the composite device
+ * @c: usb configuration to add the function to
+ * @common: pointer to the fsg_common data structure
+ * @ucommon: pointer to uasp common data structure
+ *
+ * Returns 0 on sucsess error code otherwise
+ *
+ * Initiate the uasp_function and adds it to the given configuration by calling
+ * usb_add_function()
+ */
+static int uasp_add(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common,
+ struct uasp_common *ucommon)
+{
+ struct uasp_dev *uaspd;
+ int rc;
+
+ uaspd = kzalloc(sizeof *uaspd, GFP_KERNEL);
+ if (unlikely(!uaspd))
+ return -ENOMEM;
+
+ uaspd->fsg_dev.function.name = UASP_DRIVER_DESC;
+ uaspd->fsg_dev.function.strings = fsg_strings_array;
+ uaspd->fsg_dev.function.descriptors = uasp_hs_function_desc;
+ uaspd->fsg_dev.function.hs_descriptors = uasp_hs_function_desc;
+ uaspd->fsg_dev.function.bind = uasp_bind;
+ uaspd->fsg_dev.function.unbind = uasp_unbind;
+ uaspd->fsg_dev.function.set_alt = uasp_set_alt;
+ uaspd->fsg_dev.function.disable = uasp_disable;
+
+ uaspd->fsg_dev.common = common;
+
+ uaspd->ucommon = ucommon;
+
+ /* Init the command and status buffers */
+ uaspd->cmd_buff.buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+ if (unlikely(!uaspd->cmd_buff.buf)) {
+ rc = -ENOMEM;
+ goto uasp_add_err;
+ }
+
+ ucommon->udev = uaspd;
+ rc = finish_lun_init(uaspd);
+ if (rc)
+ goto uasp_add_err;
+
+ INIT_LIST_HEAD(&uaspd->cmd_queue);
+ INIT_LIST_HEAD(&uaspd->tm_func_queue);
+ /*
+ * Our caller holds a reference to common structure so we don't have
+ * to be worry about it being freed until we return from this function.
+ * So instead of incrementing counter now and decrement in error
+ * recovery we increment it only when call to usb_add_function() was
+ * successful.
+ */
+ rc = usb_add_function(c, &uaspd->fsg_dev.function);
+
+ if (likely(rc == 0))
+ kref_get(&ucommon->ref);
+ else
+ goto uasp_add_err;
+
+ return rc;
+uasp_add_err:
+ kfree(ucommon);
+ kfree(uaspd->cmd_buff.buf);
+ kfree(uaspd);
+ return rc;
+}
+
+/**
+ * fill_usb_request() - fills the usb_request structure with the given values.
+ * @req: pointer to usb_request structure to be filled.
+ * @buf: the buffer to send/receive
+ * @length: length field of the request.
+ * @zero: zero field of the request.
+ * @context: context field of the request.
+ * @short_not_ok: short_not_ok field of the request.
+ * @stream_id: stream_id field of the request.
+ * @complete: complete function to be called on request completion
+ *
+ */
+void fill_usb_request(struct usb_request *req,
+ void *buf,
+ unsigned length,
+ unsigned zero,
+ void *context,
+ unsigned short_not_ok,
+ unsigned stream_id,
+ usb_request_complete_t complete)
+{
+ req->buf = buf;
+ req->length = length;
+ req->zero = zero;
+ req->context = context;
+ req->short_not_ok = short_not_ok;
+ req->stream_id = stream_id;
+ req->complete = complete;
+}
+
diff --git a/drivers/usb/gadget/f_uasp.h b/drivers/usb/gadget/f_uasp.h
new file mode 100644
index 0000000..f283589
--- /dev/null
+++ b/drivers/usb/gadget/f_uasp.h
@@ -0,0 +1,430 @@
+/*
+ * f_uasp.h -- Mass Storage USB UASP Composite Function header
+ *
+ * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _F_UASP_H
+#define _F_UASP_H
+
+#include <linux/kernel.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+
+#define UASP_DRIVER_DESC "Mass Storage UASP Function"
+#define UASP_DRIVER_VERSION "2010/07/1"
+
+/* Pipe usage descriptor: refer to UAS spec 5.3.3.5 */
+#define USB_DT_PIPE_USAGE 0x24
+
+typedef void (*usb_request_complete_t)(struct usb_ep *ep,
+ struct usb_request *req);
+
+/* IU identifier summary - see table 10 of the UAS Spec */
+enum iu_id {
+ IU_ID_COMMAND = 0x01,
+ IU_ID_SENSE = 0x03,
+ IU_ID_RESPONSE = 0x04,
+ IU_ID_TASK_MANAGEMENT = 0x05,
+ IU_ID_READ_READY = 0x06,
+ IU_ID_WRITE_READY = 0x07,
+};
+
+/* TASK ATTRIBUTE field - see table 13 of the UAS Spec */
+enum task_attribute_data {
+ TASK_ATTR_SIMPLE = 0,
+ TASK_ATTR_HEAD_OF_QUEUE = 1,
+ TASK_ATTR_ORDERED = 2,
+ TASK_ATTR_ACA = 4,
+};
+
+/* USB_DT_PIPE_USAGE: Pipe usage descriptor */
+struct usb_pipe_usage_descriptor {
+ __u8 bLength;
+ __u8 bDescriptorType;
+
+ __u8 bPipeID;
+/* Pipe ID defenitions: Table 9 from UAS spec*/
+#define PIPE_ID_CMD 0x01 /* Command pipe */
+#define PIPE_ID_STS 0x02 /* Status pipe */
+#define PIPE_ID_DATA_IN 0x03 /* Data-in piep */
+#define PIPE_ID_DATA_OUT 0x04 /* Data-out pipe */
+ __u8 Reserved;
+} __attribute__((__packed__));
+
+/**
+ * struct uasp_buff - UASP buffer definition.
+ * @fsg_buff: pointer to the fsg_buf that this structure extends
+ * @ep: the ep the buff was allocated for
+ * @stream_id: Same as in struct usb_req
+ *
+ * Extends the struct fsg_buffhd. Each (used) buffer will be assigned to a
+ * specific stream. The stream is the same as the stream_id in the usb_request
+ * the buffer is assigned for.
+ * Note: stream_id has a meaning only when ep != null
+ */
+struct uasp_buff {
+ struct fsg_buffhd *fsg_buff;
+ struct usb_ep *ep;
+ unsigned stream_id:16;
+};
+
+/**
+ * struct uasp_common - Common data shared by all UASP devices
+ * @udev: Programming view of uasp device.
+ * @common: points to fsg_common in fsg_dev
+ * @ubufs: buffers to be used by the uasp device. Each element in
+ * ubufs[i].fsg_buff points to a fsg_buffhd struct from fsg_common data
+ * structure
+ * @uluns: luns of the uasp device. Each element in uluns[i].lun points to a
+ * fsg_lun array element from fsg_common data structure
+ *
+ * Extends the struct fsg_common structure.
+ */
+struct uasp_common {
+ struct uasp_dev *udev;
+ struct fsg_common *common;
+ struct uasp_buff ubufs[fsg_num_buffers];
+ struct uasp_lun *uluns;
+ struct kref ref;
+};
+
+/**
+ * struct uasp_dev - Programming view of the uasp device
+ * @fsg_dev: pointer to the fsg_dev this struct extends
+ * @ucommon: pointer to the common data of the device
+ * @status: status endpoint
+ * @command: command endpoint
+ * @cmd_buff: buffer used for receiving commannd IUs
+ * @op_mode: operation mode (HS_UASP_MODE/SS_UASP_MODE)
+ * @cmd_enabled: TRUE if command endpoint is enabled
+ * @status_enabled: TRUE if status endpoint is enabled
+ * @cmd_queue: General Command IUs queue
+ * @tm_func_queue: General Task Management IUs queue
+ * @active_requests: counter for currently handled (active) general requests
+ * @pending_requests: counter for pending general requests
+ *
+ * Extends the struct fsg_dev structure.
+ */
+struct uasp_dev {
+ struct fsg_dev fsg_dev;
+
+ struct uasp_common *ucommon;
+ struct usb_ep *status;
+ struct usb_ep *command;
+ struct fsg_buffhd cmd_buff;
+
+ unsigned int cmd_enabled;
+ unsigned int status_enabled;
+
+ struct list_head cmd_queue;
+ struct list_head tm_func_queue;
+ int active_requests;
+ int pending_requests;
+};
+
+/* LUN state */
+enum lun_state {
+ LUN_STATE_IDLE = 0,
+ LUN_STATE_PROCESSING = 1,
+ LUN_STATE_RESET = 2,
+ LUN_STATE_OVERLAPPED_TAG = 3,
+ LUN_STATE_EXIT = 4,
+ LUN_STATE_TERMINATED = 5,
+};
+
+/**
+ * struct uasp_lun - Describes the uasp LUN
+ * @lun: pointer to the fsg_lun this struct extends
+ * @lun_id: id of this LUN
+ * @cmd_queue: Command IUs queue
+ * @tm_func_queue: TaskManagement IUs queue
+ * @lun_state: one of the values from enum lun_state
+ * @dev: Programming view of uasp device.
+ * @lock: lock protects for protecting: state, all the req_busy's
+ * @thread_wakeup_needed: TRUE if the LUN thread needs wakening
+ * @lun_thread_task: thread of this LUN. Performs all LUN tasks
+ * @thread_notifier: used for lun_thread_task
+ * @pending_requests: counter for pending requests for this LUN
+ * @active_requests: counter for currently handled (active) requests for
+ * this LUN
+ *
+ * Extends the struct fsg_lun structure.
+ */
+struct uasp_lun {
+ struct fsg_lun *lun;
+ __u8 lun_id[8];
+ struct list_head cmd_queue;
+ struct list_head tm_func_queue;
+ enum lun_state lun_state;
+ struct uasp_dev *dev;
+
+ spinlock_t lock;
+
+ int thread_wakeup_needed;
+ struct task_struct *lun_thread_task;
+ struct completion thread_notifier;
+
+ int pending_requests;
+ int active_requests;
+};
+
+
+/* COMMAND IU related defenitions*/
+/* COMMAND IU state */
+enum command_state {
+ COMMAND_STATE_IDLE = 0,
+ COMMAND_STATE_RR_WR = 1,
+ COMMAND_STATE_DATA = 2,
+ COMMAND_STATE_STATUS = 3,
+ COMMAND_STATE_ABORTED = 4,
+ COMMAND_STATE_COMPLETED = 5,
+ COMMAND_STATE_FAILED = 6,
+};
+
+/**
+ * struct cmd_iu - COMMAND IU - Section 6.2.2 from UAS Spec
+ * @iu_id: should be set to 01h
+ * @reserved: should be set to 0
+ * @tag: see section 4.2 of the UAS spec
+ * @forth_byte: the forth byte of the COMMAND IU. Holds cmd priority and
+ * task attribute
+ * @reserved5: should be set to 0
+ * @length: the length of the CDB. Represented by bits 2-7.
+ * Bits0-1 are reserved
+ * @reserved7: should be set to 0
+ * @lun: LUN ID for this command
+ * @cdb: the SCSI CDB
+ * @add_cdb: Additional byted of the CDB
+ * @bh: buffer used for handling this command
+ * @state: command state. See enum command_state
+ * @ep: Endpoint on which the processing of COMMAND IU currently performs
+ * @req_sts: Status of the struct usb_request item submitted for certain
+ * COMMAND IU
+ * @file_offset: For READ, WRITE, VERIFY SCSI COMMANDs the current file offset
+ * @xfer_len: For READ, WRITE, VERIFY SCSI COMMANDs the remaining transfer
+ * length
+ * @node: Link for adding to the queue
+ */
+struct cmd_iu {
+ __u8 iu_id;
+ __u8 reserved;
+ __be16 tag;
+
+ struct {
+ unsigned reserved:1;
+ unsigned command_priority:4;
+ unsigned task_attribute:3;
+ } __attribute__((__packed__)) forth_byte;
+
+ __u8 reserved5;
+ __u8 length;
+ __u8 reserved7;
+ __u8 lun[8];
+ __u8 cdb[16];
+ __u8 *add_cdb;
+
+ struct fsg_buffhd *bh;
+ int state;
+ struct usb_ep *ep;
+
+#define CMD_REQ_NOT_SUBMITTED 0
+#define CMD_REQ_IN_PROGRESS 1
+#define CMD_REQ_COMPLETED 2
+ __u8 req_sts;
+ u32 file_offset;
+ u32 xfer_len;
+ struct list_head node;
+};
+
+
+/* STATUS values of SENSE IU as defined in SAM-4 */
+enum status_code_data {
+ STATUS_GOOD = 0x00,
+ STATUS_CHECK_CONDITION = 0x02,
+ STATUS_CONDITION_MET = 0x04,
+ STATUS_BUSY = 0x08,
+ STATUS_RESERVATION_CONFLICT = 0x18,
+ STATUS_TASK_SET_FULL = 0x28,
+ STATUS_ACA_ACTIVE = 0x30,
+ STATUS_TASK_ABORTED = 0x40,
+};
+
+/* SENSE IU - section 6.2.5 of the UAS spec */
+struct sense_iu {
+ __u8 iu_id;
+ __u8 reserved1;
+ __be16 tag;
+ __be16 status_qual;
+ __u8 status;
+ __u8 rsvd8[6];
+ __be16 len;
+ __u8 sense_data[SCSI_SENSE_BUFFERSIZE];
+};
+#define UASP_SIZEOF_SENSE_IU (16 + SCSI_SENSE_BUFFERSIZE)
+
+/* TASK MANAGEMENT IU related defenitions */
+/* TM FUNCTION types - see table 20 of the UAS Spec */
+enum tm_function_data {
+ TM_FUNCTION_ABORT_TASK = 0x01,
+ TM_FUNCTION_ABORT_TASK_SET = 0x02,
+ TM_FUNCTION_CLEAR_TASK_SET = 0x04,
+ TM_FUNCTION_RESET_LUN = 0x08,
+ TM_FUNCTION_IT_NEXUS_RESET = 0x10,
+ TM_FUNCTION_CLEAR_ACA = 0x40,
+ TM_FUNCTION_QUERY_TASK = 0x80,
+ TM_FUNCTION_QUERY_TASK_SET = 0x81,
+ TM_FUNCTION_QUERY_ASYNC_EVENT = 0x82,
+};
+
+/**
+ * struct tm_iu - TM FUNCTION IU - see table 19 of the UAS Spec
+ * @iu_id: Should be set to 05h
+ * @reserved: should be set to 0
+ * @tag: section 4.2 of the UAS spec
+ * @tm_function: valid values defined in struct tm_function_data
+ * @reserved5: should be set to 0
+ * @task_tag: Reserved for all tm_functions but ABORT_TASK and QUERY_TASK
+ * @lun: LUN ID for this command
+ * @bh: buffer used for handling this command
+ * @ep: Endpoint on which the processing of TM FUNCTION IU currently performs
+ * @state: State of the TM FUNCTION IU
+ * @node: Link for adding to the queue
+ */
+struct tm_iu {
+ __u8 iu_id;
+ __u8 reserved1;
+ __be16 tag;
+ __u8 tm_function;
+ __u8 reserved5;
+ __be16 task_tag;
+ __u8 lun[8];
+
+ struct fsg_buffhd *bh;
+ struct usb_ep *ep;
+ int state;
+ struct list_head node;
+};
+
+/* Response code values of RESPONSE IU - see table 18 of the UAS Spec */
+enum response_code_data {
+ RESPONSE_TM_FUNCTION_COMPLETE = 0x00,
+ RESPONSE_INVALID_IU = 0x02,
+ RESPONSE_TM_FUNCTION_NOT_SUPPORTED = 0x04,
+ RESPONSE_TM_FUNCTION_FAILED = 0x05,
+ RESPONSE_TM_FUNCTION_SUCCEEDED = 0x08,
+ RESPONSE_INCORRECT_LUN = 0x09,
+ RESPONSE_OVERLAPPED_TAG_ATTEMPTED = 0x0A,
+};
+
+/* RESPONSE IU - see table 17 of the UAS Spec */
+struct response_iu {
+ __u8 iu_id;
+ __u8 reserved;
+ __be16 tag;
+ __u8 resp_info[3];
+ __u8 status;
+} __attribute__((__packed__));
+#define UASP_SIZEOF_RESPONSE_IU 8
+
+void fill_usb_request(struct usb_request *req,
+ void *buf,
+ unsigned length,
+ unsigned zero,
+ void *context,
+ unsigned short_not_ok,
+ unsigned stream_id,
+ usb_request_complete_t complete);
+
+/**
+ * uasp_bulk_in_complete() - Callback function for the bulk IN endpoint
+ * @ep: pointer to the usb_ep (bulk IN endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the bulk IN endpoint.
+ * The requests cmdiu state is updated according to the completion status of
+ * the usb request. If the cmdiu was LUN specific, the corresponding LUN
+ * thread is awaken. If it was general, uasp main thread is awaken. */
+void uasp_bulk_in_complete(struct usb_ep *ep, struct usb_request *req);
+
+/**
+ * uasp_bulk_out_complete() - Callback function for the bulk OUT
+ * endpoint
+ * @ep: pointer to the usb_ep (bulk OUT endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the bulk
+ * OUT endpoint. The requests cmdiu state is updated according
+ * to the completion status of the usb request. If the cmdiu was
+ * LUN specific, the corresponding LUN thread is awaken. If it
+ * was general, uasp main thread is awaken.
+ */
+void uasp_bulk_out_complete(struct usb_ep *ep, struct usb_request *req);
+
+/**
+ * status_complete() - Callback function for the status endpoint
+ * @ep: pointer to the usb_ep (status endpoint)
+ * @req: usb_request received on this endpoint
+ *
+ * This function is passed to the outreq->complete() of the status endpoint.
+ * If the request completion status isn't ECONNRESET the requests tmiu/cmdiu
+ * state is updated to aborted/completed/failed (according to the completion
+ * status of the usb request). If the tmiu/cmdiu was LUN specific, the
+ * corresponding LUN thread is awaken. If it was general, uasp main thread is
+ * awaken.
+ */
+void status_complete(struct usb_ep *ep, struct usb_request *req);
+
+/**
+ * abort_commands() - Aborts all IUs on given queues
+ * @udev: Programming view of uasp device
+ * @cmd_queue: pointer to the cmd IUs queue to abort IUs from
+ * @tm_func_queue: pointer to the tm IUs queue to abort IUs from
+ * @lock: pointer to spinlock_t to lock when performing the abort.
+ * Can be udev->lock if the cmd_queue and the tm_func_queue are general,
+ * or curlun->lock if they belong to a specific LUN
+ *
+ * TODO: Add wait mechanism using curlun->active_requests or
+ * udev->active_requests
+ */
+void abort_commands(struct uasp_dev *udev,
+ struct list_head *cmd_queue,
+ struct list_head *tm_func_queue,
+ spinlock_t *lock);
+
+/**
+ * run_lun_threads() - Wakeup all LUN threads with a given state
+ * @udev: Programming view of uasp device
+ * @state: The state to run the LUn in (from enum lun_state)
+ *
+ */
+void run_lun_threads(struct uasp_dev *udev, int state);
+
+/**
+ * all_lun_state_non_processing() - Returns 1, if all luns are in
+ * none-processing state
+ * @udev: Programming view of uasp device
+ *
+ */
+int all_lun_state_non_processing(struct uasp_dev *udev);
+
+/**
+ * close_lun() - Close the backing file of the given LUN
+ * @ulun: pointer to the LUn to close
+ *
+ * This function should be called when fsg_common->filesem is
+ * taken!
+ */
+void close_lun(struct uasp_lun *ulun);
+
+#endif /* _F_UASP_H */
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index e24f72f..b7d9e7e 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -53,6 +53,7 @@
#include "config.c"
#include "epautoconf.c"
#include "f_mass_storage.c"
+#include "f_uasp.c"
/*-------------------------------------------------------------------------*/
@@ -66,7 +67,7 @@ static struct usb_device_descriptor msg_device_desc = {
/* Vendor and product id can be overridden by module parameters. */
.idVendor = cpu_to_le16(FSG_VENDOR_ID),
.idProduct = cpu_to_le16(FSG_PRODUCT_ID),
- .bNumConfigurations = 1,
+ .bNumConfigurations = 2,
};
static struct usb_otg_descriptor otg_descriptor = {
@@ -121,7 +122,8 @@ static int __init msg_do_config(struct usb_configuration *c)
fsg_config_from_params(&config, &mod_data);
config.ops = &ops;
- retp = fsg_common_init(&common, c->cdev, &config);
+ /* Init fsg_common and start the fsg main thread */
+ retp = fsg_common_init(&common, c->cdev, &config, 1);
if (IS_ERR(retp))
return PTR_ERR(retp);
@@ -136,19 +138,72 @@ static struct usb_configuration msg_config_driver = {
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
+static int __init uasp_do_config(struct usb_configuration *c)
+{
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
+
+ struct fsg_common *fcommon;
+ struct uasp_common *ucommon;
+ struct fsg_config config;
+ int ret = 0;
+
+ fsg_config_from_params(&config, &mod_data);
+ config.ops = &ops;
+ fcommon = fsg_common_init(0, c->cdev, &config, 0);
+ if (IS_ERR(fcommon))
+ return PTR_ERR(fcommon);
+
+ ucommon = uasp_common_init(fcommon, c->cdev, &config);
+ if (IS_ERR(ucommon))
+ return PTR_ERR(ucommon);
+ ret = uasp_add(c->cdev, c, fcommon, ucommon);
+ uasp_common_put(ucommon);
+
+ return ret;
+}
+
+static struct usb_configuration uasp_config_driver = {
+ .label = "Linux UASP File-Backed Storage",
+ .bConfigurationValue = 2,
+ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
+};
+
+
/****************************** Gadget Bind ******************************/
+bool use_uasp ;
+module_param(use_uasp, bool, S_IRUGO | S_IWUSR);
static int __init msg_bind(struct usb_composite_dev *cdev)
{
int status;
- status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
- if (status < 0)
- return status;
-
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
+
+ if (use_uasp) {
+ /*
+ * TODO: fix the bellow!
+ * Right now the host always chooses the first configuration.
+ * Untill this is fixed, if we want the device to opperate in
+ * UASP mode we switch the configurations numbers
+ */
+ msg_config_driver.bConfigurationValue = 2;
+ uasp_config_driver.bConfigurationValue = 1;
+ /* register uasp configuration */
+ status = usb_add_config(cdev, &uasp_config_driver,
+ uasp_do_config);
+ if (status < 0)
+ return status;
+ } else {
+ /* register our second configuration */
+ status = usb_add_config(cdev, &msg_config_driver,
+ msg_do_config);
+ if (status < 0)
+ return status;
+ }
set_bit(0, &msg_registered);
return 0;
}
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index c7f291a..4937ad6 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -217,6 +217,7 @@ struct interrupt_data {
#define SS_UNRECOVERED_READ_ERROR 0x031100
#define SS_WRITE_ERROR 0x030c02
#define SS_WRITE_PROTECTED 0x072700
+#define SS_OVERLAPPED_COMMANDS_ATTEMPTED 0x0b4e00
#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
#define ASC(x) ((u8) ((x) >> 8))
@@ -261,9 +262,16 @@ static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
#define EP0_BUFSIZE 256
#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+/*
+ * We limit the number of UASP streams to 256 due to memory requirements.
+ * 4 buffer will be allocated for each supported stream.
+ */
+#define UASP_SS_EP_COMP_NUM_STREAMS 4
+
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+static unsigned int fsg_num_buffers = (4*
+ CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS^UASP_SS_EP_COMP_NUM_STREAMS));
module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
@@ -273,17 +281,18 @@ MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
* Number of buffers we will use.
* 2 is usually enough for good buffering pipeline
*/
-#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+#define fsg_num_buffers (4*\
+(CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS^UASP_SS_EP_COMP_NUM_STREAMS))
#endif /* CONFIG_USB_DEBUG */
/* check if fsg_num_buffers is within a valid range */
static inline int fsg_num_buffers_validate(void)
{
- if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ if (fsg_num_buffers >= 64 && fsg_num_buffers <= 1024)
return 0;
pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
- fsg_num_buffers, 2 ,4);
+ fsg_num_buffers, 64 , 1024);
return -EINVAL;
}
@@ -681,7 +690,7 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
ro = curlun->initially_ro;
if (!ro) {
filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
- if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
+ if (-EROFS == PTR_ERR(filp))
ro = 1;
}
if (ro)
@@ -696,7 +705,10 @@ static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
if (filp->f_path.dentry)
inode = filp->f_path.dentry->d_inode;
- if (!inode || (!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ if (inode && S_ISBLK(inode->i_mode)) {
+ if (bdev_read_only(inode->i_bdev))
+ ro = 1;
+ } else if (!inode || !S_ISREG(inode->i_mode)) {
LINFO(curlun, "invalid file type: %s\n", filename);
goto out;
}
diff --git a/drivers/usb/gadget/uasp_cmdiu.c b/drivers/usb/gadget/uasp_cmdiu.c
new file mode 100644
index 0000000..9fc882d
--- /dev/null
+++ b/drivers/usb/gadget/uasp_cmdiu.c
@@ -0,0 +1,514 @@
+/*
+ * uasp_cmdiu.c -- Mass Storage UAS Protocol - COMMAND IUs handling
+ * implementation
+ *
+ * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include "f_uasp.h"
+
+/**
+ * get_buffhd() - returns a buffer fot IU processing
+ * @bh: Array of the buffers in which the search should be done.
+ *
+ * Return pointer to the found buffer if it exists, 0 otherwise.
+ *
+ * This function tries to find a free buffer for COMMAND IU or
+ * TM FUNCTION IU processing.
+ */
+struct fsg_buffhd *get_buffhd(struct fsg_buffhd *bh)
+{
+ int i;
+
+ for (i = 0; i < fsg_num_buffers; i++) {
+ if (bh[i].state == BUF_STATE_EMPTY) {
+ bh[i].state = BUF_STATE_BUSY;
+ return &bh[i];
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * check_cmdiu() - initial verification of the COMMAND IU
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct lun if the COMMAND IU to be checked is addressed
+ * to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be checked.
+ * @needs_medium: Specifies, is the medium needed for the COMMAND IU processing.
+ */
+static __u32 check_cmdiu(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu,
+ __u8 needs_medium)
+{
+ __u32 ua_data = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (!curlun || !curlun->lun) {
+ if (cmdiu->cdb[0] != INQUIRY &&
+ cmdiu->cdb[0] != REQUEST_SENSE) {
+ DBG(udev->ucommon->common,
+ "%s() - Logical unit is not supported\n",
+ __func__);
+ return SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ }
+ } else {
+ if (curlun->lun->unit_attention_data &&
+ cmdiu->cdb[0] != INQUIRY &&
+ cmdiu->cdb[0] != REQUEST_SENSE) {
+ DBG(udev->ucommon->common,
+ "%s() - There is an unit attention condition\n",
+ __func__);
+ ua_data = curlun->lun->unit_attention_data;
+ curlun->lun->unit_attention_data = SS_NO_SENSE;
+ return ua_data;
+ }
+ }
+
+ if (curlun && !(curlun->lun->filp) && needs_medium) {
+ DBG(udev->ucommon->common,
+ "%s() - Medium is not present\n", __func__);
+ return SS_MEDIUM_NOT_PRESENT;
+ }
+
+ return SS_NO_SENSE;
+}
+
+/**
+ * fill_sense_iu() - fills the struct sense_iu with a given values.
+ * @udev: Programming view of UASP device.
+ * @siu: Pointer to structure to be filled.
+ * @tag: tag field of the structure.
+ * @status: status field of the structure.
+ * @sense_data: sense_data field of the structure.
+ */
+void fill_sense_iu(struct uasp_dev *udev,
+ struct sense_iu *siu,
+ __be16 tag,
+ __u8 status,
+ __u32 sense_data)
+{
+ DBG(udev->ucommon->common, "%s() - Status = %02x\n", __func__, status);
+
+ siu->iu_id = IU_ID_SENSE;
+ siu->reserved1 = 0;
+ siu->tag = tag;
+ siu->status_qual = 0; /* TODO: fix this!!! */
+ siu->status = status;
+ memset(siu->rsvd8, 0, 6);
+ siu->len = cpu_to_be16(5);
+ siu->sense_data[0] = SK(sense_data);
+ siu->sense_data[1] = ASC(sense_data);
+ siu->sense_data[2] = ASCQ(sense_data);
+}
+
+/**
+ * do_uasp_inquiry() - performs INQUIRY SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
+ * performed is addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_inquiry(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+
+/**
+ * do_uasp_request_sense() - performs REQUEST SENSE SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
+ * performed is addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_request_sense(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_test_unit_ready() - performs TEST UNIT READY SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
+ * performed is addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_test_unit_ready(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_mode_sense() - performs MODE SENSE(6) and MODE SENSE(10)
+ * SCSI commands.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
+ * performed is addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_mode_sense(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_prevent_allow() - performs PREVENT ALLOW MEDIUM REMOVAL SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
+ * performed is addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_prevent_allow(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_read() - performs READ(6), READ(10), READ(12) SCSI commands.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns non zero if usb request(s) should be submitted to PCD after cmdiu
+ * processing, 0 otherwise.
+ */
+static int do_uasp_read(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_read_capacity() - This function performs READ CAPACITY SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ *
+ */
+static int do_uasp_read_capacity(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_read_format_capacities() - performs READ FORMAT CAPACITIES
+ * SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ */
+static int do_uasp_read_format_capacities(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_start_stop() - performs START STOP UNIT SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ *
+ */
+static int do_uasp_start_stop(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_verify() - This function performs VERIFY SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after
+ * cmdiu processing, 0 otherwise.
+ *
+ * Although optional, this command is used by MS-Windows. We support a minimal
+ * version: BytChk must be 0.
+ *
+ */
+static int do_uasp_verify(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_write() - This function performs WRITE(6), WRITE(10), WRITE(12)
+ * SCSI commands.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns: 1 if an IN usb request should be submitted to PCD after processing
+ * 2 if an OUT usb request should be submitted to PCD after processing
+ * 0 otherwise.
+ */
+static int do_uasp_write(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * do_uasp_synchronize_cache() - performs SYNCHRONIZE CACHE SCSI command.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ *
+ * Returns 1 if usb request should be submitted to PCD after cmdiu processing,
+ * 0 otherwise.
+ *
+ */
+static int do_uasp_synchronize_cache(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ return 0;
+}
+
+/**
+ * process_cmdiu() - This function performs a given COMMAND IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @cmdiu: COMMAND IU to be performed.
+ */
+static void process_cmdiu(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct cmd_iu *cmdiu)
+{
+ unsigned long flags;
+ struct sense_iu *siu;
+ struct usb_request *req;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() Enter. (cmdiu->cdb[0]=%04x)\n",
+ __func__, cmdiu->cdb[0]);
+
+ /* We're using the backing file */
+ down_read(&udev->ucommon->common->filesem);
+ switch (cmdiu->cdb[0]) {
+ case INQUIRY:
+ rc = do_uasp_inquiry(udev, curlun, cmdiu);
+ break;
+ case MODE_SENSE:
+ case MODE_SENSE_10:
+ rc = do_uasp_mode_sense(udev, curlun, cmdiu);
+ break;
+ case ALLOW_MEDIUM_REMOVAL:
+ rc = do_uasp_prevent_allow(udev, curlun, cmdiu);
+ break;
+ case READ_6:
+ case READ_10:
+ case READ_12:
+ rc = do_uasp_read(udev, curlun, cmdiu);
+ break;
+ case READ_CAPACITY:
+ rc = do_uasp_read_capacity(udev, curlun, cmdiu);
+ break;
+ case READ_FORMAT_CAPACITIES:
+ rc = do_uasp_read_format_capacities(udev, curlun, cmdiu);
+ break;
+ case REQUEST_SENSE:
+ rc = do_uasp_request_sense(udev, curlun, cmdiu);
+ break;
+ case START_STOP:
+ rc = do_uasp_start_stop(udev, curlun, cmdiu);
+ break;
+ case SYNCHRONIZE_CACHE:
+ rc = do_uasp_synchronize_cache(udev, curlun, cmdiu);
+ break;
+ case TEST_UNIT_READY:
+ rc = do_uasp_test_unit_ready(udev, curlun, cmdiu);
+ break;
+ case VERIFY:
+ rc = do_uasp_verify(udev, curlun, cmdiu);
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ case WRITE_12:
+ rc = do_uasp_write(udev, curlun, cmdiu);
+ break;
+ case FORMAT_UNIT:
+ case MODE_SELECT:
+ case MODE_SELECT_10:
+ case RELEASE:
+ case RESERVE:
+ case SEND_DIAGNOSTIC:
+ default:
+ ERROR(udev->ucommon->common,
+ "%s(): Unsupported command = %x\n",
+ __func__, cmdiu->cdb[0]);
+ cmdiu->state = COMMAND_STATE_STATUS;
+ siu = (struct sense_iu *)cmdiu->bh->inreq->buf;
+ fill_sense_iu(udev, siu, cmdiu->tag,
+ STATUS_CHECK_CONDITION,
+ SS_INVALID_COMMAND);
+ fill_usb_request(cmdiu->bh->inreq, (void *)siu,
+ UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ status_complete);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ }
+
+ up_read(&udev->ucommon->common->filesem);
+ if (rc) {
+ if (rc == 1) {
+ req = cmdiu->bh->inreq;
+ cmdiu->bh->inreq_busy = 1;
+ } else {
+ req = cmdiu->bh->outreq;
+ cmdiu->bh->outreq_busy = 1;
+ }
+ if (usb_ep_queue(cmdiu->ep, req, 0)) {
+ ERROR(udev->ucommon->common,
+ "%s()usb_ep_queue failed\n", __func__);
+ cmdiu->state = COMMAND_STATE_FAILED;
+ } else {
+ DBG(udev->ucommon->common,
+ "%s() - process_cmdiu: queued req to ep\n",
+ __func__);
+ if (curlun) {
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->active_requests++;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ } else {
+ spin_lock_irqsave(
+ &(udev->ucommon->common->lock), flags);
+ udev->active_requests++;
+ spin_unlock_irqrestore(
+ &(udev->ucommon->common->lock), flags);
+ }
+ }
+ }
+}
+
+/**
+ * do_cmdiu() - This function performs the COMMAND IUs from a given queue.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if COMMAND IUs from lun::cmd_queue
+ * should be performed, 0 if COMMAND IUs from uasp_dev::cmd_queue should
+ * be performed.
+ */
+void do_cmdiu(struct uasp_dev *udev, struct uasp_lun *curlun)
+{
+ struct list_head *link;
+ struct cmd_iu *cmdiu, *tmp;
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Select the cmd_queue from which cmdius should be processed */
+ if (curlun)
+ link = &curlun->cmd_queue;
+ else
+ link = &udev->cmd_queue;
+
+ list_for_each_entry_safe(cmdiu, tmp, link, node) {
+ DBG(udev->ucommon->common, "%s() - Rolling over cmdiu queue\n",
+ __func__);
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Try to get buffers for cmdiu processing */
+ cmdiu->bh = get_buffhd(udev->ucommon->common->buffhds);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+
+ if (!cmdiu->bh) {
+ ERROR(udev->ucommon->common,
+ "%s() -Didn't manage to get buffers for "
+ "cmdiu!\n", __func__);
+ continue;
+ }
+ } else if (cmdiu->state == COMMAND_STATE_DATA) {
+ if (cmdiu->req_sts == CMD_REQ_COMPLETED)
+ spin_unlock_irqrestore(
+ &(udev->ucommon->common->lock), flags);
+ else {
+ spin_unlock_irqrestore(
+ &(udev->ucommon->common->lock), flags);
+ continue;
+ }
+ } else {
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+ continue;
+ }
+
+ process_cmdiu(udev, curlun, cmdiu);
+
+ if (cmdiu->state == COMMAND_STATE_DATA)
+ break;
+ }
+}
+
+
diff --git a/drivers/usb/gadget/uasp_tmiu.c b/drivers/usb/gadget/uasp_tmiu.c
new file mode 100644
index 0000000..23f9351
--- /dev/null
+++ b/drivers/usb/gadget/uasp_tmiu.c
@@ -0,0 +1,277 @@
+/*
+ * uasp_tmiu.c -- Mass Storage UAS Protocol - TASK MANAGEMENT IUs handling
+ * implementation
+ *
+ * Copyright (C) 2003-2005 Alan Stern
+ * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/list.h>
+#include "f_uasp.h"
+
+/**
+ * fill_response_iu() - fills the struct response_iu with the given values.
+ * @udev: Programming view of file storage gadget.
+ * @riu: Pointer to structure to be filled.
+ * @tag: tag field of the structure.
+ * @resp_info: resp_info field of the structure.
+ * @status: status field of the structure.
+ */
+void fill_response_iu(struct uasp_dev *udev,
+ struct response_iu *riu,
+ __be16 tag,
+ uint32_t resp_info,
+ uint8_t status)
+{
+ DBG(udev->ucommon->common, "%s() - Enter. Status = %02x\n", __func__,
+ status);
+ riu->iu_id = IU_ID_RESPONSE;
+ riu->reserved = 0;
+ riu->tag = tag;
+ riu->resp_info[0] = SK(resp_info);
+ riu->resp_info[1] = ASC(resp_info);
+ riu->resp_info[2] = ASCQ(resp_info);
+ riu->status = status;
+}
+
+/**
+ * reset_lun() - performs RESET LUN TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu: TM FUNCTION IU to be processed.
+ *
+ * This function performs LUN reset. It aborts all of the given LUN pending
+ * commands.
+ */
+static void reset_lun(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * abort_task() - This function performs ABORT TASK TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu: TM FUNCTION IU to be processed.
+ *
+ * This function aborts the command with the same ip_tag as in the
+ * tmiu->task_tag. It's valid only for command that are handled by a specific
+ * LUN .
+ */
+static void abort_task(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * abort_task_set() - This function performs ABORT TASK SET TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu: TM FUNCTION IU to be processed.
+ *
+ * This function aborts all the commands pending for the specified LUN.
+ */
+static void abort_task_set(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * reset_nexus() - This function performs RESET NEXUS TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @tmiu: TM FUNCTION IU to be processed.
+ */
+static void reset_nexus(struct uasp_dev *udev,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * query_unit_attention() - performs QUERY UNIT ATTENTION TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu: TM FUNCTION IU to be processed.
+ *
+ * This function is used to obtain a unit attention condition or a deferred
+ * error pending, if such exists, for the LUN on which the task management
+ * function was received.
+ */
+static void query_unit_attention(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+
+/**
+ * This function performs QUERY TASK TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu TM FUNCTION IU to be processed.
+ */
+static void query_task(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * This function performs QUERY TASK SET TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the TM FUNCTION IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @tmiu TM FUNCTION IU to be processed.
+ */
+static void query_task_set(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+}
+
+/**
+ * process_tmiu() - process a given TM FUNCTION IU.
+ * @udev: Programming view of UASP device.
+ * @curlun: Pointer to struct uasp_lun if the COMMAND IU to be performed is
+ * addressed to a valid LUN, 0 otherwise.
+ * @miu: TM FUNCTION IU to be processed.
+ */
+static void process_tmiu(struct uasp_dev *udev,
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
+{
+ struct response_iu *riu;
+ unsigned long flags;
+
+ switch (tmiu->tm_function) {
+ case TM_FUNCTION_ABORT_TASK:
+ abort_task(udev, curlun, tmiu);
+ break;
+
+ case TM_FUNCTION_ABORT_TASK_SET:
+ case TM_FUNCTION_CLEAR_TASK_SET:
+ abort_task_set(udev, curlun, tmiu);
+ break;
+
+ case TM_FUNCTION_RESET_LUN:
+ reset_lun(udev, curlun, tmiu);
+ break;
+
+ case TM_FUNCTION_IT_NEXUS_RESET:
+ reset_nexus(udev, tmiu);
+ break;
+
+ case TM_FUNCTION_QUERY_TASK:
+ query_task(udev, curlun, tmiu);
+ break;
+
+ case TM_FUNCTION_QUERY_TASK_SET:
+ query_task_set(udev, curlun, tmiu);
+ break;
+
+ case TM_FUNCTION_QUERY_ASYNC_EVENT:
+ query_unit_attention(udev, curlun, tmiu);
+ break;
+
+ default:
+ ERROR(udev->ucommon->common, "%s(): Unsupported tmiu = %x\n",
+ __func__, tmiu->tm_function);
+ riu = (struct response_iu *)tmiu->bh->inreq->buf;
+ fill_response_iu(udev, riu, tmiu->tag, 0,
+ RESPONSE_TM_FUNCTION_NOT_SUPPORTED);
+
+ fill_usb_request(tmiu->bh->inreq, (void *)riu,
+ UASP_SIZEOF_RESPONSE_IU, 0,
+ (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete);
+ tmiu->ep = udev->status;
+ break;
+ }
+
+ tmiu->state = COMMAND_STATE_STATUS;
+ if (usb_ep_queue(tmiu->ep, tmiu->bh->inreq, 0)) {
+ ERROR(udev->ucommon->common,
+ "%s()usb_ep_queue failed\n", __func__);
+ tmiu->state = COMMAND_STATE_FAILED;
+ } else {
+ tmiu->bh->inreq_busy = 1;
+ if (curlun) {
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->active_requests++;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ } else {
+ spin_lock_irqsave(&(udev->ucommon->common->lock),
+ flags);
+ udev->active_requests++;
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ flags);
+ }
+ }
+}
+
+/**
+ * do_tmdiu() - processes the TM FUNCTION IUs from a given queue.
+ * @udev: Programming view of file storage gadget.
+ * @curlun: Pointer to struct uasp_lun if TM FUNCTION IUs from
+ * uasp_lun::tm_func_queue should be processed,
+ * 0 if TM FUNCTION IUs from uasp_dev::tm_func_queue should
+ * be processed.
+ */
+void do_tmiu(struct uasp_dev *udev, struct uasp_lun *curlun)
+{
+ struct list_head *link;
+ struct tm_iu *tmiu, *tmp;
+ unsigned long flags;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Select the tm_func_queue from which tmius should be processed */
+ if (curlun)
+ link = &curlun->tm_func_queue;
+ else
+ link = &udev->tm_func_queue;
+
+ DBG(udev->ucommon->common, "%s() - Rolling over tmiu queue\n",
+ __func__);
+ list_for_each_entry_safe(tmiu, tmp, link, node) {
+ if (tmiu->state != COMMAND_STATE_IDLE)
+ continue;
+
+ /* Try to get buffer for tmiu provessing */
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ tmiu->bh = get_buffhd(udev->ucommon->common->buffhds);
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ if (!tmiu->bh) {
+ ERROR(udev->ucommon->common,
+ "%s() -Didnt manage to get buffers for tmiu!\n",
+ __func__);
+ continue;
+ }
+
+ process_tmiu(udev, curlun, tmiu);
+ }
+}
--
1.7.6
--
Sent by a Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFC/PATCH v4 2/3] uas: MS UAS Gadget driver - Implementation
2011-12-04 20:12 [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Shimrit Malichi
@ 2011-12-04 20:12 ` Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 3/3] uas: Supporting UAS and BOT configuration Shimrit Malichi
2011-12-05 8:13 ` [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Felipe Balbi
2 siblings, 0 replies; 4+ messages in thread
From: Shimrit Malichi @ 2011-12-04 20:12 UTC (permalink / raw)
To: greg; +Cc: linux-usb, linux-arm-msm, balbi, ablay, Shimrit Malichi,
open list
Implementation of different SCSI commands received in a COMMAND IU packets,
as well as most of the TASK MANAGEMENT IUs defined in table 20 of the UAS
specifications, for UASP over a HS USB bus connection.
Signed-off-by: Shimrit Malichi <smalichi@codeaurora.org>
---
drivers/usb/gadget/f_uasp.c | 25 +-
drivers/usb/gadget/f_uasp.h | 28 +-
drivers/usb/gadget/uasp_cmdiu.c | 1408 ++++++++++++++++++++++++++++++++++++++-
drivers/usb/gadget/uasp_tmiu.c | 273 ++++++++-
4 files changed, 1700 insertions(+), 34 deletions(-)
diff --git a/drivers/usb/gadget/f_uasp.c b/drivers/usb/gadget/f_uasp.c
index af1569e..3db5a11 100644
--- a/drivers/usb/gadget/f_uasp.c
+++ b/drivers/usb/gadget/f_uasp.c
@@ -922,6 +922,8 @@ reset_uasp:
fcommon->fsg = new_fsg;
fsgd = fcommon->fsg;
+ uaspd->op_mode = (fcommon->gadget->speed == USB_SPEED_SUPER ?
+ SS_UASP_MODE : HS_UASP_MODE);
/* Enable the endpoints */
config_ep_by_speed(fcommon->gadget, &fsgd->function, fsgd->bulk_in);
@@ -990,9 +992,9 @@ reset_uasp:
goto reset_uasp;
}
- DBG(uaspd->ucommon->common, "%s() allocated command request = %p, "
- "udev=%p\n", __func__,
- uaspd->cmd_buff.outreq, uaspd);
+ DBG(uaspd->ucommon->common, "%s() Enebled endpoints. "
+ "Opperation mode = %d\n", __func__,
+ uaspd->op_mode);
uaspd->cmd_buff.outreq->buf = &(uaspd->cmd_buff.buf);
uaspd->cmd_buff.inreq = NULL;
uaspd->cmd_buff.state = BUF_STATE_EMPTY;
@@ -1196,6 +1198,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
list_for_each_entry(tmp_cmdiu, &curlun->cmd_queue, node) {
if (tmp_cmdiu->state != COMMAND_STATE_IDLE &&
tmp_cmdiu->state != COMMAND_STATE_DATA &&
+ tmp_cmdiu->state != COMMAND_STATE_RR_WR &&
tmp_cmdiu->state != COMMAND_STATE_STATUS) {
continue;
}
@@ -1222,6 +1225,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
list_for_each_entry(tmp_cmdiu, &udev->cmd_queue, node) {
if (tmp_cmdiu->state != COMMAND_STATE_IDLE &&
tmp_cmdiu->state != COMMAND_STATE_DATA &&
+ tmp_cmdiu->state != COMMAND_STATE_RR_WR &&
tmp_cmdiu->state != COMMAND_STATE_STATUS)
continue;
@@ -1300,7 +1304,8 @@ overlapped_tag:
fill_usb_request(tmiu->bh->inreq, tmiu->bh->buf,
UASP_SIZEOF_RESPONSE_IU, 0,
(void *)tmiu, 0,
- be16_to_cpup(&tmiu->tag), status_complete);
+ be16_to_cpup(&tmiu->tag), status_complete,
+ udev->op_mode);
tmiu->ep = udev->status;
tmiu->bh->inreq_busy = 1;
@@ -1329,7 +1334,8 @@ overlapped_tag:
fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
UASP_SIZEOF_SENSE_IU, 0,
(void *)cmdiu, 0,
- be16_to_cpup(&cmdiu->tag), status_complete);
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
cmdiu->ep = udev->status;
cmdiu->bh->inreq_busy = 1;
if (usb_ep_queue(cmdiu->ep, cmdiu->bh->inreq, 0))
@@ -1668,7 +1674,8 @@ void abort_commands(struct uasp_dev *udev,
spin_lock_irqsave(lock, flags);
list_for_each_entry_safe(cmdiu, tmp_cmdiu, cmd_queue, node) {
- if (cmdiu->state == COMMAND_STATE_DATA) {
+ if (cmdiu->state == COMMAND_STATE_DATA ||
+ cmdiu->state == COMMAND_STATE_RR_WR) {
if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
spin_unlock_irqrestore(lock, flags);
if (cmdiu->bh->inreq_busy)
@@ -2371,6 +2378,7 @@ uasp_add_err:
* @short_not_ok: short_not_ok field of the request.
* @stream_id: stream_id field of the request.
* @complete: complete function to be called on request completion
+ * @op_mode: operation mode (HS_UASP_MODE/SS_UASP_MODE)
*
*/
void fill_usb_request(struct usb_request *req,
@@ -2380,14 +2388,15 @@ void fill_usb_request(struct usb_request *req,
void *context,
unsigned short_not_ok,
unsigned stream_id,
- usb_request_complete_t complete)
+ usb_request_complete_t complete,
+ uint8_t op_mode)
{
req->buf = buf;
req->length = length;
req->zero = zero;
req->context = context;
req->short_not_ok = short_not_ok;
- req->stream_id = stream_id;
+ req->stream_id = (op_mode == SS_UASP_MODE ? stream_id : 0);
req->complete = complete;
}
diff --git a/drivers/usb/gadget/f_uasp.h b/drivers/usb/gadget/f_uasp.h
index f283589..63ade00 100644
--- a/drivers/usb/gadget/f_uasp.h
+++ b/drivers/usb/gadget/f_uasp.h
@@ -124,6 +124,10 @@ struct uasp_dev {
struct usb_ep *command;
struct fsg_buffhd cmd_buff;
+#define HS_UASP_MODE 0
+#define SS_UASP_MODE 1
+ uint8_t op_mode;
+
unsigned int cmd_enabled;
unsigned int status_enabled;
@@ -337,6 +341,27 @@ struct response_iu {
} __attribute__((__packed__));
#define UASP_SIZEOF_RESPONSE_IU 8
+/* READ/WRITE READY IU - see table 14/15 of the UAS Spec */
+struct rw_ready_iu {
+ __u8 iu_id;
+ __u8 reserved;
+ __be16 tag; /* section 4.2 of the UAS spec */
+} __attribute__((__packed__));
+#define UASP_SIZEOF_RW_READY_IU 4
+
+/**
+ * fill_usb_request() - fills the usb_request structure with the given values.
+ * @req: pointer to usb_request structure to be filled.
+ * @buf: the buffer to send/receive
+ * @length: length field of the request.
+ * @zero: zero field of the request.
+ * @context: context field of the request.
+ * @short_not_ok: short_not_ok field of the request.
+ * @stream_id: stream_id field of the request.
+ * @complete: complete function to be called on request completion
+ * @op_mode: operation mode (HS_UASP_MODE/SS_UASP_MODE)
+ *
+ */
void fill_usb_request(struct usb_request *req,
void *buf,
unsigned length,
@@ -344,7 +369,8 @@ void fill_usb_request(struct usb_request *req,
void *context,
unsigned short_not_ok,
unsigned stream_id,
- usb_request_complete_t complete);
+ usb_request_complete_t complete,
+ uint8_t op_mode);
/**
* uasp_bulk_in_complete() - Callback function for the bulk IN endpoint
diff --git a/drivers/usb/gadget/uasp_cmdiu.c b/drivers/usb/gadget/uasp_cmdiu.c
index 9fc882d..8836945 100644
--- a/drivers/usb/gadget/uasp_cmdiu.c
+++ b/drivers/usb/gadget/uasp_cmdiu.c
@@ -117,6 +117,22 @@ void fill_sense_iu(struct uasp_dev *udev,
}
/**
+ * fill_rw_ready_iu() - fills the struct rw_ready_iu with a given values.
+ * @rwr_iu: Pointer to structure to be filled.
+ * @iu_id: can be IU_ID_READ_READY or IU_ID_WRITE_READY only
+ * @tag: tag field of the structure.
+ *
+ * TODO: add verification of iu_id
+ */
+static void fill_rw_ready_iu(struct rw_ready_iu *rwr_iu,
+ __u8 iu_id,
+ __be16 tag)
+{
+ rwr_iu->iu_id = iu_id;
+ rwr_iu->tag = tag;
+}
+
+/**
* do_uasp_inquiry() - performs INQUIRY SCSI command.
* @udev: Programming view of UASP device.
* @curlun: Pointer to struct uasp_lun if the COMMAND IU to be
@@ -130,7 +146,109 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct fsg_common *common = udev->ucommon->common;
+ struct usb_request *req = bh->inreq;
+ __u8 *buf = (__u8 *)bh->buf;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ ERROR(common, "%s() - Error condition\n", __func__);
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ memset(buf, 0, FSG_BUFLEN);
+ if (!curlun) {
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ } else {
+ buf[0] = curlun->lun->cdrom ?
+ TYPE_ROM : TYPE_DISK;
+ buf[1] = curlun->lun->removable ? 0x80 : 0;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ buf[5] = 0; /* No special options */
+ buf[6] = 0;
+ buf[7] = 0;
+ memcpy(buf + 8, common->inquiry_string,
+ sizeof(common->inquiry_string));
+ }
+
+ fill_usb_request(req, bh->buf,
+ min(36,
+ (int)get_unaligned_be16(&cmdiu->cdb[3])),
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ cmdiu->tag, status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}
@@ -143,12 +261,148 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
*
* Returns 1 if usb request should be submitted to PCD after cmdiu processing,
* 0 otherwise.
+ *
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ * If a REQUEST SENSE command is received from an initiator with a pending unit
+ * attention condition (before the target generates the contingent allegiance
+ * condition), then the target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * We implement option a).
+ *
*/
static int do_uasp_request_sense(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ __u8 *buf = (__u8 *)cmdiu->bh->buf;
+ __u32 sdinfo;
+ __u32 sd;
+ int valid, rc = 0;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)cmdiu->bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ if (!curlun) {
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->lun->sense_data;
+ sdinfo = curlun->lun->sense_data_info;
+ valid = curlun->lun->info_valid << 7;
+
+ /*
+ * If sense data exists, send it and preserve
+ * unit attention data, then clear sent sense
+ * data.
+ */
+ if (sd) {
+ curlun->lun->sense_data = SS_NO_SENSE;
+ curlun->lun->sense_data_info = 0;
+ curlun->lun->info_valid = 0;
+ /*
+ * If no sense data, sent unit attention data
+ * then clear the sent unit attention data.
+ */
+ } else {
+ sd = curlun->lun->unit_attention_data;
+ sdinfo = 0;
+ valid = 0;
+ curlun->lun->unit_attention_data =
+ SS_NO_SENSE;
+ }
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ /* Sense information */
+ put_unaligned_be32(sdinfo, &buf[3]);
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
+ min(18, (int)cmdiu->cdb[4]),
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)cmdiu->bh->buf,
+ cmdiu->tag, status, sense);
+
+ fill_usb_request(cmdiu->bh->inreq, cmdiu->bh->buf,
+ UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}
/**
@@ -165,7 +419,30 @@ static int do_uasp_test_unit_ready(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf, cmdiu->tag,
+ status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0, cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ return 1;
}
/**
@@ -183,7 +460,161 @@ static int do_uasp_mode_sense(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u8 *buf = (__u8 *)bh->buf;
+ __u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit, rc = 0;
+ int mscmnd = cmdiu->cdb[0];
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+ page_code = cmdiu->cdb[2] & 0x3f;
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if ((cmdiu->cdb[1] & ~0x08) != 0) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if ((cmdiu->cdb[2] >> 6) == 3) {
+ sense = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (page_code != 0x08 && page_code != 0x3f) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(req, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ pc = cmdiu->cdb[2] >> 6;
+ page_code = cmdiu->cdb[2] & 0x3f;
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+ memset(buf, 0, 8);
+
+ if (mscmnd == MODE_SENSE) {
+ buf[2] = (curlun->lun->ro ? 0x80 : 0x00);
+ buf += 4;
+ limit = 255;
+ } else { /* SC_MODE_SENSE_10 */
+ buf[3] = (curlun->lun->ro ? 0x80 : 0x00);
+ buf += 8;
+ limit = FSG_BUFLEN;
+ }
+ /*
+ * The mode pages, in numerical order.
+ * The only page we support is the Caching page.
+ */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10);
+ /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /*
+ * Check that a valid page was requested and the mode
+ * data length isn't too long.
+ */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ }
+
+ len = min(len, (int)cmdiu->cdb[4]) ;
+
+ if (mscmnd == MODE_SENSE)
+ /* Store the mode data length */
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+
+ fill_usb_request(req, buf0, len, 0, cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ cmdiu->tag, status, sense);
+
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}
/**
@@ -200,7 +631,47 @@ static int do_uasp_prevent_allow(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ int prevent;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ prevent = cmdiu->cdb[4] & 0x01;
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (!curlun->lun->removable) {
+ status = STATUS_CHECK_CONDITION;
+ sense = SS_INVALID_COMMAND;
+ } else if ((cmdiu->cdb[4] & ~0x01) != 0) { /* Mask away Prevent */
+ status = STATUS_CHECK_CONDITION;
+ sense = SS_INVALID_FIELD_IN_CDB;
+ } else {
+ if (curlun->lun->prevent_medium_removal && !prevent)
+ if (fsg_lun_fsync_sub(curlun->lun)) {
+ status = STATUS_CHECK_CONDITION;
+ sense = SS_COMMUNICATION_FAILURE;
+ goto uasp_prevent_allow_status;
+ }
+ curlun->lun->prevent_medium_removal = prevent;
+ }
+
+uasp_prevent_allow_status:
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, (struct sense_iu *)bh->buf,
+ cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0, cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ return 1;
}
/**
@@ -217,7 +688,219 @@ static int do_uasp_read(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u8 mscmnd = cmdiu->cdb[0];
+ loff_t file_offset_tmp;
+ __u32 amount, lba;
+ ssize_t nread;
+ unsigned int partial_page;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ if (!curlun) {
+ ERROR(udev->ucommon->common,
+ "%s() - Error condition - curlun = NULL\n",
+ __func__);
+ sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto switch_cmdiu_state;
+ }
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ if (mscmnd == READ_6) {
+ lba = get_unaligned_be24(&cmdiu->cdb[1]);
+ cmdiu->xfer_len =
+ ((cmdiu->cdb[4] == 0 ? 256 : cmdiu->cdb[4]) << 9);
+ } else {
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ /*
+ * We allow DPO (Disable Page Out = don't save data in
+ * the cache) and FUA (Force Unit Access = don't read
+ * from the cache), but we don't implement them.
+ */
+ if ((cmdiu->cdb[1] & ~0x18) != 0) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto switch_cmdiu_state;
+ }
+
+ if (mscmnd == READ_10)
+ cmdiu->xfer_len =
+ (get_unaligned_be16(&cmdiu->cdb[7]) << 9);
+ else
+ cmdiu->xfer_len =
+ (get_unaligned_be32(&cmdiu->cdb[6]) << 9);
+ }
+ cmdiu->file_offset = ((loff_t) lba) << 9;
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (lba >= curlun->lun->num_sectors) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ curlun->lun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ " xfer_len = %d\n",
+ __func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
+ }
+
+switch_cmdiu_state:
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(req, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+send_more_data: /*
+ * 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)cmdiu->xfer_len, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - cmdiu->file_offset);
+ partial_page = cmdiu->file_offset &
+ (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int) PAGE_CACHE_SIZE -
+ partial_page);
+
+ /*
+ * If we were asked to read past the end of file,
+ * end with an empty buffer.
+ */
+ if (amount == 0) {
+ curlun->lun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ cmdiu->xfer_len = 0;
+ nread = 0;
+ } else {
+ /* Perform the read */
+ file_offset_tmp = cmdiu->file_offset;
+ nread = vfs_read(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+
+ if (nread < 0) {
+ LDBG(curlun->lun,
+ "error in file read: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun->lun,
+ "partial file read: %d/%u\n",
+ (int) nread, amount);
+ nread -= (nread & 511);
+ /* Round down to a block */
+ }
+
+ cmdiu->file_offset += nread;
+ cmdiu->xfer_len -= nread;
+
+ /*
+ * If an error occurred, report it and
+ * its position
+ */
+ if (nread < amount) {
+ curlun->lun->sense_data = sense =
+ SS_UNRECOVERED_READ_ERROR;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto send_status;
+ }
+ }
+
+ fill_usb_request(req, bh->buf, nread, 0,
+ cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
+ /* Completion of sent data is not received yet */
+ DBG(udev->ucommon->common,
+ "%s() - completion for bh is not received",
+ __func__);
+ break;
+ } else {
+ /* Completion of the sent data is done */
+ DBG(udev->ucommon->common,
+ "%s() - COMMAND_STATE_DATA for bh\n", __func__);
+ if (cmdiu->xfer_len == 0)
+ goto send_status;
+ else
+ goto send_more_data;
+ }
+send_status:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+ return rc;
}
/**
@@ -235,7 +918,98 @@ static int do_uasp_read_capacity(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u8 *buf = (__u8 *)bh->buf;
+ __u32 lba;
+ int pmi, rc = 0;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ pmi = cmdiu->cdb[8];
+
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(req, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ put_unaligned_be32(curlun->lun->num_sectors - 1,
+ &buf[0]);
+ /* Max logical block */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+
+ fill_usb_request(req, bh->buf, 8, 0,
+ cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ return rc;
}
/**
@@ -253,7 +1027,99 @@ static int do_uasp_read_format_capacities(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u8 *buf = (__u8 *)bh->buf;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ } else if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_READ_READY, cmdiu->tag);
+ fill_usb_request(req, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Data is not sent, create and submit*/
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /*
+ * Only the Current/Maximum
+ * Capacity Descriptor
+ */
+ buf += 4;
+
+ put_unaligned_be32(curlun->lun->num_sectors, &buf[0]);
+ /* Number of blocks */
+ put_unaligned_be32(512, &buf[4]); /* Block length */
+ buf[4] = 0x02; /* Current capacity */
+
+ fill_usb_request(req, bh->buf, 12, 0,
+ cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_in_complete, udev->op_mode);
+
+ cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else /* Completion of the sent data is done */
+ cmdiu->state = COMMAND_STATE_STATUS;
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}
/**
@@ -271,7 +1137,108 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+ int start, loej;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ start = cmdiu->cdb[4] & 0x01;
+ loej = cmdiu->cdb[4] & 0x02;
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (!curlun->lun->removable) {
+ sense = SS_INVALID_COMMAND;
+ status = STATUS_CHECK_CONDITION;
+ } else if ((cmdiu->cdb[1] & ~0x01) != 0 || /* Mask away Immed */
+ (cmdiu->cdb[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ }
+
+ if (status)
+ goto do_uasp_start_stop_done;
+
+ if (!loej) {
+ /*
+ * No action should be taken regarding loading or ejecting of
+ * the medium
+ */
+ /*
+ * Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded.
+ */
+ if (start && !fsg_lun_is_open(curlun->lun)) {
+ sense = SS_MEDIUM_NOT_PRESENT;
+ status = STATUS_CHECK_CONDITION;
+ }
+ } else {
+ /*
+ * LOEJ = 1 & START = 0 -> requests that the medium
+ * shall be unloaded
+ */
+ if (start) {
+ if (!fsg_lun_is_open(curlun->lun)) {
+ sense = SS_MEDIUM_NOT_PRESENT;
+ status = STATUS_CHECK_CONDITION;
+ }
+ } else {
+ /* Are we allowed to unload the media? */
+ if (curlun->lun->prevent_medium_removal) {
+ DBG(udev->ucommon->common,
+ "%s(): unload attempt prevented\n",
+ __func__);
+ sense = SS_MEDIUM_REMOVAL_PREVENTED;
+ status = STATUS_CHECK_CONDITION;
+ goto do_uasp_start_stop_done;
+ }
+
+ /* Simulate an unload/eject */
+ if (udev->ucommon->common->ops &&
+ udev->ucommon->common->ops->pre_eject) {
+ int r = udev->ucommon->common->ops->pre_eject(
+ udev->ucommon->common, curlun->lun,
+ curlun - udev->ucommon->uluns);
+ if (unlikely(r < 0))
+ status = STATUS_CHECK_CONDITION;
+ else if (r) /* r > 0 means don't aject */
+ goto do_uasp_start_stop_done;
+ }
+
+ up_read(&(udev->ucommon->common->filesem));
+ down_write(&(udev->ucommon->common->filesem));
+ close_lun(curlun);
+ up_write(&(udev->ucommon->common->filesem));
+ down_read(&(udev->ucommon->common->filesem));
+
+ if (udev->ucommon->common->ops &&
+ udev->ucommon->common->ops->post_eject) {
+ if (udev->ucommon->common->ops->
+ post_eject(udev->ucommon->common,
+ curlun->lun,
+ curlun - udev->ucommon->uluns)
+ < 0)
+ status = STATUS_CHECK_CONDITION;
+ }
+ }
+ }
+
+do_uasp_start_stop_done:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}
/**
@@ -292,7 +1259,103 @@ static int do_uasp_verify(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ loff_t file_offset_tmp, file_offset;
+ __u32 ver_len, amount;
+ ssize_t nread;
+ __u32 sense = SS_NO_SENSE;
+ __u8 status = STATUS_GOOD;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ file_offset = (get_unaligned_be32(&cmdiu->cdb[2]) << 9);
+ ver_len = (get_unaligned_be32(&cmdiu->cdb[7]) << 9);
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (file_offset + ver_len > curlun->lun->file_length) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ } else if ((cmdiu->cdb[1] & ~0x10) != 0) {
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it.
+ */
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ } else {
+ if (ver_len == 0)
+ /* Verify all the remaining blocks */
+ ver_len = curlun->lun->file_length - file_offset;
+
+ /* Write out all the dirty buffers before invalidating them */
+ fsg_lun_fsync_sub(curlun->lun);
+ invalidate_sub(curlun->lun);
+
+ /* Just try to read the requested blocks */
+ while (ver_len > 0) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ * If this means reading 0 then we were asked to read
+ * past the end of file.
+ */
+ amount = min((unsigned int) ver_len, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - file_offset);
+ if (amount == 0) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun->lun, "file read %u @ %llu -> %d\n",
+ amount, (unsigned long long) file_offset,
+ (int)nread);
+
+ if (nread < 0) {
+ LDBG(curlun->lun, "error in file verify: %d\n",
+ (int) nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun->lun,
+ "partial file verify: %d/%u\n",
+ (int) nread, amount);
+ /* Round down to a sector */
+ nread -= (nread & 511);
+ }
+
+ if (nread == 0) {
+ sense = SS_UNRECOVERED_READ_ERROR;
+ status = STATUS_CHECK_CONDITION;
+ break;
+ }
+
+ file_offset += nread;
+ ver_len -= nread;
+ }
+ }
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}
/**
@@ -311,7 +1374,291 @@ static int do_uasp_write(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->outreq;
+ loff_t usb_offset = 0;
+ loff_t file_offset_tmp = 0;
+ unsigned int partial_page;
+ __u32 amount = 0;
+ ssize_t nwritten = 0;
+ u32 sense = SS_NO_SENSE;
+ __u32 lba;
+ __u8 status = STATUS_GOOD;
+ int rc = 0;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ if (!curlun) {
+ ERROR(udev->ucommon->common,
+ "%s() - Error condition - curlun = NULL\n",
+ __func__);
+ sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ status = STATUS_CHECK_CONDITION;
+ cmdiu->state = COMMAND_STATE_STATUS;
+ goto send_status;
+ }
+
+ if (curlun->lun->ro) {
+ sense = SS_WRITE_PROTECTED;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->state == COMMAND_STATE_IDLE) {
+ spin_lock(&curlun->lun->filp->f_lock);
+ /* Default is not to wait */
+ curlun->lun->filp->f_flags &= ~O_SYNC;
+ spin_unlock(&curlun->lun->filp->f_lock);
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ switch (cmdiu->cdb[0]) {
+ case WRITE_6:
+ lba = get_unaligned_be24(&cmdiu->cdb[1]);
+ cmdiu->xfer_len =
+ (cmdiu->cdb[4] == 0 ? 256 : cmdiu->cdb[4]) << 9;
+ break;
+ case WRITE_10:
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ cmdiu->xfer_len =
+ get_unaligned_be16(&cmdiu->cdb[7]) << 9;
+ break;
+ case WRITE_12:
+ lba = get_unaligned_be32(&cmdiu->cdb[2]);
+ cmdiu->xfer_len =
+ get_unaligned_be32(&cmdiu->cdb[6]) << 9;
+ break;
+ default:
+ sense = SS_INVALID_COMMAND;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ sense = check_cmdiu(udev, curlun, cmdiu, 1);
+ /* If error sent status with sense data */
+ if (sense) {
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->cdb[0] != WRITE_6) {
+ /*
+ * We allow DPO (Disable Page Out = don't save data in
+ * the cache) and FUA (Force Unit Access = write
+ * directly to the medium). We don't implement DPO; we
+ * implement FUA by performing synchronous output.
+ */
+ if (cmdiu->cdb[1] & ~0x18) {
+ sense = SS_INVALID_FIELD_IN_CDB;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+ if (!curlun->lun->nofua && (cmdiu->cdb[1] & 0x08)) {
+ /* FUA */
+ spin_lock(&curlun->lun->filp->f_lock);
+ curlun->lun->filp->f_flags |= O_SYNC;
+ spin_unlock(&curlun->lun->filp->f_lock);
+ }
+ }
+
+ if (lba >= curlun->lun->num_sectors) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ cmdiu->file_offset = usb_offset = ((loff_t) lba) << 9;
+ if (udev->op_mode == HS_UASP_MODE)
+ cmdiu->state = COMMAND_STATE_RR_WR;
+ else
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ " xfer_len = %d\n",
+ __func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
+ }
+
+ switch (cmdiu->state) {
+ case COMMAND_STATE_RR_WR:
+ /* READ READY not sent, create request and submit */
+ if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ fill_rw_ready_iu((struct rw_ready_iu *)bh->buf,
+ IU_ID_WRITE_READY, cmdiu->tag);
+ fill_usb_request(bh->inreq, cmdiu->bh->buf,
+ UASP_SIZEOF_RW_READY_IU,
+ 0, (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag),
+ status_complete, udev->op_mode);
+
+ cmdiu->ep = udev->status;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 1;
+ break;
+ }
+ /* Completion of sent READ READY IU is not received yet */
+ else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ break;
+ /* Completion of the sent READ READY is done */
+ else {
+ cmdiu->state = COMMAND_STATE_DATA;
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ }
+ case COMMAND_STATE_DATA:
+ /* Queue a request for more data from the host */
+get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
+ /*
+ * 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(cmdiu->xfer_len, FSG_BUFLEN);
+ amount = min((loff_t) amount,
+ curlun->lun->file_length - usb_offset);
+ partial_page = usb_offset & (PAGE_CACHE_SIZE - 1);
+ if (partial_page > 0)
+ amount = min(amount,
+ (unsigned int)PAGE_CACHE_SIZE -
+ partial_page);
+
+ if (amount == 0) {
+ sense = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->lun->sense_data_info = usb_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ amount -= (amount & 511);
+ if (amount == 0)
+ /*
+ * Why were we were asked to transfer a
+ * partial block?
+ */
+ goto send_status;
+
+ /* Get the next buffer */
+ usb_offset += amount;
+
+ fill_usb_request(req, bh->buf, amount, 0,
+ cmdiu, 0, be16_to_cpup(&cmdiu->tag),
+ uasp_bulk_out_complete, udev->op_mode);
+ DBG(udev->ucommon->common, "%s() fill_usb_request for"
+ " out endpoint, amout = %d",
+ __func__, amount);
+
+ cmdiu->ep = udev->fsg_dev.bulk_out;
+ cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
+ rc = 2;
+ break;
+ } else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
+ /* Completion of sent data is not received yet */
+ break;
+ else { /* Completion of the sent data is done */
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ sense = SS_COMMUNICATION_FAILURE;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+ if (req->actual != req->length) {
+ /*
+ * Host decide abort the command
+ * Note: if we going to submit more than one
+ * request for this command, we should abort
+ * all submitted requests for this command
+ */
+ DBG(udev->ucommon->common,
+ "%s() - Host aborted the command\n",
+ __func__);
+ goto send_status;
+ }
+
+ amount = req->actual;
+ if (curlun->lun->file_length - cmdiu->file_offset <
+ amount) {
+ ERROR(udev->ucommon->common,
+ "%s(): write %u @ %llu beyond end %llu\n",
+ __func__, amount,
+ (unsigned long long)cmdiu->file_offset,
+ (unsigned long long)
+ curlun->lun->file_length);
+ amount = curlun->lun->file_length -
+ cmdiu->file_offset;
+ }
+
+ /* Perform the write */
+ file_offset_tmp = cmdiu->file_offset;
+ nwritten = vfs_write(curlun->lun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ DBG(udev->ucommon->common,
+ "%s(): file write %u @ %llu -> %d\n", __func__,
+ amount, (unsigned long long)cmdiu->file_offset,
+ (int)nwritten);
+
+ if (nwritten < 0) {
+ ERROR(udev->ucommon->common,
+ "%s(): error in file write: %d\n",
+ __func__, (int)nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ DBG(udev->ucommon->common,
+ "%s(): partial file write: %d/%u\n",
+ __func__, (int)nwritten, amount);
+ nwritten -= (nwritten & 511);
+ /* Round down to a block */
+ }
+
+ cmdiu->file_offset += nwritten;
+ cmdiu->xfer_len -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ sense = SS_WRITE_ERROR;
+ curlun->lun->sense_data_info =
+ cmdiu->file_offset >> 9;
+ curlun->lun->info_valid = 1;
+ status = STATUS_CHECK_CONDITION;
+ goto send_status;
+ }
+
+ if (cmdiu->xfer_len == 0) {
+ DBG(udev->ucommon->common,
+ "%s() - cmdiu->xferlen = 0, "
+ "send status\n", __func__);
+ goto send_status;
+ }
+ cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
+ goto get_more_data;
+
+send_status:
+ cmdiu->state = COMMAND_STATE_STATUS;
+ }
+ case COMMAND_STATE_STATUS:
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(bh->inreq, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0,
+ be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+ rc = 1;
+ break;
+ default:
+ break;
+ }
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return rc;
}
/**
@@ -329,7 +1676,39 @@ static int do_uasp_synchronize_cache(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct cmd_iu *cmdiu)
{
- return 0;
+ struct fsg_buffhd *bh = cmdiu->bh;
+ struct usb_request *req = bh->inreq;
+ uint32_t sense = SS_NO_SENSE;
+ uint8_t status = STATUS_GOOD;
+ int rc;
+
+ DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ /* Check is cmdiu is filled correctly */
+ sense = check_cmdiu(udev, curlun, cmdiu, 0);
+
+ /*
+ * We ignore the requested LBA and write out all file's
+ * dirty data buffers.
+ */
+ rc = fsg_lun_fsync_sub(curlun->lun);
+
+ if (sense)
+ status = STATUS_CHECK_CONDITION;
+ else if (rc) {
+ sense = SS_WRITE_ERROR;
+ status = STATUS_CHECK_CONDITION;
+ }
+
+ cmdiu->state = COMMAND_STATE_STATUS;
+ fill_sense_iu(udev, bh->buf, cmdiu->tag, status, sense);
+ fill_usb_request(req, bh->buf, UASP_SIZEOF_SENSE_IU, 0,
+ (void *)cmdiu, 0, be16_to_cpup(&cmdiu->tag), status_complete,
+ udev->op_mode);
+ cmdiu->ep = udev->status;
+
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ return 1;
}
/**
@@ -413,7 +1792,7 @@ static void process_cmdiu(struct uasp_dev *udev,
fill_usb_request(cmdiu->bh->inreq, (void *)siu,
UASP_SIZEOF_SENSE_IU, 0,
(void *)cmdiu, 0, be16_to_cpup(&cmdiu->tag),
- status_complete);
+ status_complete, udev->op_mode);
cmdiu->ep = udev->status;
rc = 1;
break;
@@ -489,7 +1868,8 @@ void do_cmdiu(struct uasp_dev *udev, struct uasp_lun *curlun)
"cmdiu!\n", __func__);
continue;
}
- } else if (cmdiu->state == COMMAND_STATE_DATA) {
+ } else if (cmdiu->state == COMMAND_STATE_DATA ||
+ cmdiu->state == COMMAND_STATE_RR_WR) {
if (cmdiu->req_sts == CMD_REQ_COMPLETED)
spin_unlock_irqrestore(
&(udev->ucommon->common->lock), flags);
diff --git a/drivers/usb/gadget/uasp_tmiu.c b/drivers/usb/gadget/uasp_tmiu.c
index 23f9351..5f70424 100644
--- a/drivers/usb/gadget/uasp_tmiu.c
+++ b/drivers/usb/gadget/uasp_tmiu.c
@@ -54,10 +54,38 @@ void fill_response_iu(struct uasp_dev *udev,
* commands.
*/
static void reset_lun(struct uasp_dev *udev,
- struct uasp_lun *curlun,
- struct tm_iu *tmiu)
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
{
+ struct response_iu *riu;
+ uint8_t status;
+ unsigned long flags;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto res_lun_fill_response;
+ }
+
+ abort_commands(udev, &curlun->cmd_queue, &curlun->tm_func_queue,
+ &(curlun->lock));
+
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->pending_requests = 0;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+
+ curlun->lun->unit_attention_data = SS_RESET_OCCURRED;
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+
+res_lun_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0, status);
+
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
}
/**
@@ -67,15 +95,69 @@ static void reset_lun(struct uasp_dev *udev,
* addressed to a valid LUN, 0 otherwise.
* @tmiu: TM FUNCTION IU to be processed.
*
- * This function aborts the command with the same ip_tag as in the
- * tmiu->task_tag. It's valid only for command that are handled by a specific
- * LUN .
+ * This function aborts the command with the same tag as in the
+ * tmiu->task_tag. It's valid only for command that are handled
+ * by a specific LUN .
*/
static void abort_task(struct uasp_dev *udev,
- struct uasp_lun *curlun,
- struct tm_iu *tmiu)
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
{
+ struct cmd_iu *cmdiu, *tmp;
+ struct response_iu *riu;
+ unsigned long flags;
+ uint8_t status;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto abrt_task_fill_response;
+ }
+
+ /* Try to find the command in curlun */
+ list_for_each_entry_safe(cmdiu, tmp, &curlun->cmd_queue, node)
+ if (cmdiu->tag == tmiu->task_tag)
+ goto found;
+
+ /* Command with specified ipt_tag not found */
+ DBG(udev->ucommon->common, "%s(): cmdiu with tag %04x wasn't found\n",
+ __func__, tmiu->task_tag);
+ cmdiu = 0;
+
+found:
+ if (cmdiu) {
+ spin_lock_irqsave(&(curlun->lock), flags);
+ if (cmdiu->state == COMMAND_STATE_DATA ||
+ cmdiu->state == COMMAND_STATE_RR_WR) {
+ if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ if (cmdiu->bh->inreq_busy)
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->inreq);
+ if (cmdiu->bh->outreq_busy)
+ usb_ep_dequeue(cmdiu->ep,
+ cmdiu->bh->outreq);
+ spin_lock_irqsave(&(curlun->lock), flags);
+ }
+ } else if (cmdiu->state == COMMAND_STATE_STATUS) {
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ usb_ep_dequeue(cmdiu->ep, cmdiu->bh->inreq);
+ spin_lock_irqsave(&(curlun->lock), flags);
+ } else
+ cmdiu->state = COMMAND_STATE_ABORTED;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ }
+
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+
+abrt_task_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0, status);
+
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
}
/**
@@ -88,10 +170,36 @@ static void abort_task(struct uasp_dev *udev,
* This function aborts all the commands pending for the specified LUN.
*/
static void abort_task_set(struct uasp_dev *udev,
- struct uasp_lun *curlun,
- struct tm_iu *tmiu)
+ struct uasp_lun *curlun,
+ struct tm_iu *tmiu)
{
+ struct response_iu *riu;
+ uint8_t status;
+ unsigned long flags;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto abrt_ts_fill_response;
+ }
+
+ abort_commands(udev, &curlun->cmd_queue, 0, &(curlun->lock));
+
+ spin_lock_irqsave(&(curlun->lock), flags);
+ curlun->pending_requests = 0;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+
+abrt_ts_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0, status);
+
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
}
/**
@@ -100,9 +208,54 @@ static void abort_task_set(struct uasp_dev *udev,
* @tmiu: TM FUNCTION IU to be processed.
*/
static void reset_nexus(struct uasp_dev *udev,
- struct tm_iu *tmiu)
+ struct tm_iu *tmiu)
{
+ struct response_iu *riu;
+ unsigned long flags;
+ uint8_t status;
+ int rc = 0;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+
+ riu = (struct response_iu *)tmiu->bh->buf;
+
+ run_lun_threads(udev, LUN_STATE_RESET);
+
+ /*
+ * Wait for luns completing the nexus reset.
+ * Sleep if luns are in processing
+ */
+ while (!all_lun_state_non_processing(udev)) {
+ DBG(udev->ucommon->common,
+ "%s() - Luns are in process. Going to sleep\n", __func__);
+ rc = sleep_thread(udev->ucommon->common);
+ if (rc) {
+ ERROR(udev->ucommon->common,
+ "%s() - sleep_thread failed! (%d)", __func__, rc);
+ status = RESPONSE_TM_FUNCTION_FAILED;
+ goto reset_nexus_fill_response;
+ }
+ DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+ rc = 0;
+ }
+
+ /* Abort general commands and tmius */
+ abort_commands(udev, &udev->cmd_queue, &udev->tm_func_queue,
+ &(udev->ucommon->common->lock));
+
+ spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ udev->pending_requests = 0;
+ spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+reset_nexus_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0,
+ RESPONSE_TM_FUNCTION_COMPLETE);
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
}
/**
@@ -120,7 +273,37 @@ static void query_unit_attention(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct tm_iu *tmiu)
{
+ struct response_iu *riu;
+ uint8_t status;
+ uint32_t resp_info = 0;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto qut_fill_response;
+ }
+
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+
+ if (curlun->lun->unit_attention_data) {
+ status = RESPONSE_TM_FUNCTION_SUCCEEDED;
+ /*
+ * We don't keep queue of unit attention conditions,
+ * and deferred errors also. We only keep unit attention
+ * condition with higher precedence level.
+ */
+ resp_info = curlun->lun->unit_attention_data | (1 << 20);
+ }
+
+qut_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, resp_info, status);
+
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+
+ tmiu->ep = udev->status;
}
@@ -135,7 +318,40 @@ static void query_task(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct tm_iu *tmiu)
{
+ struct cmd_iu *cmdiu = 0;
+ struct cmd_iu *tmp_cmdiu;
+ struct response_iu *riu;
+ unsigned long flags;
+ uint8_t status = RESPONSE_TM_FUNCTION_COMPLETE;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto q_task_fill_response;
+ }
+
+ /* Try to find in command in curlun */
+ spin_lock_irqsave(&(curlun->lock), flags);
+ list_for_each_entry_safe(cmdiu, tmp_cmdiu, &curlun->cmd_queue, node) {
+ if (cmdiu->tag == tmiu->task_tag) {
+ if (cmdiu->state == COMMAND_STATE_IDLE ||
+ cmdiu->state == COMMAND_STATE_DATA ||
+ cmdiu->state == COMMAND_STATE_RR_WR ||
+ cmdiu->state == COMMAND_STATE_STATUS)
+ status = RESPONSE_TM_FUNCTION_SUCCEEDED;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ goto q_task_fill_response;
+ }
+ }
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+
+q_task_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0, status);
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
}
/**
@@ -149,7 +365,42 @@ static void query_task_set(struct uasp_dev *udev,
struct uasp_lun *curlun,
struct tm_iu *tmiu)
{
+ struct cmd_iu *cmdiu = 0;
+ struct cmd_iu *tmp_cmdiu;
+ struct response_iu *riu;
+ unsigned long flags;
+ uint8_t status;
+
DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ riu = (struct response_iu *)tmiu->bh->buf;
+ if (!curlun) {
+ status = RESPONSE_INCORRECT_LUN;
+ goto q_task_set_fill_response;
+ }
+
+ /* Try to find none-completed command in curlun */
+ spin_lock_irqsave(&(curlun->lock), flags);
+ list_for_each_entry_safe(cmdiu, tmp_cmdiu, &curlun->cmd_queue, node) {
+ if (cmdiu->state == COMMAND_STATE_IDLE ||
+ cmdiu->state == COMMAND_STATE_RR_WR ||
+ cmdiu->state == COMMAND_STATE_DATA ||
+ cmdiu->state == COMMAND_STATE_STATUS) {
+ status = RESPONSE_TM_FUNCTION_SUCCEEDED;
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ goto q_task_set_fill_response;
+ }
+ }
+
+ spin_unlock_irqrestore(&(curlun->lock), flags);
+ status = RESPONSE_TM_FUNCTION_COMPLETE;
+
+q_task_set_fill_response:
+ fill_response_iu(udev, riu, tmiu->tag, 0, status);
+ fill_usb_request(tmiu->bh->inreq, (void *)riu, UASP_SIZEOF_RESPONSE_IU,
+ 0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
+ status_complete, udev->op_mode);
+ tmiu->ep = udev->status;
+ DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
}
/**
@@ -206,7 +457,7 @@ static void process_tmiu(struct uasp_dev *udev,
fill_usb_request(tmiu->bh->inreq, (void *)riu,
UASP_SIZEOF_RESPONSE_IU, 0,
(void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
- status_complete);
+ status_complete, udev->op_mode);
tmiu->ep = udev->status;
break;
}
--
1.7.6
--
Sent by a Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [RFC/PATCH v4 3/3] uas: Supporting UAS and BOT configuration.
2011-12-04 20:12 [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 2/3] uas: MS UAS Gadget driver - Implementation Shimrit Malichi
@ 2011-12-04 20:12 ` Shimrit Malichi
2011-12-05 8:13 ` [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Felipe Balbi
2 siblings, 0 replies; 4+ messages in thread
From: Shimrit Malichi @ 2011-12-04 20:12 UTC (permalink / raw)
To: greg; +Cc: linux-usb, linux-arm-msm, balbi, ablay, Shimrit Malichi,
open list
Two configuration were added to the mass storage gadget: BOT (the first one),
and UAS. The linux host can switch between the configurations by changing
the value of bConfigValue in /sys/bus/usb/devices/<your_usb_device>/
Also, a fallback to HS in case SS configuration fails was added.
Signed-off-by: Shimrit Malichi <smalichi@codeaurora.org>
---
drivers/usb/gadget/f_mass_storage.c | 92 +---
drivers/usb/gadget/f_uasp.c | 1063 ++++++++++++++++++++---------------
drivers/usb/gadget/f_uasp.h | 106 +++-
drivers/usb/gadget/mass_storage.c | 88 ++--
drivers/usb/gadget/storage_common.c | 66 +++-
drivers/usb/gadget/uasp_cmdiu.c | 176 +++---
drivers/usb/gadget/uasp_tmiu.c | 60 +-
7 files changed, 955 insertions(+), 696 deletions(-)
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index b777d72..96da3f6 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -317,39 +317,16 @@ static const char fsg_string_interface[] = "Mass Storage";
struct fsg_dev;
struct fsg_common;
-/* FSF callback functions */
-struct fsg_operations {
- /*
- * Callback function to call when thread exits. If no
- * callback is set or it returns value lower then zero MSF
- * will force eject all LUNs it operates on (including those
- * marked as non-removable or with prevent_medium_removal flag
- * set).
- */
- int (*thread_exits)(struct fsg_common *common);
-
- /*
- * Called prior to ejection. Negative return means error,
- * zero means to continue with ejection, positive means not to
- * eject.
- */
- int (*pre_eject)(struct fsg_common *common,
- struct fsg_lun *lun, int num);
- /*
- * Called after ejection. Negative return means error, zero
- * or positive is just a success.
- */
- int (*post_eject)(struct fsg_common *common,
- struct fsg_lun *lun, int num);
-};
-
/* Data shared by all the FSG instances. */
struct fsg_common {
struct usb_gadget *gadget;
struct usb_composite_dev *cdev;
+ struct msg_common_data *msg_common;
struct fsg_dev *fsg, *new_fsg;
wait_queue_head_t fsg_wait;
+ struct mutex *config_mutex;
+
/* filesem protects: backing files in use */
struct rw_semaphore filesem;
@@ -408,31 +385,6 @@ struct fsg_common {
struct kref ref;
};
-struct fsg_config {
- unsigned nluns;
- struct fsg_lun_config {
- const char *filename;
- char ro;
- char removable;
- char cdrom;
- char nofua;
- } luns[FSG_MAX_LUNS];
-
- const char *lun_name_format;
- const char *thread_name;
-
- /* Callback functions. */
- const struct fsg_operations *ops;
- /* Gadget's private data. */
- void *private_data;
-
- const char *vendor_name; /* 8 characters or less */
- const char *product_name; /* 16 characters or less */
- u16 release;
-
- char can_stall;
-};
-
struct fsg_dev {
struct usb_function function;
struct usb_gadget *gadget; /* Copy of cdev->gadget */
@@ -2364,7 +2316,10 @@ reset:
usb_ep_disable(fsg->bulk_out);
fsg->bulk_out_enabled = 0;
}
-
+ DBG(common,
+ "%s()- disabled endpoints, releasing config mutex\n",
+ __func__);
+ mutex_unlock(common->config_mutex);
common->fsg = NULL;
wake_up(&common->fsg_wait);
}
@@ -2373,6 +2328,9 @@ reset:
if (!new_fsg || rc)
return rc;
+ DBG(common, "%s()- Enabling endpoints, taking config mutex\n",
+ __func__);
+ mutex_lock(common->config_mutex);
common->fsg = new_fsg;
fsg = common->fsg;
@@ -2703,8 +2661,7 @@ static inline void fsg_common_put(struct fsg_common *common)
static struct fsg_common *fsg_common_init(struct fsg_common *common,
struct usb_composite_dev *cdev,
- struct fsg_config *cfg,
- int start_thread)
+ struct fsg_config *cfg)
{
struct usb_gadget *gadget = cdev->gadget;
struct fsg_buffhd *bh;
@@ -2750,6 +2707,7 @@ static struct fsg_common *fsg_common_init(struct fsg_common *common,
common->ep0 = gadget->ep0;
common->ep0req = cdev->req;
common->cdev = cdev;
+ common->config_mutex = &(cfg->config_mutex);
/* Maybe allocate device-global string IDs, and patch descriptors */
if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
@@ -2866,15 +2824,12 @@ buffhds_first_it:
spin_lock_init(&common->lock);
kref_init(&common->ref);
- /* Tell the thread to start working */
- if (start_thread) {
- common->thread_task =
- kthread_create(fsg_main_thread, common,
- cfg->thread_name ?: "file-storage");
- if (IS_ERR(common->thread_task)) {
- rc = PTR_ERR(common->thread_task);
- goto error_release;
- }
+ common->thread_task =
+ kthread_create(fsg_main_thread, common,
+ cfg->thread_name ?: "file-storage");
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
}
init_completion(&common->thread_notifier);
init_waitqueue_head(&common->fsg_wait);
@@ -2905,11 +2860,10 @@ buffhds_first_it:
}
kfree(pathbuf);
- if (start_thread) {
- DBG(common, "I/O thread pid: %d\n",
- task_pid_nr(common->thread_task));
- wake_up_process(common->thread_task);
- }
+ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+ wake_up_process(common->thread_task);
+
return common;
error_luns:
@@ -3200,6 +3154,6 @@ fsg_common_from_params(struct fsg_common *common,
{
struct fsg_config cfg;
fsg_config_from_params(&cfg, params);
- return fsg_common_init(common, cdev, &cfg, 1);
+ return fsg_common_init(common, cdev, &cfg);
}
diff --git a/drivers/usb/gadget/f_uasp.c b/drivers/usb/gadget/f_uasp.c
index 3db5a11..f3d1efa 100644
--- a/drivers/usb/gadget/f_uasp.c
+++ b/drivers/usb/gadget/f_uasp.c
@@ -176,11 +176,9 @@
#include <linux/kref.h>
#include <linux/kthread.h>
#include <linux/string.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/kernel.h>
#include <linux/usb/storage.h>
+#include "f_uasp.h"
#include "uasp_cmdiu.c"
#include "uasp_tmiu.c"
@@ -201,20 +199,21 @@ uasp_intf_desc = {
/* BULK-in pipe descriptors */
static struct usb_endpoint_descriptor
-uasp_bulk_in_desc = {
+uasp_fs_bulk_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(64),
};
-struct usb_pipe_usage_descriptor
-uasp_bulk_in_pipe_usg_desc = {
- .bLength = sizeof uasp_bulk_in_pipe_usg_desc,
- .bDescriptorType = USB_DT_PIPE_USAGE,
- .bPipeID = PIPE_ID_DATA_IN,
- .Reserved = 0,
+static struct usb_endpoint_descriptor
+uasp_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
};
struct usb_endpoint_descriptor
@@ -238,22 +237,31 @@ uasp_bulk_in_ep_comp_desc = {
.wBytesPerInterval = 0,
};
+struct usb_pipe_usage_descriptor
+uasp_bulk_in_pipe_usg_desc = {
+ .bLength = sizeof uasp_bulk_in_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_DATA_IN,
+ .Reserved = 0,
+};
+
/* BULK-out pipe descriptors */
struct usb_endpoint_descriptor
-uasp_bulk_out_desc = {
+uasp_fs_bulk_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(64),
};
-struct usb_pipe_usage_descriptor
-uasp_bulk_out_pipe_usg_desc = {
- .bLength = sizeof uasp_bulk_out_pipe_usg_desc,
- .bDescriptorType = USB_DT_PIPE_USAGE,
- .bPipeID = PIPE_ID_DATA_OUT,
- .Reserved = 0,
+struct usb_endpoint_descriptor
+uasp_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
};
struct usb_endpoint_descriptor
@@ -277,22 +285,31 @@ uasp_bulk_out_ep_comp_desc = {
.wBytesPerInterval = 0,
};
+struct usb_pipe_usage_descriptor
+uasp_bulk_out_pipe_usg_desc = {
+ .bLength = sizeof uasp_bulk_out_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_DATA_OUT,
+ .Reserved = 0,
+};
+
/* Status pipe - descriptors */
struct usb_endpoint_descriptor
-uasp_status_in_desc = {
+uasp_fs_status_in_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(64),
};
-struct usb_pipe_usage_descriptor
-uasp_status_in_pipe_usg_desc = {
- .bLength = sizeof uasp_status_in_pipe_usg_desc,
- .bDescriptorType = USB_DT_PIPE_USAGE,
- .bPipeID = PIPE_ID_STS,
- .Reserved = 0,
+struct usb_endpoint_descriptor
+uasp_hs_status_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
};
struct usb_endpoint_descriptor
@@ -316,22 +333,32 @@ uasp_status_in_ep_comp_desc = {
.wBytesPerInterval = 0,
};
+
+struct usb_pipe_usage_descriptor
+uasp_status_in_pipe_usg_desc = {
+ .bLength = sizeof uasp_status_in_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_STS,
+ .Reserved = 0,
+};
+
/* Command pipe descriptors */
struct usb_endpoint_descriptor
-uasp_command_out_desc = {
+uasp_fs_command_out_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
- .wMaxPacketSize = cpu_to_le16(512),
+ .wMaxPacketSize = cpu_to_le16(64),
};
-struct usb_pipe_usage_descriptor
-uasp_command_out_pipe_usg_desc = {
- .bLength = sizeof uasp_command_out_pipe_usg_desc,
- .bDescriptorType = USB_DT_PIPE_USAGE,
- .bPipeID = PIPE_ID_CMD,
- .Reserved = 0,
+struct usb_endpoint_descriptor
+uasp_hs_command_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
};
struct usb_endpoint_descriptor
@@ -355,16 +382,38 @@ uasp_command_out_ep_comp_desc = {
.wBytesPerInterval = 0,
};
+struct usb_pipe_usage_descriptor
+uasp_command_out_pipe_usg_desc = {
+ .bLength = sizeof uasp_command_out_pipe_usg_desc,
+ .bDescriptorType = USB_DT_PIPE_USAGE,
+ .bPipeID = PIPE_ID_CMD,
+ .Reserved = 0,
+};
+
+/* FS configuration function descriptors */
+struct usb_descriptor_header *uasp_fs_function_desc[] = {
+ (struct usb_descriptor_header *) &uasp_intf_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_fs_bulk_out_desc,
+ (struct usb_descriptor_header *) &uasp_bulk_out_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_fs_status_in_desc,
+ (struct usb_descriptor_header *) &uasp_status_in_pipe_usg_desc,
+ (struct usb_descriptor_header *) &uasp_fs_command_out_desc,
+ (struct usb_descriptor_header *) &uasp_command_out_pipe_usg_desc,
+ NULL,
+};
+
/* HS configuration function descriptors */
struct usb_descriptor_header *uasp_hs_function_desc[] = {
(struct usb_descriptor_header *) &uasp_intf_desc,
- (struct usb_descriptor_header *) &uasp_bulk_in_desc,
+ (struct usb_descriptor_header *) &uasp_hs_bulk_in_desc,
(struct usb_descriptor_header *) &uasp_bulk_in_pipe_usg_desc,
- (struct usb_descriptor_header *) &uasp_bulk_out_desc,
+ (struct usb_descriptor_header *) &uasp_hs_bulk_out_desc,
(struct usb_descriptor_header *) &uasp_bulk_out_pipe_usg_desc,
- (struct usb_descriptor_header *) &uasp_status_in_desc,
+ (struct usb_descriptor_header *) &uasp_hs_status_in_desc,
(struct usb_descriptor_header *) &uasp_status_in_pipe_usg_desc,
- (struct usb_descriptor_header *) &uasp_command_out_desc,
+ (struct usb_descriptor_header *) &uasp_hs_command_out_desc,
(struct usb_descriptor_header *) &uasp_command_out_pipe_usg_desc,
NULL,
};
@@ -388,35 +437,61 @@ struct usb_descriptor_header *uasp_ss_function_desc[] = {
};
/*--------------------------------------------------------------------------*/
+static int uasp_exception_in_progress(struct uasp_common *common)
+{
+ return common->state > UASP_STATE_IDLE;
+}
+
+/* Caller must hold udev->lock */
+static void uasp_wakeup_thread(struct uasp_common *common)
+{
+ /* Tell the main thread that something has happened */
+ common->thread_wakeup_needed = 1;
+ if (common->thread_task)
+ wake_up_process(common->thread_task);
+}
+
static inline struct uasp_dev *uaspd_from_func(struct usb_function *f)
{
- struct fsg_dev *fsg_dev = fsg_from_func(f);
- return container_of(fsg_dev, struct uasp_dev, fsg_dev);
+ return container_of(f, struct uasp_dev, function);
+}
+
+static void uasp_raise_exception(struct uasp_common *common,
+ enum fsg_state new_state)
+{
+ unsigned long flags;
+
+ /*
+ * Do nothing if a higher-priority exception is already in progress.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal.
+ */
+ spin_lock_irqsave(&common->lock, flags);
+ if (common->state <= new_state) {
+ common->state = new_state;
+ if (common->thread_task)
+ send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ common->thread_task);
+ }
+ spin_unlock_irqrestore(&common->lock, flags);
}
static void uasp_common_release(struct kref *ref)
{
struct uasp_common *ucommon =
container_of(ref, struct uasp_common, ref);
- struct uasp_lun *ulun;
- int i;
- /* First stop all lun threads */
- run_lun_threads(ucommon->udev, LUN_STATE_EXIT);
- for (i = 0; i < ucommon->common->nluns; i++) {
- ulun = &(ucommon->uluns[i]);
- if (ulun->lun_state != LUN_STATE_TERMINATED) {
- wait_for_completion(&ulun->thread_notifier);
- /* The cleanup routine waits for this completion also */
- complete(&ulun->thread_notifier);
- }
+ /* If the thread isn't already dead, tell it to exit now */
+ if (ucommon->state != UASP_STATE_TERMINATED) {
+ uasp_raise_exception(ucommon, FSG_STATE_EXIT);
+ wait_for_completion(&ucommon->thread_notifier);
}
- fsg_common_release(&(ucommon->common->ref));
+
kfree(ucommon->uluns);
+ kfree(ucommon->ubufs);
kfree(ucommon);
}
-
static inline void uasp_common_put(struct uasp_common *common)
{
kref_put(&(common->ref), uasp_common_release);
@@ -427,23 +502,48 @@ static struct uasp_lun *find_lun_by_id(struct uasp_dev *udev, __u8 *lun_id)
int i;
struct uasp_lun *curlun;
- DBG(udev->ucommon->common, "%s() - Enter.\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter.\n", __func__);
- for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ for (i = 0; i < udev->ucommon->nluns; ++i) {
curlun = &udev->ucommon->uluns[i];
if (memcmp(lun_id, curlun->lun_id, 8) == 0) {
- DBG(udev->ucommon->common, "%s() - LUN found\n",
+ DBG(udev->ucommon, "%s() - LUN found\n",
__func__);
return curlun;
}
}
- DBG(udev->ucommon->common, "%s() - LUN not found\n", __func__);
+ DBG(udev->ucommon, "%s() - LUN not found\n", __func__);
return 0;
}
/**
- * wakeup_lun_thread() - Wakes up the given LUn thread
+ * uasp_sleep_thread() - sleep UASP main thread
+ * @common: pointer to uasp common data structure
+ */
+int uasp_sleep_thread(struct uasp_common *common)
+{
+ int rc = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (common->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+ common->thread_wakeup_needed = 0;
+ return rc;
+}
+
+/**
+ * wakeup_lun_thread() - Wakes up the given LUN thread
* @lun: the LUN which thread needs wakening
*
* NOTE: Caller must hold uasp_lun->lock
@@ -472,31 +572,31 @@ static void command_complete(struct usb_ep *ep, struct usb_request *req)
unsigned long flags;
if (req->actual > 0)
- dump_msg(udev->ucommon->common, "command", req->buf,
+ dump_msg(udev->ucommon, "command", req->buf,
req->actual);
- DBG(udev->ucommon->common, "%s() - Enter", __func__);
+ DBG(udev->ucommon, "%s() - Enter", __func__);
if (req != udev->cmd_buff.outreq) {
- ERROR(udev->ucommon->common, "(%s) req(%p) != "
+ ERROR(udev->ucommon, "(%s) req(%p) != "
"cmd_buff.outreq(%p), udev=%p,"
" common->state = %d\n",
__func__, req, udev->cmd_buff.outreq, udev,
- udev->ucommon->common->state);
+ udev->ucommon->state);
}
if (req->status == -ECONNRESET) {
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
udev->cmd_buff.state = BUF_STATE_EMPTY;
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
usb_ep_fifo_flush(ep);
return;
}
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
udev->cmd_buff.state = BUF_STATE_FULL;
- wakeup_thread(udev->ucommon->common);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ uasp_wakeup_thread(udev->ucommon);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
}
/**
@@ -520,12 +620,12 @@ void status_complete(struct usb_ep *ep, struct usb_request *req)
uint8_t cmd_id = ((uint8_t *)req->context)[0];
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter", __func__);
+ DBG(udev->ucommon, "%s() - Enter", __func__);
if (req->status == -ECONNRESET)
usb_ep_fifo_flush(ep);
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
/* If Sense IU is filled for TM FUNCTION IU */
if (cmd_id == IU_ID_TASK_MANAGEMENT) {
tmiu = (struct tm_iu *)req->context;
@@ -534,14 +634,14 @@ void status_complete(struct usb_ep *ep, struct usb_request *req)
if (req->status == -ERESTART)
tmiu->state = COMMAND_STATE_ABORTED;
else if (req->status) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - TMIU FAILED!!! Status = %d",
__func__, req->status);
tmiu->state = COMMAND_STATE_FAILED;
} else
tmiu->state = COMMAND_STATE_COMPLETED;
}
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - received IU_ID_TASK_MANAGEMENT "
"(Code = %02x tmiu->state = %d)\n",
__func__, tmiu->tm_function, tmiu->state);
@@ -556,14 +656,14 @@ void status_complete(struct usb_ep *ep, struct usb_request *req)
if (req->status == -ERESTART)
cmdiu->state = COMMAND_STATE_ABORTED;
else if (req->status) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - CMDIU FAILED!!! Status = %d",
__func__, req->status);
cmdiu->state = COMMAND_STATE_FAILED;
} else if (cmdiu->state == COMMAND_STATE_STATUS)
cmdiu->state = COMMAND_STATE_COMPLETED;
}
- DBG(udev->ucommon->common, "%s() - received IU_ID_COMMAND"
+ DBG(udev->ucommon, "%s() - received IU_ID_COMMAND"
" (OpCode = %02x, smdiu->state = %d)\n",
__func__, cmdiu->cdb[0], cmdiu->state);
cmdiu->req_sts = CMD_REQ_COMPLETED;
@@ -571,29 +671,29 @@ void status_complete(struct usb_ep *ep, struct usb_request *req)
curlun = find_lun_by_id(udev, cmdiu->lun);
} else {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - received invalid IU (iu_id = %02x)!\n",
__func__, cmd_id);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
return;
}
if (curlun) {
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ spin_unlock_irqrestore(&(udev->ucommon->lock),
flags);
spin_lock_irqsave(&(curlun->lock), flags);
curlun->pending_requests++;
curlun->active_requests--;
wakeup_lun_thread(curlun);
spin_unlock_irqrestore(&(curlun->lock), flags);
- spin_lock_irqsave(&(udev->ucommon->common->lock),
+ spin_lock_irqsave(&(udev->ucommon->lock),
flags);
} else {
udev->pending_requests++;
udev->active_requests--;
- wakeup_thread(udev->ucommon->common);
+ uasp_wakeup_thread(udev->ucommon);
}
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
}
/**
@@ -613,14 +713,14 @@ void uasp_bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
struct cmd_iu *cmdiu;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (req->status == -ECONNRESET)
usb_ep_fifo_flush(ep);
cmdiu = (struct cmd_iu *)req->context;
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
if (cmdiu->state != COMMAND_STATE_ABORTED &&
cmdiu->state != COMMAND_STATE_FAILED) {
if (req->status == -ERESTART)
@@ -635,19 +735,19 @@ void uasp_bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
curlun = find_lun_by_id(udev, cmdiu->lun);
if (curlun) {
- spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_unlock_irqrestore(&udev->ucommon->lock, flags);
spin_lock_irqsave(&curlun->lock, flags);
curlun->pending_requests++;
curlun->active_requests--;
wakeup_lun_thread(curlun);
spin_unlock_irqrestore(&curlun->lock, flags);
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
} else {
udev->pending_requests++;
udev->active_requests--;
- wakeup_thread(udev->ucommon->common);
+ uasp_wakeup_thread(udev->ucommon);
}
- spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_unlock_irqrestore(&udev->ucommon->lock, flags);
}
@@ -668,12 +768,12 @@ void uasp_bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
struct cmd_iu *cmdiu;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (req->status == -ECONNRESET)
usb_ep_fifo_flush(ep);
- spin_lock_irqsave(&udev->ucommon->common->lock, flags);
+ spin_lock_irqsave(&udev->ucommon->lock, flags);
cmdiu = (struct cmd_iu *)req->context;
if (cmdiu->state != COMMAND_STATE_ABORTED &&
@@ -689,19 +789,19 @@ void uasp_bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
curlun = find_lun_by_id(udev, cmdiu->lun);
if (curlun) {
- spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_unlock_irqrestore(&udev->ucommon->lock, flags);
spin_lock_irqsave(&curlun->lock, flags);
curlun->pending_requests++;
curlun->active_requests--;
wakeup_lun_thread(curlun);
spin_unlock_irqrestore(&curlun->lock, flags);
- spin_lock_irqsave(&udev->ucommon->common->lock, flags);
+ spin_lock_irqsave(&udev->ucommon->lock, flags);
} else {
udev->pending_requests++;
udev->active_requests--;
- wakeup_thread(udev->ucommon->common);
+ uasp_wakeup_thread(udev->ucommon);
}
- spin_unlock_irqrestore(&udev->ucommon->common->lock, flags);
+ spin_unlock_irqrestore(&udev->ucommon->lock, flags);
}
/**
@@ -722,11 +822,11 @@ static void remove_completed_commands(struct uasp_dev *udev,
struct tm_iu *tmiu;
struct tm_iu *tmp_tmiu;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Remove completed, aborted or failed commands from cmd_queue */
list_for_each_entry_safe(cmdiu, tmp_cmdiu, cmd_queue, node) {
- DBG(udev->ucommon->common, "%s() - cmd_queue cycle"
+ DBG(udev->ucommon, "%s() - cmd_queue cycle"
" cmdiu->state=%d "
" cmdiu->req_sts=%d\n",
__func__, cmdiu->state, cmdiu->req_sts);
@@ -757,13 +857,13 @@ static void remove_completed_commands(struct uasp_dev *udev,
if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS)
continue;
}
- DBG(udev->ucommon->common, "%s() - deleted cmdiu: "
+ DBG(udev->ucommon, "%s() - deleted cmdiu: "
"cmdiu[0] = %d, cmdiu->state = %d,"
"cmdiu->tag = %d\n",
__func__, cmdiu->cdb[0], cmdiu->state, cmdiu->tag);
list_del(&cmdiu->node);
if (cmdiu->bh) {
- DBG(udev->ucommon->common, "%s() - Freeing the "
+ DBG(udev->ucommon, "%s() - Freeing the "
"cmdiu->bh\n", __func__);
cmdiu->bh->state = BUF_STATE_EMPTY;
}
@@ -778,19 +878,19 @@ static void remove_completed_commands(struct uasp_dev *udev,
tmiu->state != COMMAND_STATE_FAILED)
continue;
- DBG(udev->ucommon->common, "%s() - deleted tmiu\n", __func__);
+ DBG(udev->ucommon, "%s() - deleted tmiu\n", __func__);
list_del(&tmiu->node);
if (tmiu->bh) {
- DBG(udev->ucommon->common, "%s() - Freeing the "
+ DBG(udev->ucommon, "%s() - Freeing the "
"tmiu->bh\n", __func__);
tmiu->bh->state = BUF_STATE_EMPTY;
}
kfree(tmiu);
}
if (list_empty(cmd_queue) && list_empty(tm_func_queue))
- DBG(udev->ucommon->common, "%s() - both lists are empty\n",
+ DBG(udev->ucommon, "%s() - both lists are empty\n",
__func__);
- DBG(udev->ucommon->common, "%s() - exit\n", __func__);
+ DBG(udev->ucommon, "%s() - exit\n", __func__);
}
/**
@@ -802,40 +902,36 @@ static void remove_completed_commands(struct uasp_dev *udev,
*
* Initiates all endpoints and enables them. Allocates buffers and requests.
*/
-static int do_uasp_set_interface(struct uasp_dev *uaspd,
- struct fsg_dev *new_fsg)
+static int do_uasp_set_interface(struct uasp_common *ucommon,
+ struct uasp_dev *new_uaspd)
{
int rc = 0;
int i;
- struct fsg_dev *fsgd;
- struct fsg_common *fcommon;
unsigned long flags;
+ struct uasp_dev *udev = ucommon->udev;
- if (!uaspd || !uaspd->ucommon || !uaspd->ucommon->common)
+ if (!ucommon)
return -EIO;
- DBG(uaspd->ucommon->common, "%s()- Enter\n", __func__);
+ DBG(ucommon, "%s()- Enter\n", __func__);
- fcommon = uaspd->ucommon->common;
- if (uaspd->ucommon->common->running)
- DBG(uaspd->ucommon->common, "reset inteface\n");
+ if (ucommon->running)
+ DBG(ucommon, "reset inteface\n");
reset_uasp:
/* Deallocate the requests */
- if (uaspd->ucommon->common->fsg) {
- fsgd = fcommon->fsg;
-
- abort_commands(uaspd, &uaspd->cmd_queue, &uaspd->tm_func_queue,
- &(uaspd->ucommon->common->lock));
- remove_completed_commands(uaspd, &uaspd->cmd_queue,
- &uaspd->tm_func_queue);
- uaspd->pending_requests = 0;
-
- for (i = 0; i < uaspd->ucommon->common->nluns; i++) {
- struct uasp_lun *ulun = &uaspd->ucommon->uluns[i];
- abort_commands(uaspd, &ulun->cmd_queue,
+ if (ucommon->udev) {
+ abort_commands(udev, &udev->cmd_queue,
+ &udev->tm_func_queue, &(udev->ucommon->lock));
+ remove_completed_commands(udev, &udev->cmd_queue,
+ &udev->tm_func_queue);
+ udev->pending_requests = 0;
+
+ for (i = 0; i < ucommon->nluns; i++) {
+ struct uasp_lun *ulun = &ucommon->uluns[i];
+ abort_commands(udev, &ulun->cmd_queue,
&ulun->tm_func_queue, &(ulun->lock));
- remove_completed_commands(uaspd, &ulun->cmd_queue,
+ remove_completed_commands(udev, &ulun->cmd_queue,
&ulun->tm_func_queue);
spin_lock_irqsave(&(ulun->lock), flags);
ulun->pending_requests = 0;
@@ -847,130 +943,142 @@ reset_uasp:
spin_unlock_irq(&(ulun->lock));
}
/* Clear out the controller's fifos */
- if (fcommon->fsg->bulk_in_enabled)
- usb_ep_fifo_flush(fcommon->fsg->bulk_in);
- if (fcommon->fsg->bulk_out_enabled)
- usb_ep_fifo_flush(fcommon->fsg->bulk_out);
- usb_ep_fifo_flush(uaspd->ucommon->udev->status);
- usb_ep_fifo_flush(uaspd->ucommon->udev->command);
-
- spin_lock_irq(&fcommon->lock);
+ if (udev->bulk_in_enabled)
+ usb_ep_fifo_flush(udev->bulk_in);
+ if (udev->bulk_out_enabled)
+ usb_ep_fifo_flush(udev->bulk_out);
+ usb_ep_fifo_flush(udev->status);
+ usb_ep_fifo_flush(udev->command);
+
+ spin_lock_irq(&ucommon->lock);
/* Reset the I/O buffer states and pointers */
for (i = 0; i < fsg_num_buffers; ++i) {
- struct fsg_buffhd *bh = &fcommon->buffhds[i];
+ struct fsg_buffhd *bh =
+ ucommon->ubufs[i].fsg_buff;
if (bh->inreq) {
- usb_ep_free_request(fsgd->bulk_in, bh->inreq);
+ usb_ep_free_request(udev->bulk_in, bh->inreq);
bh->inreq = NULL;
}
if (bh->outreq) {
- usb_ep_free_request(fsgd->bulk_out, bh->outreq);
+ usb_ep_free_request(udev->bulk_out, bh->outreq);
bh->outreq = NULL;
}
bh->state = BUF_STATE_EMPTY;
}
-
+ spin_unlock_irq(&ucommon->lock);
/* Deallocate command and status requests */
- if (uaspd->cmd_buff.inreq) {
- ERROR(uaspd->ucommon->common,
+ if (udev->cmd_buff.inreq) {
+ ERROR(ucommon,
"%s(): uaspd->cmd_buff.inreq isn't NULL. "
"How can that be???",
__func__);
- usb_ep_free_request(uaspd->command,
- uaspd->cmd_buff.inreq);
- uaspd->cmd_buff.inreq = NULL;
+ usb_ep_free_request(udev->command,
+ udev->cmd_buff.inreq);
+ udev->cmd_buff.inreq = NULL;
}
- if (uaspd->cmd_buff.outreq) {
- usb_ep_free_request(uaspd->command,
- uaspd->cmd_buff.outreq);
- uaspd->cmd_buff.outreq = NULL;
+ if (udev->cmd_buff.outreq) {
+ (void)usb_ep_dequeue(udev->command,
+ udev->cmd_buff.outreq);
+ usb_ep_free_request(udev->command,
+ udev->cmd_buff.outreq);
+ udev->cmd_buff.outreq = NULL;
}
- uaspd->cmd_buff.state = BUF_STATE_EMPTY;
- spin_unlock_irq(&fcommon->lock);
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
/* Disable the endpoints */
- if (fsgd->bulk_in_enabled) {
- usb_ep_disable(fsgd->bulk_in);
- fsgd->bulk_in_enabled = 0;
+ if (udev->bulk_in_enabled) {
+ usb_ep_disable(udev->bulk_in);
+ udev->bulk_in_enabled = 0;
}
- if (fsgd->bulk_out_enabled) {
- usb_ep_disable(fsgd->bulk_out);
- fsgd->bulk_out_enabled = 0;
+ if (udev->bulk_out_enabled) {
+ usb_ep_disable(udev->bulk_out);
+ udev->bulk_out_enabled = 0;
}
- fsgd->bulk_in->desc = NULL;
- fsgd->bulk_out->desc = NULL;
+ udev->bulk_in->desc = NULL;
+ udev->bulk_out->desc = NULL;
- if (uaspd->cmd_enabled) {
- usb_ep_disable(uaspd->command);
- uaspd->cmd_enabled = 0;
+ if (udev->cmd_enabled) {
+ usb_ep_disable(udev->command);
+ udev->cmd_enabled = 0;
}
- if (uaspd->status_enabled) {
- usb_ep_disable(uaspd->status);
- uaspd->status_enabled = 0;
+ if (udev->status_enabled) {
+ usb_ep_disable(udev->status);
+ udev->status_enabled = 0;
}
- uaspd->command->desc = NULL;
- uaspd->status->desc = NULL;
- DBG(uaspd->ucommon->common, "%s()- disabled endpoints\n",
+ udev->command->desc = NULL;
+ udev->status->desc = NULL;
+ DBG(ucommon,
+ "%s()- disabled endpoints, releasing config mutex\n",
__func__);
-
- fcommon->fsg = NULL;
- wake_up(&fcommon->fsg_wait);
+ mutex_unlock(ucommon->config_mutex);
+ ucommon->udev = NULL;
+ wake_up(&ucommon->uasp_wait);
}
- fcommon->running = 0;
- if (!new_fsg || rc)
+ ucommon->running = 0;
+ if (!new_uaspd || rc) {
+ udev->op_mode = UASP_MODE_UNSET;
return rc;
+ }
+ DBG(ucommon, "%s()- Enabling endpoints, taking config mutex\n",
+ __func__);
+ mutex_lock(ucommon->config_mutex);
+ ucommon->udev = new_uaspd;
+ udev = ucommon->udev;
+ if (!udev->op_mode) {
+ if (udev->forced_hs_mode)
+ udev->op_mode = HS_UASP_MODE;
+ else
+ udev->op_mode =
+ (ucommon->gadget->speed == USB_SPEED_SUPER ?
+ SS_UASP_MODE : HS_UASP_MODE);
+ }
- fcommon->fsg = new_fsg;
- fsgd = fcommon->fsg;
- uaspd->op_mode = (fcommon->gadget->speed == USB_SPEED_SUPER ?
- SS_UASP_MODE : HS_UASP_MODE);
-
- /* Enable the endpoints */
- config_ep_by_speed(fcommon->gadget, &fsgd->function, fsgd->bulk_in);
- rc = usb_ep_enable(fsgd->bulk_in);
+ config_ep_by_speed(ucommon->gadget, &udev->function, udev->bulk_in);
+ rc = usb_ep_enable(udev->bulk_in);
if (rc)
goto reset_uasp;
- fsgd->bulk_in->driver_data = uaspd;
- fsgd->bulk_in_enabled = 1;
+ udev->bulk_in->driver_data = udev;
+ udev->bulk_in_enabled = 1;
- config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
- fsgd->bulk_out);
- rc = usb_ep_enable(fsgd->bulk_out);
+ config_ep_by_speed(ucommon->gadget, &udev->function,
+ udev->bulk_out);
+ rc = usb_ep_enable(udev->bulk_out);
if (rc)
goto reset_uasp;
- fsgd->bulk_out->driver_data = uaspd;
- fsgd->bulk_out_enabled = 1;
+ udev->bulk_out->driver_data = udev;
+ udev->bulk_out_enabled = 1;
- fsgd->common->bulk_out_maxpacket =
- le16_to_cpu(fsgd->bulk_out->maxpacket);
- clear_bit(IGNORE_BULK_OUT, &fsgd->atomic_bitflags);
+ /*ucommon->bulk_out_maxpacket =
+ le16_to_cpu(udev->bulk_out->maxpacket);
+ clear_bit(IGNORE_BULK_OUT, &fsgd->atomic_bitflags);*/
- config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
- uaspd->command);
- rc = usb_ep_enable(uaspd->command);
+ config_ep_by_speed(ucommon->gadget, &udev->function,
+ udev->command);
+ rc = usb_ep_enable(udev->command);
if (rc)
goto reset_uasp;
- uaspd->command->driver_data = uaspd;
- uaspd->cmd_enabled = 1;
+ udev->command->driver_data = udev;
+ udev->cmd_enabled = 1;
- config_ep_by_speed(fsgd->common->gadget, &fsgd->function,
- uaspd->status);
- rc = usb_ep_enable(uaspd->status);
+ config_ep_by_speed(ucommon->gadget, &udev->function,
+ udev->status);
+ rc = usb_ep_enable(udev->status);
if (rc)
goto reset_uasp;
- uaspd->status->driver_data = uaspd;
- uaspd->status_enabled = 1;
+ udev->status->driver_data = udev;
+ udev->status_enabled = 1;
/* Allocate the data - requests */
for (i = 0; i < fsg_num_buffers; ++i) {
- struct uasp_buff *buff = &uaspd->ucommon->ubufs[i];
+ struct uasp_buff *buff = &ucommon->ubufs[i];
- buff->fsg_buff->inreq = usb_ep_alloc_request(fsgd->bulk_in,
+ buff->fsg_buff->inreq = usb_ep_alloc_request(udev->bulk_in,
GFP_ATOMIC);
if (!buff->fsg_buff->inreq)
goto reset_uasp;
- buff->fsg_buff->outreq = usb_ep_alloc_request(fsgd->bulk_out,
+ buff->fsg_buff->outreq = usb_ep_alloc_request(udev->bulk_out,
GFP_ATOMIC);
if (!buff->fsg_buff->outreq)
goto reset_uasp;
@@ -984,24 +1092,24 @@ reset_uasp:
}
/* Allocate command ep request */
- uaspd->cmd_buff.outreq = usb_ep_alloc_request(uaspd->command,
+ udev->cmd_buff.outreq = usb_ep_alloc_request(udev->command,
GFP_ATOMIC);
- if (!uaspd->cmd_buff.outreq) {
- ERROR(uaspd->ucommon->common, "failed allocating outreq for "
+ if (!udev->cmd_buff.outreq) {
+ ERROR(ucommon, "failed allocating outreq for "
"command buffer\n");
goto reset_uasp;
}
- DBG(uaspd->ucommon->common, "%s() Enebled endpoints. "
+ DBG(ucommon, "%s() Enebled endpoints. "
"Opperation mode = %d\n", __func__,
- uaspd->op_mode);
- uaspd->cmd_buff.outreq->buf = &(uaspd->cmd_buff.buf);
- uaspd->cmd_buff.inreq = NULL;
- uaspd->cmd_buff.state = BUF_STATE_EMPTY;
-
- fcommon->running = 1;
- for (i = 0; i < fsgd->common->nluns; ++i)
- fsgd->common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ udev->op_mode);
+ udev->cmd_buff.outreq->buf = &(udev->cmd_buff.buf);
+ udev->cmd_buff.inreq = NULL;
+ udev->cmd_buff.state = BUF_STATE_EMPTY;
+
+ ucommon->running = 1;
+ for (i = 0; i < ucommon->nluns; ++i)
+ ucommon->uluns[i].lun->unit_attention_data = SS_RESET_OCCURRED;
return 0;
}
@@ -1011,12 +1119,10 @@ static void handle_uasp_exception(struct uasp_common *ucommon)
int sig;
int i;
struct fsg_buffhd *bh;
- enum fsg_state old_state;
+ enum uasp_state old_state;
int rc;
- struct fsg_common *fcommon = ucommon->common;
-
- DBG(ucommon->common, "%s()- Enter\n", __func__);
+ DBG(ucommon, "%s()- Enter\n", __func__);
/*
* Clear the existing signals. Anything but SIGUSR1 is converted
@@ -1027,9 +1133,9 @@ static void handle_uasp_exception(struct uasp_common *ucommon)
if (!sig)
break;
if (sig != SIGUSR1) {
- if (fcommon->state < FSG_STATE_EXIT)
- DBG(fcommon, "Main thread exiting on signal\n");
- fcommon->state = FSG_STATE_EXIT;
+ if (ucommon->state < UASP_STATE_EXIT)
+ DBG(ucommon, "Main thread exiting on signal\n");
+ ucommon->state = UASP_STATE_EXIT;
}
}
@@ -1037,51 +1143,42 @@ static void handle_uasp_exception(struct uasp_common *ucommon)
* Reset the I/O buffer states and pointers, the SCSI state, and the
* exception. Then invoke the handler.
*/
- spin_lock_irq(&fcommon->lock);
+ spin_lock_irq(&ucommon->lock);
for (i = 0; i < fsg_num_buffers; ++i) {
- bh = &fcommon->buffhds[i];
+ bh = ucommon->ubufs[i].fsg_buff;
bh->state = BUF_STATE_EMPTY;
}
- old_state = fcommon->state;
- fcommon->state = FSG_STATE_IDLE;
- spin_unlock_irq(&fcommon->lock);
+ old_state = ucommon->state;
+ ucommon->state = UASP_STATE_IDLE;
+ spin_unlock_irq(&ucommon->lock);
/* Carry out any extra actions required for the exception */
switch (old_state) {
- case FSG_STATE_ABORT_BULK_OUT:
- case FSG_STATE_RESET:
- /* TODO */
- break;
-
- case FSG_STATE_CONFIG_CHANGE:
- if (fcommon->fsg == fcommon->new_fsg) {
- DBG(fcommon, "nothing to do. same config\n");
- break;
- }
+ case UASP_STATE_CONFIG_CHANGE:
/* Enable/disable the interface according to the new_config */
- rc = do_uasp_set_interface(ucommon->udev, fcommon->new_fsg);
+ rc = do_uasp_set_interface(ucommon, ucommon->new_udev);
if (rc != 0)
- fcommon->fsg = NULL; /* Reset on errors */
+ ucommon->udev = NULL; /* Reset on errors */
+ if (ucommon->new_udev)
+ usb_composite_setup_continue(ucommon->cdev);
break;
- case FSG_STATE_EXIT:
- case FSG_STATE_TERMINATED:
+ case UASP_STATE_EXIT:
+ case UASP_STATE_TERMINATED:
/* Free resources */
- (void)do_uasp_set_interface(ucommon->udev, NULL);
- spin_lock_irq(&fcommon->lock);
- fcommon->state = FSG_STATE_TERMINATED; /* Stop the thread*/
- spin_unlock_irq(&fcommon->lock);
+ (void)do_uasp_set_interface(ucommon, NULL);
+ spin_lock_irq(&ucommon->lock);
+ ucommon->state = UASP_STATE_TERMINATED; /* Stop the thread*/
+ spin_unlock_irq(&ucommon->lock);
break;
- case FSG_STATE_INTERFACE_CHANGE:
- case FSG_STATE_DISCONNECT:
- case FSG_STATE_COMMAND_PHASE:
- case FSG_STATE_DATA_PHASE:
- case FSG_STATE_STATUS_PHASE:
- case FSG_STATE_IDLE:
+ case UASP_STATE_INTERFACE_CHANGE:
+ case UASP_STATE_DISCONNECT:
+ case UASP_STATE_RESET:
+ case UASP_STATE_IDLE:
break;
}
- DBG(ucommon->common, "%s()- Exit\n", __func__);
+ DBG(ucommon, "%s()- Exit\n", __func__);
}
/**
@@ -1121,7 +1218,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
req = udev->cmd_buff.outreq;
*command = NULL;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Id of the received command (tmiu or cmdiu) */
cmd_id = ((uint8_t *)req->buf)[0];
@@ -1131,7 +1228,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Invalid completion status */
if (req->status) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Invalid completion status for command "
"request = -%d\n", __func__, req->status);
return -EINVAL;
@@ -1139,7 +1236,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Check is the data received via command endpoint is a command */
if (cmd_id != IU_ID_TASK_MANAGEMENT && cmd_id != IU_ID_COMMAND) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Invalid data is received\n", __func__);
/* TODO: something needs to be done (e.g. halt endpoints) */
return -EINVAL;
@@ -1147,7 +1244,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Invalid count of bytes received for tmiu */
if (cmd_id == IU_ID_TASK_MANAGEMENT && req->actual != 16) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Invalid byte count for tmiu is received = %d\n",
__func__, req->actual);
/* TODO: something needs to be done (e.g. halt endpoints) */
@@ -1156,7 +1253,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Invalid count of bytes received for cmdiu */
if (cmd_id == IU_ID_COMMAND && req->actual < 32) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Invalid byte count for cmdiu is received = %d\n",
__func__, req->actual);
/* TODO: something needs to be done (e.g. halt endpoints) */
@@ -1171,7 +1268,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
tmiu = kmalloc(sizeof(struct tm_iu), GFP_KERNEL);
if (!tmiu) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - No memory for tmiu\n", __func__);
return -ENOMEM;
}
@@ -1181,7 +1278,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
cmdiu = kmalloc(sizeof(struct cmd_iu), GFP_KERNEL);
if (!cmdiu) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - No memory for cmdiu\n", __func__);
return -ENOMEM;
}
@@ -1191,7 +1288,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Check for overlapping tag */
/* Check for tag overlapping over all cmd an tm_func queues */
- for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ for (i = 0; i < udev->ucommon->nluns; ++i) {
curlun = &udev->ucommon->uluns[i];
spin_lock_irqsave(&(curlun->lock), flags);
@@ -1221,7 +1318,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
spin_unlock_irqrestore(&(curlun->lock), flags);
}
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
list_for_each_entry(tmp_cmdiu, &udev->cmd_queue, node) {
if (tmp_cmdiu->state != COMMAND_STATE_IDLE &&
tmp_cmdiu->state != COMMAND_STATE_DATA &&
@@ -1231,7 +1328,7 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Overlapped tag found */
if (tmp_cmdiu->tag == tag) {
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ spin_unlock_irqrestore(&(udev->ucommon->lock),
flags);
goto overlapped_tag;
}
@@ -1244,12 +1341,12 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
/* Overlapped tag found */
if (tmp_tmiu->tag == tag) {
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ spin_unlock_irqrestore(&(udev->ucommon->lock),
flags);
goto overlapped_tag;
}
}
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
/* No overlapped tag */
if (cmd_id == IU_ID_TASK_MANAGEMENT)
@@ -1258,42 +1355,43 @@ static int uasp_command_check(struct uasp_dev *udev, void **command)
return 1;
overlapped_tag:
- ERROR(udev->ucommon->common, "%s() - Overlapped tag found. "
+ ERROR(udev->ucommon, "%s() - Overlapped tag found. "
"Aborting all\n", __func__);
run_lun_threads(udev, LUN_STATE_OVERLAPPED_TAG);
/* Wait for luns abort completion. Sleep if luns are in processing */
while (!all_lun_state_non_processing(udev)) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - Luns are in process. Going to sleep\n", __func__);
- rc = sleep_thread(udev->ucommon->common);
+ rc = uasp_sleep_thread(udev->ucommon);
if (rc) {
- ERROR(udev->ucommon->common,
- "%s() - sleep_thread failed! (%d)", __func__, rc);
+ ERROR(udev->ucommon,
+ "%s() - uasp_sleep_thread failed! (%d)",
+ __func__, rc);
return -EINVAL;
}
- DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+ DBG(udev->ucommon, "%s() - Wakes up\n", __func__);
rc = 0;
}
/* Abort none-lun commands */
abort_commands(udev, &udev->cmd_queue, &udev->tm_func_queue,
- &(udev->ucommon->common->lock));
+ &(udev->ucommon->lock));
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
udev->pending_requests = 0;
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
if (cmd_id == IU_ID_TASK_MANAGEMENT) {
tmiu = *command;
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
- tmiu->bh = get_buffhd(udev->ucommon->common->buffhds);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
+ tmiu->bh = get_buffhd(udev->ucommon->ubufs);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
if (!tmiu->bh) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s(): didnt manage to get buffers for tmiu!\n",
__func__);
return -EINVAL;
@@ -1317,12 +1415,12 @@ overlapped_tag:
} else {
cmdiu = *command;
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
- cmdiu->bh = get_buffhd(udev->ucommon->common->buffhds);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
+ cmdiu->bh = get_buffhd(udev->ucommon->ubufs);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
if (!cmdiu->bh) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s(): didnt manage to get buffers for cmdiu!\n",
__func__);
return -EINVAL;
@@ -1364,7 +1462,7 @@ static void insert_tm_func_to_list(struct uasp_dev *udev, struct tm_iu *tmiu)
struct tm_iu *tmiu1;
struct uasp_lun *curlun;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
curlun = find_lun_by_id(udev, tmiu->lun);
tmiu->state = COMMAND_STATE_IDLE;
@@ -1449,10 +1547,10 @@ static void insert_cmd_to_list(struct uasp_dev *udev, struct cmd_iu *cmdiu)
struct cmd_iu *cmdiu1;
struct uasp_lun *curlun;
- DBG(udev->ucommon->common, "%s(): cmdiu->lun = %p\n", __func__,
+ DBG(udev->ucommon, "%s(): cmdiu->lun = %p\n", __func__,
cmdiu->lun);
- DBG(udev->ucommon->common, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ DBG(udev->ucommon, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
cmdiu->lun[0], cmdiu->lun[1], cmdiu->lun[2], cmdiu->lun[3],
cmdiu->lun[4], cmdiu->lun[5], cmdiu->lun[6], cmdiu->lun[7]);
@@ -1489,7 +1587,7 @@ static void insert_cmd_to_list(struct uasp_dev *udev, struct cmd_iu *cmdiu)
/* In the case when the queue is empty */
list_add_tail(&cmdiu->node, link);
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - Cmdiu is added to the tail of the queue\n", __func__);
}
@@ -1510,7 +1608,7 @@ static int get_command(struct uasp_dev *udev)
int rc = 0;
void *command = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
queue_cmd_ep:
/* If command endpoint is not active, activate */
@@ -1525,11 +1623,11 @@ queue_cmd_ep:
udev->cmd_buff.outreq->short_not_ok = 1;
rc = usb_ep_queue(udev->command, udev->cmd_buff.outreq, 0);
if (rc) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s()usb_ep_queue failed = %d\n", __func__, rc);
udev->cmd_buff.state = BUF_STATE_EMPTY;
}
- DBG(udev->ucommon->common, "%s() queued command request = %p\n",
+ DBG(udev->ucommon, "%s() queued command request = %p\n",
__func__, udev->cmd_buff.outreq);
return rc;
}
@@ -1540,13 +1638,13 @@ queue_cmd_ep:
rc = uasp_command_check(udev, &command);
if (rc == 0) {
- DBG(udev->ucommon->common, "%s() - Received a TMC IU\n",
+ DBG(udev->ucommon, "%s() - Received a TMC IU\n",
__func__);
insert_tm_func_to_list(udev, (struct tm_iu *)command);
udev->cmd_buff.state = BUF_STATE_EMPTY;
goto queue_cmd_ep;
} else if (rc == 1) {
- DBG(udev->ucommon->common, "%s() -Received a CMD IU\n",
+ DBG(udev->ucommon, "%s() -Received a CMD IU\n",
__func__);
insert_cmd_to_list(udev, (struct cmd_iu *)command);
udev->cmd_buff.state = BUF_STATE_EMPTY;
@@ -1567,9 +1665,9 @@ int all_lun_state_non_processing(struct uasp_dev *udev)
struct uasp_lun *curlun;
int i;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
- for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ for (i = 0; i < udev->ucommon->nluns; ++i) {
curlun = &udev->ucommon->uluns[i];
if (curlun->lun_state > LUN_STATE_IDLE)
return 0;
@@ -1590,8 +1688,8 @@ static int pending_cmd_in_lun(void *data)
struct uasp_lun *curlun;
int i;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
- for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
+ for (i = 0; i < udev->ucommon->nluns; ++i) {
curlun = &udev->ucommon->uluns[i];
if (curlun->pending_requests)
return 1;
@@ -1633,17 +1731,17 @@ void run_lun_threads(struct uasp_dev *udev, int state)
int i;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter. State = %d\n",
+ DBG(udev->ucommon, "%s() - Enter. State = %d\n",
__func__, state);
- for (i = 0; i < udev->ucommon->common->nluns; ++i) {
+ for (i = 0; i < udev->ucommon->nluns; ++i) {
curlun = &udev->ucommon->uluns[i];
spin_lock_irqsave(&(curlun->lock), flags);
curlun->lun_state = state;
wakeup_lun_thread(curlun);
spin_unlock_irqrestore(&(curlun->lock), flags);
}
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
}
/**
@@ -1667,7 +1765,7 @@ void abort_commands(struct uasp_dev *udev,
struct cmd_iu *cmdiu, *tmp_cmdiu;
struct tm_iu *tmiu, *tmp_tmiu;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (!cmd_queue)
goto tmiu_part;
@@ -1732,58 +1830,47 @@ tmiu_part:
void do_uasp(struct uasp_dev *udev)
{
unsigned long flags;
- struct fsg_dev *fsg = &(udev->fsg_dev);
struct uasp_common *ucommon = udev->ucommon;
int rc;
- DBG(ucommon->common, "%s() - Enter\n", __func__);
-
- spin_lock_irqsave(&(fsg->common->lock), flags);
- if (!exception_in_progress(fsg->common))
- fsg->common->state = FSG_STATE_COMMAND_PHASE;
- spin_unlock_irqrestore(&(fsg->common->lock), flags);
+ DBG(ucommon, "%s() - Enter\n", __func__);
- if (exception_in_progress(fsg->common))
+ if (uasp_exception_in_progress(ucommon))
return;
if (get_command(udev))
return;
- spin_lock_irqsave(&(fsg->common->lock), flags);
- if (!exception_in_progress(fsg->common))
- fsg->common->state = FSG_STATE_DATA_PHASE;
- spin_unlock_irqrestore(&(fsg->common->lock), flags);
-
- if (exception_in_progress(fsg->common))
+ if (uasp_exception_in_progress(ucommon))
return;
- spin_lock_irqsave(&(ucommon->common->lock), flags);
+ spin_lock_irqsave(&(ucommon->lock), flags);
udev->pending_requests = 0;
- spin_unlock_irqrestore(&(ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(ucommon->lock), flags);
do_tmiu(udev, NULL);
- if (exception_in_progress(fsg->common))
+ if (uasp_exception_in_progress(ucommon))
return;
do_cmdiu(udev, NULL);
- if (exception_in_progress(fsg->common))
+ if (uasp_exception_in_progress(ucommon))
return;
remove_completed_commands(udev, &udev->cmd_queue, &udev->tm_func_queue);
- spin_lock_irqsave(&(fsg->common->lock), flags);
- if (!exception_in_progress(fsg->common)) {
- fsg->common->state = FSG_STATE_IDLE;
- spin_unlock_irqrestore(&(fsg->common->lock), flags);
+ spin_lock_irqsave(&(ucommon->lock), flags);
+ if (!uasp_exception_in_progress(ucommon)) {
+ ucommon->state = UASP_STATE_IDLE;
+ spin_unlock_irqrestore(&(ucommon->lock), flags);
run_lun_threads(udev, LUN_STATE_PROCESSING);
} else
- spin_unlock_irqrestore(&(fsg->common->lock), flags);
+ spin_unlock_irqrestore(&(ucommon->lock), flags);
rc = 0;
while (!rc) {
/* If exception is in progress */
- if (exception_in_progress(ucommon->common)) {
- DBG(ucommon->common,
+ if (uasp_exception_in_progress(ucommon)) {
+ DBG(ucommon,
"%s() - Exception is in progress\n", __func__);
return;
}
@@ -1791,39 +1878,39 @@ void do_uasp(struct uasp_dev *udev)
/* Sleep if luns are in processing */
rc = all_lun_state_non_processing(udev);
if (!rc) {
- DBG(ucommon->common,
+ DBG(ucommon,
"%s() - Luns are in process\n", __func__);
goto sleep;
}
/* Wake up if command is received */
if (udev->cmd_buff.state == BUF_STATE_FULL) {
- DBG(ucommon->common,
+ DBG(ucommon,
"%s() - Command is received\n", __func__);
return;
}
/* Wake up if there are pending requests in luns */
if (pending_cmd_in_lun(udev)) {
- DBG(ucommon->common,
+ DBG(ucommon,
"%s() - Pending requests in LUN\n", __func__);
return;
}
/* Wake up if there are pending requests */
if (udev->pending_requests) {
- DBG(ucommon->common,
+ DBG(ucommon,
"%s() - Pending requests in device\n",
__func__);
return;
}
sleep:
/* Try to sleep */
- DBG(ucommon->common, "%s() - Going to sleep\n", __func__);
- rc = sleep_thread(fsg->common);
+ DBG(ucommon, "%s() - Going to sleep\n", __func__);
+ rc = uasp_sleep_thread(ucommon);
if (rc)
return;
- DBG(ucommon->common, "%s() - Wakes up\n", __func__);
+ DBG(ucommon, "%s() - Wakes up\n", __func__);
rc = 0;
}
@@ -1867,7 +1954,7 @@ static int lun_exception_in_progress(struct uasp_lun *curlun)
static void handle_lun_exception(struct uasp_dev *udev, struct uasp_lun *curlun)
{
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Abort all commands and remove them from lists */
abort_commands(udev, &curlun->cmd_queue, &curlun->tm_func_queue,
@@ -1890,7 +1977,7 @@ static void handle_lun_exception(struct uasp_dev *udev, struct uasp_lun *curlun)
break;
}
spin_unlock_irqrestore(&(curlun->lock), flags);
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
}
/**
@@ -1915,15 +2002,15 @@ static int uasp_lun_thread(void *param)
if (!ulun)
return -1;
udev = ulun->dev;
- DBG(udev->ucommon->common, "%s() - Enter for lun_id = %d\n", __func__,
+ DBG(udev->ucommon, "%s() - Enter for lun_id = %d\n", __func__,
ulun->lun_id[7]);
while (ulun->lun_state != LUN_STATE_TERMINATED) {
- DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+ DBG(udev->ucommon, "%s() - Wakes up\n", __func__);
if (lun_exception_in_progress(ulun)) {
- DBG(udev->ucommon->common,
- "%s() - exception_in_progress!"
+ DBG(udev->ucommon,
+ "%s() - uasp_exception_in_progress!"
" ulun->lun_state=%d\n", __func__,
ulun->lun_state);
handle_lun_exception(udev, ulun);
@@ -1934,8 +2021,8 @@ static int uasp_lun_thread(void *param)
* If the main thread isn't running, no need to run lun threads
* as well.
*/
- if (!udev->ucommon->common->running) {
- DBG(udev->ucommon->common,
+ if (!udev->ucommon->running) {
+ DBG(udev->ucommon,
"%s() - uasp thread main thread not running - "
"going to sleep...\n", __func__);
sleep_lun_thread(ulun);
@@ -1959,22 +2046,22 @@ static int uasp_lun_thread(void *param)
if (lun_exception_in_progress(ulun))
continue;
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
if (!lun_exception_in_progress(ulun)) {
ulun->lun_state = LUN_STATE_IDLE;
- wakeup_thread(udev->ucommon->common);
+ uasp_wakeup_thread(udev->ucommon);
}
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
- DBG(udev->ucommon->common, "%s() - Going to sleep\n", __func__);
+ DBG(udev->ucommon, "%s() - Going to sleep\n", __func__);
sleep_lun_thread(ulun);
continue;
}
- DBG(udev->ucommon->common, "uasp lun main loop: exiting\n");
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ DBG(udev->ucommon, "uasp lun main loop: exiting\n");
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
ulun->lun_thread_task = NULL;
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
/* Let the unbind and cleanup routines know the thread has exited */
complete_and_exit(&ulun->thread_notifier, 0);
return 0;
@@ -1993,8 +2080,6 @@ static int uasp_lun_thread(void *param)
static int uasp_main_thread(void *param)
{
struct uasp_common *ucommon = (struct uasp_common *)param;
- struct fsg_common *common = ucommon->common;
-
/*
* Allow the thread to be killed by a signal, but set the signal mask
* to block everything but INT, TERM, KILL, and USR1.
@@ -2015,41 +2100,41 @@ static int uasp_main_thread(void *param)
set_fs(get_ds());
/* The main loop */
- while (common->state != FSG_STATE_TERMINATED) {
- DBG(common, "uasp main loop: continuing\n");
- if (exception_in_progress(ucommon->common) ||
+ while (ucommon->state != UASP_STATE_TERMINATED) {
+ DBG(ucommon, "uasp main loop: continuing\n");
+ if (uasp_exception_in_progress(ucommon) ||
signal_pending(current)) {
- DBG(common, "uasp thread main loop: exception\n");
+ DBG(ucommon, "uasp thread main loop: exception\n");
handle_uasp_exception(ucommon);
continue;
}
- if (!common->running) {
- DBG(common, "uasp thread main loop: not running\n");
- sleep_thread(ucommon->common);
+ if (!ucommon->running) {
+ DBG(ucommon, "uasp thread main loop: not running\n");
+ uasp_sleep_thread(ucommon);
continue;
}
do_uasp(ucommon->udev);
}
- DBG(common, "uasp main loop: exiting\n");
+ DBG(ucommon, "uasp main loop: exiting\n");
- spin_lock_irq(&common->lock);
- common->thread_task = NULL;
- spin_unlock_irq(&common->lock);
+ spin_lock_irq(&ucommon->lock);
+ ucommon->thread_task = NULL;
+ spin_unlock_irq(&ucommon->lock);
- if (!common->ops || !common->ops->thread_exits ||
- common->ops->thread_exits(common) < 0) {
+ if (!ucommon->ops || !ucommon->ops->thread_exits ||
+ ucommon->ops->thread_exits(ucommon) < 0) {
struct uasp_lun *ulun = ucommon->uluns;
unsigned i ;
- down_write(&common->filesem);
- for (i = 0; i < common->nluns; i++, ulun++)
+ down_write(&ucommon->filesem);
+ for (i = 0; i < ucommon->nluns; i++, ulun++)
close_lun(ulun);
- up_write(&common->filesem);
+ up_write(&ucommon->filesem);
}
/* Let the unbind and cleanup routines know the thread has exited */
- complete_and_exit(&common->thread_notifier, 0);
+ complete_and_exit(&ucommon->thread_notifier, 0);
return 0;
}
@@ -2063,74 +2148,107 @@ static int uasp_main_thread(void *param)
* This function should be called after (struct fsg_common) common was already
* initiated by fsg_common_init
*/
-static struct uasp_common *uasp_common_init(struct fsg_common *common,
- struct usb_composite_dev *cdev,
- struct fsg_config *cfg)
+static struct uasp_common *uasp_common_init(struct msg_common_data *common,
+ struct usb_composite_dev *cdev)
{
struct fsg_lun *flun;
struct uasp_lun *ulun;
struct uasp_common *ucommon;
- int nluns = common->nluns;
- int i, rc;
+ int i, rc = 0;
- if (!common || !cdev || !cfg)
+ if (!common || !cdev)
return NULL;
- DBG(common, "%s() - Enter\n", __func__);
+ DBG(cdev, "%s() - Enter\n", __func__);
ucommon = kzalloc(sizeof *ucommon, GFP_KERNEL);
if (unlikely(!ucommon))
return NULL;
- /* Save reference to fsg_common structure in ucommon */
- ucommon->common = common;
-
- /* Allocate the uLUNs and init them according to fsg_common luns */
- ulun = kzalloc(nluns * sizeof *ulun, GFP_KERNEL);
+ ucommon->msg_common = common;
+ /*Allocate the uLUNs and init them according to fsg_common luns */
+ ulun = kzalloc(common->config.nluns * sizeof *ulun, GFP_KERNEL);
if (!ulun) {
kfree(ucommon);
return ERR_PTR(-ENOMEM);
}
ucommon->uluns = ulun;
+ ucommon->nluns = common->config.nluns;
/* Create the reference between ulun and fsg_lun */
- for (i = 0, flun = common->luns; i < nluns;
+ for (i = 0, flun = common->fcommon->luns; i < common->config.nluns;
++i, ++flun, ++ulun)
ulun->lun = flun;
+ ucommon->ops = common->config.uasp_ops;
+ ucommon->gadget = cdev->gadget;
+ ucommon->cdev = cdev;
+ ucommon->config_mutex = &(common->config.config_mutex);
+
/*
- * Buffers in ubufs are static -- no need for additional allocation.
- * Connect each ubuf to fsg_buff from the buffhds cyclic list
+ * Allocate the buffers in ubufs and connect each ubuf to fsg_buff
+ * from the buffhds cyclic list
*/
+ ucommon->ubufs = kzalloc(sizeof(struct uasp_buff)*fsg_num_buffers,
+ GFP_KERNEL);
+ if (!ucommon->ubufs)
+ goto error_release;
+
for (i = 0; i < fsg_num_buffers; i++) {
- ucommon->ubufs[i].fsg_buff = &(common->buffhds[i]);
+ ucommon->ubufs[i].fsg_buff = &(common->fcommon->buffhds[i]);
ucommon->ubufs[i].ep = NULL;
ucommon->ubufs[i].stream_id = 0;
}
- kref_init(&ucommon->ref);
+ /* Prepare inquiryString */
+ if (common->config.release != 0xffff) {
+ i = common->config.release;
+ } else {
+ i = usb_gadget_controller_number(cdev->gadget);
+ if (i >= 0) {
+ i = 0x0300 + i;
+ } else {
+ WARNING(ucommon, "controller '%s' not recognized\n",
+ cdev->gadget->name);
+ i = 0x0399;
+ }
+ }
+ snprintf(ucommon->inquiry_string, sizeof ucommon->inquiry_string,
+ "%-8s%-16s%04x", common->config.vendor_name ?: "Linux",
+ /* Assume product name dependent on the first LUN */
+ common->config.product_name ?: (common->fcommon->luns->cdrom
+ ? "File-Stor UAS-Gadget"
+ : "File-CD UAS-Gadget"),
+ i);
+
/* Tell the thread to start working */
- common->thread_task =
+ ucommon->thread_task =
kthread_create(uasp_main_thread, (void *)ucommon,
- cfg->thread_name ?: "file-storage-UASP");
- if (IS_ERR(common->thread_task)) {
- rc = PTR_ERR(common->thread_task);
+ common->config.thread_name ?:
+ "file-storage-UASP");
+ if (IS_ERR(ucommon->thread_task)) {
+ rc = PTR_ERR(ucommon->thread_task);
goto error_release;
}
/* Information */
- INFO(common, UASP_DRIVER_DESC ", version: " UASP_DRIVER_VERSION "\n");
- DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+ INFO(ucommon, UASP_DRIVER_DESC ", version: " UASP_DRIVER_VERSION "\n");
+ DBG(ucommon, "I/O thread pid: %d created for UASP Gdaget\n",
+ task_pid_nr(ucommon->thread_task));
+
+ kref_init(&ucommon->ref);
+ init_waitqueue_head(&ucommon->uasp_wait);
+ init_completion(&ucommon->thread_notifier);
- wake_up_process(common->thread_task);
+ wake_up_process(ucommon->thread_task);
return ucommon;
error_release:
- common->state = FSG_STATE_TERMINATED; /* The thread is dead */
+ ucommon->state = UASP_STATE_TERMINATED; /* The thread is dead */
/* Call uasp_common_release() directly, ref might be not initialised */
- uasp_common_release(&common->ref);
+ uasp_common_release(&ucommon->ref);
return ERR_PTR(rc);
}
@@ -2151,7 +2269,7 @@ static int finish_lun_init(struct uasp_dev *udev)
return -EIO;
for (i = 0, ulun = udev->ucommon->uluns;
- i < udev->ucommon->common->nluns; i++, ulun++) {
+ i < udev->ucommon->nluns; i++, ulun++) {
/* TODO: this is a workaround, fix later */
memset(ulun->lun_id, 0, 8);
ulun->lun_id[0] = i;
@@ -2163,8 +2281,8 @@ static int finish_lun_init(struct uasp_dev *udev)
/* Create and start lun threads */
sprintf(thread_name, "uasp-lun-thread%d", i);
- DBG(udev->ucommon->common,
- "creating & starting lun thread: thread_name = %s\n",
+ DBG(udev->ucommon,
+ "creating lun thread: thread_name = %s\n",
thread_name);
ulun->lun_thread_task = kthread_create(uasp_lun_thread,
@@ -2177,7 +2295,6 @@ static int finish_lun_init(struct uasp_dev *udev)
init_completion(&ulun->thread_notifier);
wake_up_process(ulun->lun_thread_task);
}
- INFO(udev->ucommon->common, "All lun threads are started\n");
return rc;
err_lun_init:
@@ -2193,71 +2310,110 @@ err_lun_init:
*
* Return 0 on succeed, error code on failure
*
- * TODO: Add fall back to usb_ep_autoconfig() if usb_ep_autoconfig_ss() fails.
- * In that case mark somehow that we can only operate in HS mode
*/
static int __init uasp_bind(struct usb_configuration *c, struct usb_function *f)
{
struct uasp_dev *uaspd = uaspd_from_func(f);
- struct fsg_dev *fsgd = &(uaspd->fsg_dev);
struct usb_gadget *gadget = c->cdev->gadget;
int rc;
int i;
+ int hs_mode = 0;
struct usb_ep *ep;
- fsgd->common->gadget = gadget;
+ uaspd->ucommon->gadget = gadget;
/* Allocate new interface */
i = usb_interface_id(c, f);
if (i < 0)
return i;
uasp_intf_desc.bInterfaceNumber = i;
- fsgd->interface_number = i;
- /* Find all the endpoints we will use */
- ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_in_desc,
- &uasp_bulk_in_ep_comp_desc);
- if (!ep)
- goto autoconf_fail;
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_in_desc,
+ &uasp_bulk_in_ep_comp_desc);
+ if (!ep) {
+ ERROR(uaspd->ucommon,
+ "%s(): Unable to autoconfigure endpoints"
+ " in SS mode. Falling back to HS mode...\n",
+ __func__);
+ goto fall_back_to_hs;
+ }
+ } else {
+fall_back_to_hs:
+ hs_mode = 1;
+ uaspd->op_mode = HS_UASP_MODE;
+ uaspd->forced_hs_mode = 1;
+ ep = usb_ep_autoconfig(gadget, &uasp_fs_bulk_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+ }
ep->driver_data = uaspd; /* claim the endpoint */
- fsgd->bulk_in = ep;
+ uaspd->bulk_in = ep;
- ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_out_desc,
+ if (hs_mode)
+ ep = usb_ep_autoconfig(gadget, &uasp_fs_bulk_out_desc);
+ else
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_bulk_out_desc,
&uasp_bulk_out_ep_comp_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = uaspd; /* claim the endpoint */
- fsgd->bulk_out = ep;
+ uaspd->bulk_out = ep;
- ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_in_desc,
+ if (hs_mode)
+ ep = usb_ep_autoconfig(gadget, &uasp_fs_status_in_desc);
+ else
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_status_in_desc,
&uasp_status_in_ep_comp_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = uaspd; /* claim the endpoint */
uaspd->status = ep;
- ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_command_out_desc,
+ if (hs_mode)
+ ep = usb_ep_autoconfig(gadget, &uasp_fs_command_out_desc);
+ else
+ ep = usb_ep_autoconfig_ss(gadget, &uasp_ss_command_out_desc,
&uasp_command_out_ep_comp_desc);
if (!ep)
goto autoconf_fail;
ep->driver_data = uaspd; /* claim the endpoint */
uaspd->command = ep;
- /* Assume endpoint addresses are the same for both speeds */
- uasp_bulk_in_desc.bEndpointAddress =
- uasp_ss_bulk_in_desc.bEndpointAddress;
- uasp_bulk_out_desc.bEndpointAddress =
- uasp_ss_bulk_out_desc.bEndpointAddress;
- uasp_status_in_desc.bEndpointAddress =
- uasp_ss_status_in_desc.bEndpointAddress;
- uasp_command_out_desc.bEndpointAddress =
- uasp_ss_command_out_desc.bEndpointAddress;
- f->ss_descriptors = uasp_ss_function_desc;
+ if (hs_mode) {
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ uasp_hs_bulk_in_desc.bEndpointAddress =
+ uasp_fs_bulk_in_desc.bEndpointAddress;
+ uasp_hs_bulk_out_desc.bEndpointAddress =
+ uasp_fs_bulk_out_desc.bEndpointAddress;
+ uasp_hs_status_in_desc.bEndpointAddress =
+ uasp_fs_status_in_desc.bEndpointAddress;
+ uasp_hs_command_out_desc.bEndpointAddress =
+ uasp_fs_command_out_desc.bEndpointAddress;
+ }
+ } else { /* SuperSpeed configuration sucseeded */
+ uasp_fs_bulk_in_desc.bEndpointAddress =
+ uasp_ss_bulk_in_desc.bEndpointAddress;
+ uasp_hs_bulk_in_desc.bEndpointAddress =
+ uasp_ss_bulk_in_desc.bEndpointAddress;
+ uasp_fs_bulk_out_desc.bEndpointAddress =
+ uasp_ss_bulk_out_desc.bEndpointAddress;
+ uasp_hs_bulk_out_desc.bEndpointAddress =
+ uasp_ss_bulk_out_desc.bEndpointAddress;
+ uasp_fs_status_in_desc.bEndpointAddress =
+ uasp_ss_status_in_desc.bEndpointAddress;
+ uasp_hs_status_in_desc.bEndpointAddress =
+ uasp_ss_status_in_desc.bEndpointAddress;
+ uasp_fs_command_out_desc.bEndpointAddress =
+ uasp_ss_command_out_desc.bEndpointAddress;
+ uasp_hs_command_out_desc.bEndpointAddress =
+ uasp_ss_command_out_desc.bEndpointAddress;
+ }
return 0;
autoconf_fail:
- ERROR(fsgd->common, "unable to autoconfigure all endpoints\n");
+ ERROR(uaspd->ucommon, "unable to autoconfigure all endpoints\n");
rc = -ENOTSUPP;
return rc;
}
@@ -2265,14 +2421,28 @@ autoconf_fail:
static void uasp_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct uasp_dev *uaspd = uaspd_from_func(f);
+ struct uasp_lun *ulun;
+ int i;
+
+ DBG(uaspd->ucommon, "%s() - Enter\n", __func__);
- DBG(uaspd->fsg_dev.common, "unbind\n");
- if (uaspd->fsg_dev.common->fsg == &(uaspd->fsg_dev)) {
- uaspd->fsg_dev.common->new_fsg = NULL;
- raise_exception(uaspd->fsg_dev.common, FSG_STATE_CONFIG_CHANGE);
+ /* First stop all lun threads */
+ run_lun_threads(uaspd, LUN_STATE_EXIT);
+ for (i = 0; i < uaspd->ucommon->nluns; i++) {
+ ulun = &(uaspd->ucommon->uluns[i]);
+ if (ulun->lun_state != LUN_STATE_TERMINATED) {
+ wait_for_completion(&ulun->thread_notifier);
+ /* The cleanup routine waits for this completion also */
+ complete(&ulun->thread_notifier);
+ }
+ }
+
+ DBG(uaspd->ucommon, "%s(): Stoped all LUN threads\n", __func__);
+ if (uaspd->ucommon->udev) {
+ uaspd->ucommon->new_udev = NULL;
+ uasp_raise_exception(uaspd->ucommon, UASP_STATE_CONFIG_CHANGE);
/* TODO: make interruptible or killable somehow? */
- wait_event(uaspd->fsg_dev.common->fsg_wait,
- !uaspd->ucommon->common->fsg);
+ wait_event(uaspd->ucommon->uasp_wait, !uaspd->ucommon->udev);
}
uasp_common_put(uaspd->ucommon);
kfree(uaspd->cmd_buff.buf);
@@ -2281,18 +2451,32 @@ static void uasp_unbind(struct usb_configuration *c, struct usb_function *f)
static int uasp_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
- struct fsg_dev *fsg = fsg_from_func(f);
- fsg->common->new_fsg = fsg;
- raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
- return 0;
+ struct uasp_dev *udev = uaspd_from_func(f);
+ int i;
+
+ if (!udev->ucommon->udev) {
+ struct uasp_lun *ulun;
+ DBG(udev->ucommon,
+ "%s() - Waking up UASP main thread\n", __func__);
+ wake_up_process(udev->ucommon->thread_task);
+
+ /* Wakeup LUN threads */
+ for (i = 0, ulun = udev->ucommon->uluns;
+ i < udev->ucommon->nluns; i++, ulun++)
+ wake_up_process(ulun->lun_thread_task);
+ INFO(udev->ucommon, "All lun threads are started\n");
+ }
+ udev->ucommon->new_udev = udev;
+ uasp_raise_exception(udev->ucommon, UASP_STATE_CONFIG_CHANGE);
+ return USB_GADGET_DELAYED_STATUS;
}
static void uasp_disable(struct usb_function *f)
{
struct uasp_dev *udev = uaspd_from_func(f);
- udev->fsg_dev.common->new_fsg = NULL;
- raise_exception(udev->fsg_dev.common, FSG_STATE_CONFIG_CHANGE);
+ udev->ucommon->new_udev = NULL;
+ uasp_raise_exception(udev->ucommon, UASP_STATE_CONFIG_CHANGE);
}
/**
@@ -2309,7 +2493,6 @@ static void uasp_disable(struct usb_function *f)
*/
static int uasp_add(struct usb_composite_dev *cdev,
struct usb_configuration *c,
- struct fsg_common *common,
struct uasp_common *ucommon)
{
struct uasp_dev *uaspd;
@@ -2319,18 +2502,17 @@ static int uasp_add(struct usb_composite_dev *cdev,
if (unlikely(!uaspd))
return -ENOMEM;
- uaspd->fsg_dev.function.name = UASP_DRIVER_DESC;
- uaspd->fsg_dev.function.strings = fsg_strings_array;
- uaspd->fsg_dev.function.descriptors = uasp_hs_function_desc;
- uaspd->fsg_dev.function.hs_descriptors = uasp_hs_function_desc;
- uaspd->fsg_dev.function.bind = uasp_bind;
- uaspd->fsg_dev.function.unbind = uasp_unbind;
- uaspd->fsg_dev.function.set_alt = uasp_set_alt;
- uaspd->fsg_dev.function.disable = uasp_disable;
-
- uaspd->fsg_dev.common = common;
+ uaspd->function.name = UASP_DRIVER_DESC;
+ uaspd->function.strings = fsg_strings_array;
+ uaspd->function.descriptors = uasp_fs_function_desc;
+ uaspd->function.hs_descriptors = uasp_hs_function_desc;
+ uaspd->function.ss_descriptors = uasp_ss_function_desc;
+ uaspd->function.bind = uasp_bind;
+ uaspd->function.unbind = uasp_unbind;
+ uaspd->function.set_alt = uasp_set_alt;
+ uaspd->function.disable = uasp_disable;
- uaspd->ucommon = ucommon;
+ uaspd->ucommon = ucommon;
/* Init the command and status buffers */
uaspd->cmd_buff.buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
@@ -2339,7 +2521,6 @@ static int uasp_add(struct usb_composite_dev *cdev,
goto uasp_add_err;
}
- ucommon->udev = uaspd;
rc = finish_lun_init(uaspd);
if (rc)
goto uasp_add_err;
@@ -2353,7 +2534,7 @@ static int uasp_add(struct usb_composite_dev *cdev,
* recovery we increment it only when call to usb_add_function() was
* successful.
*/
- rc = usb_add_function(c, &uaspd->fsg_dev.function);
+ rc = usb_add_function(c, &uaspd->function);
if (likely(rc == 0))
kref_get(&ucommon->ref);
diff --git a/drivers/usb/gadget/f_uasp.h b/drivers/usb/gadget/f_uasp.h
index 63ade00..3f1e042 100644
--- a/drivers/usb/gadget/f_uasp.h
+++ b/drivers/usb/gadget/f_uasp.h
@@ -18,6 +18,15 @@
#define _F_UASP_H
#include <linux/kernel.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/completion.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -79,30 +88,80 @@ struct uasp_buff {
unsigned stream_id:16;
};
+struct uasp_common;
+struct uasp_lun;
+struct uasp_dev;
+
+enum uasp_state {
+ UASP_STATE_IDLE = 0,
+ UASP_STATE_RESET,
+ UASP_STATE_INTERFACE_CHANGE,
+ UASP_STATE_CONFIG_CHANGE,
+ UASP_STATE_DISCONNECT,
+ UASP_STATE_EXIT,
+ UASP_STATE_TERMINATED
+};
+
/**
* struct uasp_common - Common data shared by all UASP devices
+ * @gadget: pointer to the gadget driver
* @udev: Programming view of uasp device.
- * @common: points to fsg_common in fsg_dev
- * @ubufs: buffers to be used by the uasp device. Each element in
- * ubufs[i].fsg_buff points to a fsg_buffhd struct from fsg_common data
- * structure
- * @uluns: luns of the uasp device. Each element in uluns[i].lun points to a
- * fsg_lun array element from fsg_common data structure
- *
- * Extends the struct fsg_common structure.
+ * @new_udev: Programming view of new uasp device. Used in configuration switch
+ * @uasp_wait: wait queue
+ * @filesem: filesem protects: backing files in use
+ * @lock: lock protects: state, all the req_busy's
+ * @ubufs: buffers to be used by the uasp device.
+ * @uluns: luns of the uasp device.
+ * @nluns: number of luns
+ * @state: For exception handling
+ * @running: set to 1 if the UASP interface is active
+ * @ops: Callback functions.
+ * @private_data: Gadget's private data.
+ * @inquiry_string: Vendor (8 chars), product (16 chars), release (4 hexadecimal
+ * digits) and NULL byte
+ * @ref:
*/
struct uasp_common {
- struct uasp_dev *udev;
- struct fsg_common *common;
- struct uasp_buff ubufs[fsg_num_buffers];
+ struct usb_gadget *gadget;
+ struct usb_composite_dev *cdev;
+ struct msg_common_data *msg_common;
+ struct uasp_dev *udev, *new_udev;
+ wait_queue_head_t uasp_wait;
+
+ struct mutex *config_mutex;
+
+ struct rw_semaphore filesem;
+ spinlock_t lock;
+
+ struct uasp_buff *ubufs;
struct uasp_lun *uluns;
+
+ unsigned int nluns;
+ enum uasp_state state;
+ unsigned int running:1;
+
+ int thread_wakeup_needed;
+ struct completion thread_notifier;
+ struct task_struct *thread_task;
+
+ const struct fsg_operations *ops;
+
+ char inquiry_string[8 + 16 + 4 + 1];
+
struct kref ref;
};
/**
* struct uasp_dev - Programming view of the uasp device
- * @fsg_dev: pointer to the fsg_dev this struct extends
+ * @function: usb function
+ * @gadget: Copy of cdev->gadget
* @ucommon: pointer to the common data of the device
+ * @bulk_in_enabled: set to 1 when BULK IN ep is enabled
+ * @bulk_out_enabled: set to 1 when BULK OUT ep is enabled
+ * @cmd_enabled: set to 1 when COMMAND ep is enabled
+ * @status_enabled: set to 1 when STATUS ep is enabled
+ * @bulk_in: BULK IN endpoint
+ * @bulk_out: BULK OUT endpoint
* @status: status endpoint
* @command: command endpoint
* @cmd_buff: buffer used for receiving commannd IUs
@@ -117,19 +176,27 @@ struct uasp_common {
* Extends the struct fsg_dev structure.
*/
struct uasp_dev {
- struct fsg_dev fsg_dev;
-
+ struct usb_function function;
+ struct usb_gadget *gadget;
struct uasp_common *ucommon;
+
+ unsigned int bulk_in_enabled:1;
+ unsigned int bulk_out_enabled:1;
+ unsigned int cmd_enabled:1;
+ unsigned int status_enabled:1;
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
struct usb_ep *status;
struct usb_ep *command;
+
struct fsg_buffhd cmd_buff;
-#define HS_UASP_MODE 0
-#define SS_UASP_MODE 1
+#define UASP_MODE_UNSET 0
+#define HS_UASP_MODE 1
+#define SS_UASP_MODE 2
uint8_t op_mode;
-
- unsigned int cmd_enabled;
- unsigned int status_enabled;
+ unsigned int forced_hs_mode;
struct list_head cmd_queue;
struct list_head tm_func_queue;
@@ -453,4 +520,5 @@ int all_lun_state_non_processing(struct uasp_dev *udev);
*/
void close_lun(struct uasp_lun *ulun);
+int uasp_sleep_thread(struct uasp_common *common);
#endif /* _F_UASP_H */
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index b7d9e7e..5e4c3d2 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -28,9 +28,7 @@
*/
-#include <linux/kernel.h>
#include <linux/utsname.h>
-#include <linux/usb/ch9.h>
/*-------------------------------------------------------------------------*/
@@ -95,9 +93,11 @@ static struct fsg_module_parameters mod_data = {
FSG_MODULE_PARAMETERS(/* no prefix */, mod_data);
static unsigned long msg_registered;
+static struct msg_common_data msg_common;
+
static void msg_cleanup(void);
-static int msg_thread_exits(struct fsg_common *common)
+static int msg_thread_exits(void *common)
{
msg_cleanup();
return 0;
@@ -105,13 +105,9 @@ static int msg_thread_exits(struct fsg_common *common)
static int __init msg_do_config(struct usb_configuration *c)
{
- static const struct fsg_operations ops = {
- .thread_exits = msg_thread_exits,
- };
static struct fsg_common common;
struct fsg_common *retp;
- struct fsg_config config;
int ret;
if (gadget_is_otg(c->cdev->gadget)) {
@@ -119,14 +115,11 @@ static int __init msg_do_config(struct usb_configuration *c)
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
- fsg_config_from_params(&config, &mod_data);
- config.ops = &ops;
-
/* Init fsg_common and start the fsg main thread */
- retp = fsg_common_init(&common, c->cdev, &config, 1);
+ retp = fsg_common_init(&common, c->cdev, &msg_common.config);
if (IS_ERR(retp))
return PTR_ERR(retp);
-
+ common.msg_common = &msg_common;
ret = fsg_bind_config(c->cdev, c, &common);
fsg_common_put(&common);
return ret;
@@ -140,25 +133,14 @@ static struct usb_configuration msg_config_driver = {
static int __init uasp_do_config(struct usb_configuration *c)
{
- static const struct fsg_operations ops = {
- .thread_exits = msg_thread_exits,
- };
-
- struct fsg_common *fcommon;
struct uasp_common *ucommon;
- struct fsg_config config;
int ret = 0;
- fsg_config_from_params(&config, &mod_data);
- config.ops = &ops;
- fcommon = fsg_common_init(0, c->cdev, &config, 0);
- if (IS_ERR(fcommon))
- return PTR_ERR(fcommon);
-
- ucommon = uasp_common_init(fcommon, c->cdev, &config);
+ ucommon = uasp_common_init(&msg_common, c->cdev);
if (IS_ERR(ucommon))
return PTR_ERR(ucommon);
- ret = uasp_add(c->cdev, c, fcommon, ucommon);
+ msg_common.ucommon = ucommon;
+ ret = uasp_add(c->cdev, c, ucommon);
uasp_common_put(ucommon);
return ret;
@@ -174,36 +156,38 @@ static struct usb_configuration uasp_config_driver = {
/****************************** Gadget Bind ******************************/
-bool use_uasp ;
-module_param(use_uasp, bool, S_IRUGO | S_IWUSR);
static int __init msg_bind(struct usb_composite_dev *cdev)
{
int status;
-
+ struct usb_function *fsg_func;
+ static const struct fsg_operations ops = {
+ .thread_exits = msg_thread_exits,
+ };
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
- if (use_uasp) {
- /*
- * TODO: fix the bellow!
- * Right now the host always chooses the first configuration.
- * Untill this is fixed, if we want the device to opperate in
- * UASP mode we switch the configurations numbers
- */
- msg_config_driver.bConfigurationValue = 2;
- uasp_config_driver.bConfigurationValue = 1;
- /* register uasp configuration */
- status = usb_add_config(cdev, &uasp_config_driver,
- uasp_do_config);
- if (status < 0)
- return status;
- } else {
- /* register our second configuration */
- status = usb_add_config(cdev, &msg_config_driver,
- msg_do_config);
- if (status < 0)
- return status;
- }
+ fsg_config_from_params(&msg_common.config, &mod_data);
+ msg_common.config.ops = &ops;
+ msg_common.config.uasp_ops = &ops;
+ mutex_init(&msg_common.config.config_mutex);
+
+ /* register MS-BOT configuration */
+ status = usb_add_config(cdev, &msg_config_driver,
+ msg_do_config);
+ if (status < 0)
+ return status;
+
+ fsg_func = list_first_entry(&msg_config_driver.functions,
+ struct usb_function,
+ list);
+ msg_common.fcommon = (fsg_from_func(fsg_func))->common;
+
+ /* Register MS-UAS configuration */
+ status = usb_add_config(cdev, &uasp_config_driver, uasp_do_config);
+ if (status < 0)
+ return status;
+
+
set_bit(0, &msg_registered);
return 0;
}
@@ -233,5 +217,9 @@ static void msg_cleanup(void)
{
if (test_and_clear_bit(0, &msg_registered))
usb_composite_unregister(&msg_driver);
+ memset(&msg_common.config, 0, sizeof(struct fsg_config));
+
+ msg_common.fcommon = NULL;
+ msg_common.ucommon = NULL;
}
module_exit(msg_cleanup);
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/storage_common.c
index 4937ad6..6e84b27 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/storage_common.c
@@ -46,7 +46,7 @@
/*
* When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
* sets the number of pipeline buffers (length of the fsg_buffhd array).
- * The valid range of num_buffers is: num >= 2 && num <= 4.
+ * The valid range of num_buffers is: num >= 256 && num <= 1024.
*/
@@ -252,6 +252,70 @@ struct fsg_lun {
#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
+/* FSF callback functions */
+struct fsg_operations {
+ /*
+ * Callback function to call when thread exits. If no
+ * callback is set or it returns value lower then zero MSF
+ * will force eject all LUNs it operates on (including those
+ * marked as non-removable or with prevent_medium_removal flag
+ * set).
+ */
+ int (*thread_exits)(void *common);
+
+ /*
+ * Called prior to ejection. Negative return means error,
+ * zero means to continue with ejection, positive means not to
+ * eject.
+ */
+ int (*pre_eject)(void *common,
+ struct fsg_lun *lun, int num);
+ /*
+ * Called after ejection. Negative return means error, zero
+ * or positive is just a success.
+ */
+ int (*post_eject)(void *common,
+ struct fsg_lun *lun, int num);
+};
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+struct fsg_config {
+ unsigned nluns;
+ struct fsg_lun_config {
+ const char *filename;
+ char ro;
+ char removable;
+ char cdrom;
+ char nofua;
+ } luns[FSG_MAX_LUNS];
+
+ const char *lun_name_format;
+ const char *thread_name;
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* UAS Callback functions. */
+ const struct fsg_operations *uasp_ops;
+ /* Gadget's private data. */
+ void *private_data;
+ struct mutex config_mutex;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+ u16 release;
+
+ char can_stall;
+};
+
+struct msg_common_data {
+ struct fsg_config config;
+ struct fsg_common *fcommon;
+ struct uasp_common *ucommon;
+};
+
+
static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
{
return container_of(dev, struct fsg_lun, dev);
diff --git a/drivers/usb/gadget/uasp_cmdiu.c b/drivers/usb/gadget/uasp_cmdiu.c
index 8836945..61902e4 100644
--- a/drivers/usb/gadget/uasp_cmdiu.c
+++ b/drivers/usb/gadget/uasp_cmdiu.c
@@ -27,14 +27,14 @@
* This function tries to find a free buffer for COMMAND IU or
* TM FUNCTION IU processing.
*/
-struct fsg_buffhd *get_buffhd(struct fsg_buffhd *bh)
+struct fsg_buffhd *get_buffhd(struct uasp_buff *bh)
{
int i;
for (i = 0; i < fsg_num_buffers; i++) {
- if (bh[i].state == BUF_STATE_EMPTY) {
- bh[i].state = BUF_STATE_BUSY;
- return &bh[i];
+ if (bh[i].fsg_buff->state == BUF_STATE_EMPTY) {
+ bh[i].fsg_buff->state = BUF_STATE_BUSY;
+ return bh[i].fsg_buff;
}
}
@@ -56,12 +56,12 @@ static __u32 check_cmdiu(struct uasp_dev *udev,
{
__u32 ua_data = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (!curlun || !curlun->lun) {
if (cmdiu->cdb[0] != INQUIRY &&
cmdiu->cdb[0] != REQUEST_SENSE) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - Logical unit is not supported\n",
__func__);
return SS_LOGICAL_UNIT_NOT_SUPPORTED;
@@ -70,18 +70,24 @@ static __u32 check_cmdiu(struct uasp_dev *udev,
if (curlun->lun->unit_attention_data &&
cmdiu->cdb[0] != INQUIRY &&
cmdiu->cdb[0] != REQUEST_SENSE) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - There is an unit attention condition\n",
__func__);
ua_data = curlun->lun->unit_attention_data;
curlun->lun->unit_attention_data = SS_NO_SENSE;
return ua_data;
}
+ /* HACK!!! REMOVE!!! */
+ if (curlun->lun->unit_attention_data == SS_RESET_OCCURRED &&
+ cmdiu->cdb[0] == INQUIRY) {
+ curlun->lun->unit_attention_data = SS_NO_SENSE;
+ DBG(udev->ucommon, "check_cmdiu() - "
+ "HACK!!! RESETTING unit attention condition\n");
+ }
}
if (curlun && !(curlun->lun->filp) && needs_medium) {
- DBG(udev->ucommon->common,
- "%s() - Medium is not present\n", __func__);
+ DBG(udev->ucommon, "%s() - Medium is not present\n", __func__);
return SS_MEDIUM_NOT_PRESENT;
}
@@ -102,7 +108,7 @@ void fill_sense_iu(struct uasp_dev *udev,
__u8 status,
__u32 sense_data)
{
- DBG(udev->ucommon->common, "%s() - Status = %02x\n", __func__, status);
+ DBG(udev->ucommon, "%s() - Status = %02x\n", __func__, status);
siu->iu_id = IU_ID_SENSE;
siu->reserved1 = 0;
@@ -147,14 +153,14 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
struct cmd_iu *cmdiu)
{
struct fsg_buffhd *bh = cmdiu->bh;
- struct fsg_common *common = udev->ucommon->common;
+ struct uasp_common *ucommon = udev->ucommon;
struct usb_request *req = bh->inreq;
__u8 *buf = (__u8 *)bh->buf;
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
int rc = 0;
- DBG(common, "%s() - Enter\n", __func__);
+ DBG(ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
/* Check is cmdiu is filled correctly */
@@ -162,7 +168,7 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
/* If error sent status with sense data */
if (sense) {
- ERROR(common, "%s() - Error condition\n", __func__);
+ ERROR(ucommon, "%s() - Error condition\n", __func__);
status = STATUS_CHECK_CONDITION;
cmdiu->state = COMMAND_STATE_STATUS;
} else if (udev->op_mode == HS_UASP_MODE)
@@ -213,8 +219,8 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
buf[5] = 0; /* No special options */
buf[6] = 0;
buf[7] = 0;
- memcpy(buf + 8, common->inquiry_string,
- sizeof(common->inquiry_string));
+ memcpy(buf + 8, ucommon->inquiry_string,
+ sizeof(ucommon->inquiry_string));
}
fill_usb_request(req, bh->buf,
@@ -224,7 +230,7 @@ static int do_uasp_inquiry(struct uasp_dev *udev,
be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
@@ -286,7 +292,7 @@ static int do_uasp_request_sense(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
/* Check is cmdiu is filled correctly */
@@ -377,7 +383,7 @@ static int do_uasp_request_sense(struct uasp_dev *udev,
be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
@@ -401,7 +407,7 @@ static int do_uasp_request_sense(struct uasp_dev *udev,
break;
}
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return rc;
}
@@ -424,7 +430,7 @@ static int do_uasp_test_unit_ready(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Check is cmdiu is filled correctly */
sense = check_cmdiu(udev, curlun, cmdiu, 0);
@@ -472,7 +478,7 @@ static int do_uasp_mode_sense(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
sense = check_cmdiu(udev, curlun, cmdiu, 0);
@@ -592,7 +598,7 @@ static int do_uasp_mode_sense(struct uasp_dev *udev,
fill_usb_request(req, buf0, len, 0, cmdiu, 0,
be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
@@ -637,7 +643,7 @@ static int do_uasp_prevent_allow(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Check is cmdiu is filled correctly */
sense = check_cmdiu(udev, curlun, cmdiu, 0);
@@ -699,11 +705,11 @@ static int do_uasp_read(struct uasp_dev *udev,
__u8 status = STATUS_GOOD;
int rc = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
if (!curlun) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Error condition - curlun = NULL\n",
__func__);
sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
@@ -759,7 +765,7 @@ static int do_uasp_read(struct uasp_dev *udev,
cmdiu->state = COMMAND_STATE_DATA;
cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
- DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ DBG(udev->ucommon, "%s() lba = %d, file_offset = %d,"
" xfer_len = %d\n",
__func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
}
@@ -867,19 +873,19 @@ send_more_data: /*
fill_usb_request(req, bh->buf, nread, 0,
cmdiu, 0, be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
} else if (cmdiu->req_sts == CMD_REQ_IN_PROGRESS) {
/* Completion of sent data is not received yet */
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - completion for bh is not received",
__func__);
break;
} else {
/* Completion of the sent data is done */
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - COMMAND_STATE_DATA for bh\n", __func__);
if (cmdiu->xfer_len == 0)
goto send_status;
@@ -926,7 +932,7 @@ static int do_uasp_read_capacity(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
/* Check is cmdiu is filled correctly */
@@ -987,7 +993,7 @@ static int do_uasp_read_capacity(struct uasp_dev *udev,
cmdiu, 0, be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
@@ -1034,7 +1040,7 @@ static int do_uasp_read_format_capacities(struct uasp_dev *udev,
__u8 status = STATUS_GOOD;
int rc = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (cmdiu->state == COMMAND_STATE_IDLE) {
/* Check is cmdiu is filled correctly */
@@ -1096,7 +1102,7 @@ static int do_uasp_read_format_capacities(struct uasp_dev *udev,
cmdiu, 0, be16_to_cpup(&cmdiu->tag),
uasp_bulk_in_complete, udev->op_mode);
- cmdiu->ep = udev->fsg_dev.bulk_in;
+ cmdiu->ep = udev->bulk_in;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 1;
break;
@@ -1118,7 +1124,7 @@ static int do_uasp_read_format_capacities(struct uasp_dev *udev,
break;
}
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return rc;
}
@@ -1143,7 +1149,7 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
__u8 status = STATUS_GOOD;
int start, loej;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
start = cmdiu->cdb[4] & 0x01;
loej = cmdiu->cdb[4] & 0x02;
@@ -1190,7 +1196,7 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
} else {
/* Are we allowed to unload the media? */
if (curlun->lun->prevent_medium_removal) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s(): unload attempt prevented\n",
__func__);
sense = SS_MEDIUM_REMOVAL_PREVENTED;
@@ -1199,10 +1205,10 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
}
/* Simulate an unload/eject */
- if (udev->ucommon->common->ops &&
- udev->ucommon->common->ops->pre_eject) {
- int r = udev->ucommon->common->ops->pre_eject(
- udev->ucommon->common, curlun->lun,
+ if (udev->ucommon->ops &&
+ udev->ucommon->ops->pre_eject) {
+ int r = udev->ucommon->ops->pre_eject(
+ udev->ucommon, curlun->lun,
curlun - udev->ucommon->uluns);
if (unlikely(r < 0))
status = STATUS_CHECK_CONDITION;
@@ -1210,18 +1216,17 @@ static int do_uasp_start_stop(struct uasp_dev *udev,
goto do_uasp_start_stop_done;
}
- up_read(&(udev->ucommon->common->filesem));
- down_write(&(udev->ucommon->common->filesem));
+ up_read(&(udev->ucommon->filesem));
+ down_write(&(udev->ucommon->filesem));
close_lun(curlun);
- up_write(&(udev->ucommon->common->filesem));
- down_read(&(udev->ucommon->common->filesem));
-
- if (udev->ucommon->common->ops &&
- udev->ucommon->common->ops->post_eject) {
- if (udev->ucommon->common->ops->
- post_eject(udev->ucommon->common,
- curlun->lun,
- curlun - udev->ucommon->uluns)
+ up_write(&(udev->ucommon->filesem));
+ down_read(&(udev->ucommon->filesem));
+
+ if (udev->ucommon->ops &&
+ udev->ucommon->ops->post_eject) {
+ if (udev->ucommon->ops->
+ post_eject(udev->ucommon, curlun->lun,
+ curlun - udev->ucommon->uluns)
< 0)
status = STATUS_CHECK_CONDITION;
}
@@ -1237,7 +1242,7 @@ do_uasp_start_stop_done:
udev->op_mode);
cmdiu->ep = udev->status;
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return 1;
}
@@ -1267,7 +1272,7 @@ static int do_uasp_verify(struct uasp_dev *udev,
__u32 sense = SS_NO_SENSE;
__u8 status = STATUS_GOOD;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
file_offset = (get_unaligned_be32(&cmdiu->cdb[2]) << 9);
ver_len = (get_unaligned_be32(&cmdiu->cdb[7]) << 9);
@@ -1354,7 +1359,7 @@ static int do_uasp_verify(struct uasp_dev *udev,
be16_to_cpup(&cmdiu->tag), status_complete,
udev->op_mode);
cmdiu->ep = udev->status;
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return 1;
}
@@ -1386,10 +1391,10 @@ static int do_uasp_write(struct uasp_dev *udev,
__u8 status = STATUS_GOOD;
int rc = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
if (!curlun) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() - Error condition - curlun = NULL\n",
__func__);
sense = SS_LOGICAL_UNIT_NOT_SUPPORTED;
@@ -1474,7 +1479,7 @@ static int do_uasp_write(struct uasp_dev *udev,
else
cmdiu->state = COMMAND_STATE_DATA;
cmdiu->req_sts = CMD_REQ_NOT_SUBMITTED;
- DBG(udev->ucommon->common, "%s() lba = %d, file_offset = %d,"
+ DBG(udev->ucommon, "%s() lba = %d, file_offset = %d,"
" xfer_len = %d\n",
__func__, lba, cmdiu->file_offset, cmdiu->xfer_len);
}
@@ -1549,11 +1554,11 @@ get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
fill_usb_request(req, bh->buf, amount, 0,
cmdiu, 0, be16_to_cpup(&cmdiu->tag),
uasp_bulk_out_complete, udev->op_mode);
- DBG(udev->ucommon->common, "%s() fill_usb_request for"
+ DBG(udev->ucommon, "%s() fill_usb_request for"
" out endpoint, amout = %d",
__func__, amount);
- cmdiu->ep = udev->fsg_dev.bulk_out;
+ cmdiu->ep = udev->bulk_out;
cmdiu->req_sts = CMD_REQ_IN_PROGRESS;
rc = 2;
break;
@@ -1577,7 +1582,7 @@ get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
* request for this command, we should abort
* all submitted requests for this command
*/
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - Host aborted the command\n",
__func__);
goto send_status;
@@ -1586,7 +1591,7 @@ get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
amount = req->actual;
if (curlun->lun->file_length - cmdiu->file_offset <
amount) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s(): write %u @ %llu beyond end %llu\n",
__func__, amount,
(unsigned long long)cmdiu->file_offset,
@@ -1601,18 +1606,18 @@ get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
nwritten = vfs_write(curlun->lun->filp,
(char __user *) bh->buf,
amount, &file_offset_tmp);
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s(): file write %u @ %llu -> %d\n", __func__,
amount, (unsigned long long)cmdiu->file_offset,
(int)nwritten);
if (nwritten < 0) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s(): error in file write: %d\n",
__func__, (int)nwritten);
nwritten = 0;
} else if (nwritten < amount) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s(): partial file write: %d/%u\n",
__func__, (int)nwritten, amount);
nwritten -= (nwritten & 511);
@@ -1633,7 +1638,7 @@ get_more_data: if (cmdiu->req_sts == CMD_REQ_NOT_SUBMITTED) {
}
if (cmdiu->xfer_len == 0) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - cmdiu->xferlen = 0, "
"send status\n", __func__);
goto send_status;
@@ -1657,7 +1662,7 @@ send_status:
break;
}
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return rc;
}
@@ -1682,7 +1687,7 @@ static int do_uasp_synchronize_cache(struct uasp_dev *udev,
uint8_t status = STATUS_GOOD;
int rc;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Check is cmdiu is filled correctly */
sense = check_cmdiu(udev, curlun, cmdiu, 0);
@@ -1707,7 +1712,7 @@ static int do_uasp_synchronize_cache(struct uasp_dev *udev,
udev->op_mode);
cmdiu->ep = udev->status;
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
return 1;
}
@@ -1727,11 +1732,11 @@ static void process_cmdiu(struct uasp_dev *udev,
struct usb_request *req;
int rc = 0;
- DBG(udev->ucommon->common, "%s() Enter. (cmdiu->cdb[0]=%04x)\n",
+ DBG(udev->ucommon, "%s() Enter. (cmdiu->cdb[0]=%04x)\n",
__func__, cmdiu->cdb[0]);
/* We're using the backing file */
- down_read(&udev->ucommon->common->filesem);
+ down_read(&udev->ucommon->filesem);
switch (cmdiu->cdb[0]) {
case INQUIRY:
rc = do_uasp_inquiry(udev, curlun, cmdiu);
@@ -1781,7 +1786,7 @@ static void process_cmdiu(struct uasp_dev *udev,
case RESERVE:
case SEND_DIAGNOSTIC:
default:
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s(): Unsupported command = %x\n",
__func__, cmdiu->cdb[0]);
cmdiu->state = COMMAND_STATE_STATUS;
@@ -1798,7 +1803,7 @@ static void process_cmdiu(struct uasp_dev *udev,
break;
}
- up_read(&udev->ucommon->common->filesem);
+ up_read(&udev->ucommon->filesem);
if (rc) {
if (rc == 1) {
req = cmdiu->bh->inreq;
@@ -1808,11 +1813,11 @@ static void process_cmdiu(struct uasp_dev *udev,
cmdiu->bh->outreq_busy = 1;
}
if (usb_ep_queue(cmdiu->ep, req, 0)) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s()usb_ep_queue failed\n", __func__);
cmdiu->state = COMMAND_STATE_FAILED;
} else {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - process_cmdiu: queued req to ep\n",
__func__);
if (curlun) {
@@ -1821,10 +1826,10 @@ static void process_cmdiu(struct uasp_dev *udev,
spin_unlock_irqrestore(&(curlun->lock), flags);
} else {
spin_lock_irqsave(
- &(udev->ucommon->common->lock), flags);
+ &(udev->ucommon->lock), flags);
udev->active_requests++;
spin_unlock_irqrestore(
- &(udev->ucommon->common->lock), flags);
+ &(udev->ucommon->lock), flags);
}
}
}
@@ -1843,7 +1848,7 @@ void do_cmdiu(struct uasp_dev *udev, struct uasp_lun *curlun)
struct cmd_iu *cmdiu, *tmp;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Select the cmd_queue from which cmdius should be processed */
if (curlun)
@@ -1852,18 +1857,18 @@ void do_cmdiu(struct uasp_dev *udev, struct uasp_lun *curlun)
link = &udev->cmd_queue;
list_for_each_entry_safe(cmdiu, tmp, link, node) {
- DBG(udev->ucommon->common, "%s() - Rolling over cmdiu queue\n",
+ DBG(udev->ucommon, "%s() - Rolling over cmdiu queue\n",
__func__);
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
if (cmdiu->state == COMMAND_STATE_IDLE) {
/* Try to get buffers for cmdiu processing */
- cmdiu->bh = get_buffhd(udev->ucommon->common->buffhds);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ cmdiu->bh = get_buffhd(udev->ucommon->ubufs);
+ spin_unlock_irqrestore(&(udev->ucommon->lock),
flags);
if (!cmdiu->bh) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() -Didn't manage to get buffers for "
"cmdiu!\n", __func__);
continue;
@@ -1872,15 +1877,14 @@ void do_cmdiu(struct uasp_dev *udev, struct uasp_lun *curlun)
cmdiu->state == COMMAND_STATE_RR_WR) {
if (cmdiu->req_sts == CMD_REQ_COMPLETED)
spin_unlock_irqrestore(
- &(udev->ucommon->common->lock), flags);
+ &(udev->ucommon->lock), flags);
else {
spin_unlock_irqrestore(
- &(udev->ucommon->common->lock), flags);
+ &(udev->ucommon->lock), flags);
continue;
}
} else {
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
- flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
continue;
}
diff --git a/drivers/usb/gadget/uasp_tmiu.c b/drivers/usb/gadget/uasp_tmiu.c
index 5f70424..8f8fd86 100644
--- a/drivers/usb/gadget/uasp_tmiu.c
+++ b/drivers/usb/gadget/uasp_tmiu.c
@@ -32,7 +32,7 @@ void fill_response_iu(struct uasp_dev *udev,
uint32_t resp_info,
uint8_t status)
{
- DBG(udev->ucommon->common, "%s() - Enter. Status = %02x\n", __func__,
+ DBG(udev->ucommon, "%s() - Enter. Status = %02x\n", __func__,
status);
riu->iu_id = IU_ID_RESPONSE;
riu->reserved = 0;
@@ -61,7 +61,7 @@ static void reset_lun(struct uasp_dev *udev,
uint8_t status;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
@@ -108,7 +108,7 @@ static void abort_task(struct uasp_dev *udev,
unsigned long flags;
uint8_t status;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
status = RESPONSE_INCORRECT_LUN;
@@ -121,7 +121,7 @@ static void abort_task(struct uasp_dev *udev,
goto found;
/* Command with specified ipt_tag not found */
- DBG(udev->ucommon->common, "%s(): cmdiu with tag %04x wasn't found\n",
+ DBG(udev->ucommon, "%s(): cmdiu with tag %04x wasn't found\n",
__func__, tmiu->task_tag);
cmdiu = 0;
@@ -177,7 +177,7 @@ static void abort_task_set(struct uasp_dev *udev,
uint8_t status;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
@@ -215,7 +215,7 @@ static void reset_nexus(struct uasp_dev *udev,
uint8_t status;
int rc = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
@@ -226,26 +226,27 @@ static void reset_nexus(struct uasp_dev *udev,
* Sleep if luns are in processing
*/
while (!all_lun_state_non_processing(udev)) {
- DBG(udev->ucommon->common,
+ DBG(udev->ucommon,
"%s() - Luns are in process. Going to sleep\n", __func__);
- rc = sleep_thread(udev->ucommon->common);
+ rc = uasp_sleep_thread(udev->ucommon);
if (rc) {
- ERROR(udev->ucommon->common,
- "%s() - sleep_thread failed! (%d)", __func__, rc);
+ ERROR(udev->ucommon,
+ "%s() - uasp_sleep_thread failed! (%d)",
+ __func__, rc);
status = RESPONSE_TM_FUNCTION_FAILED;
goto reset_nexus_fill_response;
}
- DBG(udev->ucommon->common, "%s() - Wakes up\n", __func__);
+ DBG(udev->ucommon, "%s() - Wakes up\n", __func__);
rc = 0;
}
/* Abort general commands and tmius */
abort_commands(udev, &udev->cmd_queue, &udev->tm_func_queue,
- &(udev->ucommon->common->lock));
+ &(udev->ucommon->lock));
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
udev->pending_requests = 0;
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
status = RESPONSE_TM_FUNCTION_COMPLETE;
reset_nexus_fill_response:
@@ -255,7 +256,7 @@ reset_nexus_fill_response:
0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
status_complete, udev->op_mode);
tmiu->ep = udev->status;
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
}
/**
@@ -277,7 +278,7 @@ static void query_unit_attention(struct uasp_dev *udev,
uint8_t status;
uint32_t resp_info = 0;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
status = RESPONSE_INCORRECT_LUN;
@@ -324,7 +325,7 @@ static void query_task(struct uasp_dev *udev,
unsigned long flags;
uint8_t status = RESPONSE_TM_FUNCTION_COMPLETE;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
status = RESPONSE_INCORRECT_LUN;
@@ -371,7 +372,7 @@ static void query_task_set(struct uasp_dev *udev,
unsigned long flags;
uint8_t status;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
riu = (struct response_iu *)tmiu->bh->buf;
if (!curlun) {
status = RESPONSE_INCORRECT_LUN;
@@ -400,7 +401,7 @@ q_task_set_fill_response:
0, (void *)tmiu, 0, be16_to_cpup(&tmiu->tag),
status_complete, udev->op_mode);
tmiu->ep = udev->status;
- DBG(udev->ucommon->common, "%s() - Exit\n", __func__);
+ DBG(udev->ucommon, "%s() - Exit\n", __func__);
}
/**
@@ -448,7 +449,7 @@ static void process_tmiu(struct uasp_dev *udev,
break;
default:
- ERROR(udev->ucommon->common, "%s(): Unsupported tmiu = %x\n",
+ ERROR(udev->ucommon, "%s(): Unsupported tmiu = %x\n",
__func__, tmiu->tm_function);
riu = (struct response_iu *)tmiu->bh->inreq->buf;
fill_response_iu(udev, riu, tmiu->tag, 0,
@@ -464,8 +465,7 @@ static void process_tmiu(struct uasp_dev *udev,
tmiu->state = COMMAND_STATE_STATUS;
if (usb_ep_queue(tmiu->ep, tmiu->bh->inreq, 0)) {
- ERROR(udev->ucommon->common,
- "%s()usb_ep_queue failed\n", __func__);
+ ERROR(udev->ucommon, "%s()usb_ep_queue failed\n", __func__);
tmiu->state = COMMAND_STATE_FAILED;
} else {
tmiu->bh->inreq_busy = 1;
@@ -474,10 +474,10 @@ static void process_tmiu(struct uasp_dev *udev,
curlun->active_requests++;
spin_unlock_irqrestore(&(curlun->lock), flags);
} else {
- spin_lock_irqsave(&(udev->ucommon->common->lock),
+ spin_lock_irqsave(&(udev->ucommon->lock),
flags);
udev->active_requests++;
- spin_unlock_irqrestore(&(udev->ucommon->common->lock),
+ spin_unlock_irqrestore(&(udev->ucommon->lock),
flags);
}
}
@@ -497,7 +497,7 @@ void do_tmiu(struct uasp_dev *udev, struct uasp_lun *curlun)
struct tm_iu *tmiu, *tmp;
unsigned long flags;
- DBG(udev->ucommon->common, "%s() - Enter\n", __func__);
+ DBG(udev->ucommon, "%s() - Enter\n", __func__);
/* Select the tm_func_queue from which tmius should be processed */
if (curlun)
@@ -505,19 +505,19 @@ void do_tmiu(struct uasp_dev *udev, struct uasp_lun *curlun)
else
link = &udev->tm_func_queue;
- DBG(udev->ucommon->common, "%s() - Rolling over tmiu queue\n",
+ DBG(udev->ucommon, "%s() - Rolling over tmiu queue\n",
__func__);
list_for_each_entry_safe(tmiu, tmp, link, node) {
if (tmiu->state != COMMAND_STATE_IDLE)
continue;
/* Try to get buffer for tmiu provessing */
- spin_lock_irqsave(&(udev->ucommon->common->lock), flags);
- tmiu->bh = get_buffhd(udev->ucommon->common->buffhds);
- spin_unlock_irqrestore(&(udev->ucommon->common->lock), flags);
+ spin_lock_irqsave(&(udev->ucommon->lock), flags);
+ tmiu->bh = get_buffhd(udev->ucommon->ubufs);
+ spin_unlock_irqrestore(&(udev->ucommon->lock), flags);
if (!tmiu->bh) {
- ERROR(udev->ucommon->common,
+ ERROR(udev->ucommon,
"%s() -Didnt manage to get buffers for tmiu!\n",
__func__);
continue;
--
1.7.6
--
Sent by a Consultant for Qualcomm Innovation Center, Inc.
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum
^ permalink raw reply related [flat|nested] 4+ messages in thread
* Re: [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure
2011-12-04 20:12 [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 2/3] uas: MS UAS Gadget driver - Implementation Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 3/3] uas: Supporting UAS and BOT configuration Shimrit Malichi
@ 2011-12-05 8:13 ` Felipe Balbi
2 siblings, 0 replies; 4+ messages in thread
From: Felipe Balbi @ 2011-12-05 8:13 UTC (permalink / raw)
To: Shimrit Malichi
Cc: greg, linux-usb, linux-arm-msm, balbi, ablay, Tatyana Brokhman,
open list
[-- Attachment #1: Type: text/plain, Size: 1643 bytes --]
Hi,
On Sun, Dec 04, 2011 at 10:12:29PM +0200, Shimrit Malichi wrote:
> This patch implements the infrastructure for the UAS gadget driver.
> The UAS gadget driver registers as a second configuration of the MS
> gadet driver.
>
> A new module parameter was added to the mass_storage module:
> bool use_uasp. (default = 0)
> If this parameter is set to true, the mass_storage module will register
> with the UAS configuration as the devices first configuration and
> operate according to the UAS protocol.
>
> The number of buffers used by the mass_storage device was increased
> according to the number of supported streams.
>
> It defines the API for COMMAND/TASK MANAGEMENT IU implementation.
>
> Change-Id: I86ec7f23b15e602b0f46934adbf5824472e59a1f
> Signed-off-by: Tatyana Brokhman <tlinder@codeaurora.org>
> ---
> drivers/usb/gadget/f_mass_storage.c | 26 +-
> drivers/usb/gadget/f_uasp.c | 2393 +++++++++++++++++++++++++++++++++++
> drivers/usb/gadget/f_uasp.h | 430 +++++++
> drivers/usb/gadget/mass_storage.c | 67 +-
> drivers/usb/gadget/storage_common.c | 24 +-
> drivers/usb/gadget/uasp_cmdiu.c | 514 ++++++++
> drivers/usb/gadget/uasp_tmiu.c | 277 ++++
> 7 files changed, 3708 insertions(+), 23 deletions(-)
> create mode 100644 drivers/usb/gadget/f_uasp.c
> create mode 100644 drivers/usb/gadget/f_uasp.h
> create mode 100644 drivers/usb/gadget/uasp_cmdiu.c
> create mode 100644 drivers/usb/gadget/uasp_tmiu.c
I wonder how many times we will have to ask for this to be done on
TARGET FRAMEWORK!!! It's like talking to the walls.
--
balbi
[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2011-12-05 8:13 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2011-12-04 20:12 [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 2/3] uas: MS UAS Gadget driver - Implementation Shimrit Malichi
2011-12-04 20:12 ` [RFC/PATCH v4 3/3] uas: Supporting UAS and BOT configuration Shimrit Malichi
2011-12-05 8:13 ` [RFC/PATCH v4 1/3] uas: MS UAS Gadget driver - Infrastructure Felipe Balbi
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).