diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-21 20:40:42 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-05-21 20:40:42 -0400 |
| commit | ba01a87e37d3ca9efe141e2907c2ec3f89490b4f (patch) | |
| tree | 144f2b3534b5428192c6d3645ec49c6de2fcdaa2 | |
| parent | c9bfa7d75ba7269c97595f03c3152372e8b37823 (diff) | |
| parent | c52661d60f636d17e26ad834457db333bd1df494 (diff) | |
Merge branch 'usb-target-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending
Pull usb-gadget scsi-target merge from Nicholas Bellinger:
"As promised, here is the pull request for Sebastian's usb-gadget
target UASP / BOT driver for v3.5-rc1. This code has been in
linux-next for a number of weeks, and is now ready for an initial
merge.
This fabric uses the target framework to provide a usb gadget device.
This gadget supports the USB Attached SCSI Protocol (UASP) and Bulk
Only Transfers (BOT or BBB). BOT is the primary interface, UAS is the
alternative interface.
Note this series is dependent upon a single target core patch for
adding se_cmd->unknown_data_length in target-pending/for-next, that
got merged in the parent.
Kudos to Sebastian for making this driver happen so easily, and for
his patches to improve usb-core and target core along the way to his
goal. Also thanks to Felipe + Greg-KH for their help in getting this
driver ready for mainline."
* 'usb-target-merge' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target-pending:
usb-gadget: Initial merge of target module for UASP + BOT
| -rw-r--r-- | drivers/usb/gadget/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/usb/gadget/Makefile | 2 | ||||
| -rw-r--r-- | drivers/usb/gadget/tcm_usb_gadget.c | 2480 | ||||
| -rw-r--r-- | drivers/usb/gadget/tcm_usb_gadget.h | 146 |
4 files changed, 2638 insertions, 0 deletions
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 2633f759511..569b33e754b 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig | |||
| @@ -798,6 +798,16 @@ config USB_MASS_STORAGE | |||
| 798 | Say "y" to link the driver statically, or "m" to build | 798 | Say "y" to link the driver statically, or "m" to build |
| 799 | a dynamically linked module called "g_mass_storage". | 799 | a dynamically linked module called "g_mass_storage". |
| 800 | 800 | ||
| 801 | config USB_GADGET_TARGET | ||
| 802 | tristate "USB Gadget Target Fabric Module" | ||
| 803 | depends on TARGET_CORE | ||
| 804 | help | ||
| 805 | This fabric is an USB gadget. Two USB protocols are supported that is | ||
| 806 | BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is | ||
| 807 | advertised on alternative interface 0 (primary) and UAS is on | ||
| 808 | alternative interface 1. Both protocols can work on USB2.0 and USB3.0. | ||
| 809 | UAS utilizes the USB 3.0 feature called streams support. | ||
| 810 | |||
| 801 | config USB_G_SERIAL | 811 | config USB_G_SERIAL |
| 802 | tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" | 812 | tristate "Serial Gadget (with CDC ACM and CDC OBEX support)" |
| 803 | help | 813 | help |
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index b7f6eefc392..fc5b83683de 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile | |||
| @@ -52,6 +52,7 @@ g_nokia-y := nokia.o | |||
| 52 | g_webcam-y := webcam.o | 52 | g_webcam-y := webcam.o |
| 53 | g_ncm-y := ncm.o | 53 | g_ncm-y := ncm.o |
| 54 | g_acm_ms-y := acm_ms.o | 54 | g_acm_ms-y := acm_ms.o |
| 55 | g_tcm_usb_gadget-y := tcm_usb_gadget.o | ||
| 55 | 56 | ||
| 56 | obj-$(CONFIG_USB_ZERO) += g_zero.o | 57 | obj-$(CONFIG_USB_ZERO) += g_zero.o |
| 57 | obj-$(CONFIG_USB_AUDIO) += g_audio.o | 58 | obj-$(CONFIG_USB_AUDIO) += g_audio.o |
| @@ -71,3 +72,4 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o | |||
| 71 | obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o | 72 | obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o |
| 72 | obj-$(CONFIG_USB_G_NCM) += g_ncm.o | 73 | obj-$(CONFIG_USB_G_NCM) += g_ncm.o |
| 73 | obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o | 74 | obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o |
| 75 | obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o | ||
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c new file mode 100644 index 00000000000..c46439c8dd7 --- /dev/null +++ b/drivers/usb/gadget/tcm_usb_gadget.c | |||
| @@ -0,0 +1,2480 @@ | |||
| 1 | /* Target based USB-Gadget | ||
| 2 | * | ||
| 3 | * UAS protocol handling, target callbacks, configfs handling, | ||
| 4 | * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling. | ||
| 5 | * | ||
| 6 | * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de> | ||
| 7 | * License: GPLv2 as published by FSF. | ||
| 8 | */ | ||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/module.h> | ||
| 11 | #include <linux/types.h> | ||
| 12 | #include <linux/string.h> | ||
| 13 | #include <linux/configfs.h> | ||
| 14 | #include <linux/ctype.h> | ||
| 15 | #include <linux/usb/ch9.h> | ||
| 16 | #include <linux/usb/composite.h> | ||
| 17 | #include <linux/usb/gadget.h> | ||
| 18 | #include <linux/usb/storage.h> | ||
| 19 | #include <scsi/scsi.h> | ||
| 20 | #include <scsi/scsi_tcq.h> | ||
| 21 | #include <target/target_core_base.h> | ||
| 22 | #include <target/target_core_fabric.h> | ||
| 23 | #include <target/target_core_fabric_configfs.h> | ||
| 24 | #include <target/target_core_configfs.h> | ||
| 25 | #include <target/configfs_macros.h> | ||
| 26 | #include <asm/unaligned.h> | ||
| 27 | |||
| 28 | #include "usbstring.c" | ||
| 29 | #include "epautoconf.c" | ||
| 30 | #include "config.c" | ||
| 31 | #include "composite.c" | ||
| 32 | |||
| 33 | #include "tcm_usb_gadget.h" | ||
| 34 | |||
| 35 | static struct target_fabric_configfs *usbg_fabric_configfs; | ||
| 36 | |||
| 37 | static inline struct f_uas *to_f_uas(struct usb_function *f) | ||
| 38 | { | ||
| 39 | return container_of(f, struct f_uas, function); | ||
| 40 | } | ||
| 41 | |||
| 42 | static void usbg_cmd_release(struct kref *); | ||
| 43 | |||
| 44 | static inline void usbg_cleanup_cmd(struct usbg_cmd *cmd) | ||
| 45 | { | ||
| 46 | kref_put(&cmd->ref, usbg_cmd_release); | ||
| 47 | } | ||
| 48 | |||
| 49 | /* Start bot.c code */ | ||
| 50 | |||
| 51 | static int bot_enqueue_cmd_cbw(struct f_uas *fu) | ||
| 52 | { | ||
| 53 | int ret; | ||
| 54 | |||
| 55 | if (fu->flags & USBG_BOT_CMD_PEND) | ||
| 56 | return 0; | ||
| 57 | |||
| 58 | ret = usb_ep_queue(fu->ep_out, fu->cmd.req, GFP_ATOMIC); | ||
| 59 | if (!ret) | ||
| 60 | fu->flags |= USBG_BOT_CMD_PEND; | ||
| 61 | return ret; | ||
| 62 | } | ||
| 63 | |||
| 64 | static void bot_status_complete(struct usb_ep *ep, struct usb_request *req) | ||
| 65 | { | ||
| 66 | struct usbg_cmd *cmd = req->context; | ||
| 67 | struct f_uas *fu = cmd->fu; | ||
| 68 | |||
| 69 | usbg_cleanup_cmd(cmd); | ||
| 70 | if (req->status < 0) { | ||
| 71 | pr_err("ERR %s(%d)\n", __func__, __LINE__); | ||
| 72 | return; | ||
| 73 | } | ||
| 74 | |||
| 75 | /* CSW completed, wait for next CBW */ | ||
| 76 | bot_enqueue_cmd_cbw(fu); | ||
| 77 | } | ||
| 78 | |||
| 79 | static void bot_enqueue_sense_code(struct f_uas *fu, struct usbg_cmd *cmd) | ||
| 80 | { | ||
| 81 | struct bulk_cs_wrap *csw = &fu->bot_status.csw; | ||
| 82 | int ret; | ||
| 83 | u8 *sense; | ||
| 84 | unsigned int csw_stat; | ||
| 85 | |||
| 86 | csw_stat = cmd->csw_code; | ||
| 87 | |||
| 88 | /* | ||
| 89 | * We can't send SENSE as a response. So we take ASC & ASCQ from our | ||
| 90 | * sense buffer and queue it and hope the host sends a REQUEST_SENSE | ||
| 91 | * command where it learns why we failed. | ||
| 92 | */ | ||
| 93 | sense = cmd->sense_iu.sense; | ||
| 94 | |||
| 95 | csw->Tag = cmd->bot_tag; | ||
| 96 | csw->Status = csw_stat; | ||
| 97 | fu->bot_status.req->context = cmd; | ||
| 98 | ret = usb_ep_queue(fu->ep_in, fu->bot_status.req, GFP_ATOMIC); | ||
| 99 | if (ret) | ||
| 100 | pr_err("%s(%d) ERR: %d\n", __func__, __LINE__, ret); | ||
| 101 | } | ||
| 102 | |||
| 103 | static void bot_err_compl(struct usb_ep *ep, struct usb_request *req) | ||
| 104 | { | ||
| 105 | struct usbg_cmd *cmd = req->context; | ||
| 106 | struct f_uas *fu = cmd->fu; | ||
| 107 | |||
| 108 | if (req->status < 0) | ||
| 109 | pr_err("ERR %s(%d)\n", __func__, __LINE__); | ||
| 110 | |||
| 111 | if (cmd->data_len) { | ||
| 112 | if (cmd->data_len > ep->maxpacket) { | ||
| 113 | req->length = ep->maxpacket; | ||
| 114 | cmd->data_len -= ep->maxpacket; | ||
| 115 | } else { | ||
| 116 | req->length = cmd->data_len; | ||
| 117 | cmd->data_len = 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | usb_ep_queue(ep, req, GFP_ATOMIC); | ||
| 121 | return ; | ||
| 122 | } | ||
| 123 | bot_enqueue_sense_code(fu, cmd); | ||
| 124 | } | ||
| 125 | |||
| 126 | static void bot_send_bad_status(struct usbg_cmd *cmd) | ||
| 127 | { | ||
| 128 | struct f_uas *fu = cmd->fu; | ||
| 129 | struct bulk_cs_wrap *csw = &fu->bot_status.csw; | ||
| 130 | struct usb_request *req; | ||
| 131 | struct usb_ep *ep; | ||
| 132 | |||
| 133 | csw->Residue = cpu_to_le32(cmd->data_len); | ||
| 134 | |||
| 135 | if (cmd->data_len) { | ||
| 136 | if (cmd->is_read) { | ||
| 137 | ep = fu->ep_in; | ||
| 138 | req = fu->bot_req_in; | ||
| 139 | } else { | ||
| 140 | ep = fu->ep_out; | ||
| 141 | req = fu->bot_req_out; | ||
| 142 | } | ||
| 143 | |||
| 144 | if (cmd->data_len > fu->ep_in->maxpacket) { | ||
| 145 | req->length = ep->maxpacket; | ||
| 146 | cmd->data_len -= ep->maxpacket; | ||
| 147 | } else { | ||
| 148 | req->length = cmd->data_len; | ||
| 149 | cmd->data_len = 0; | ||
| 150 | } | ||
| 151 | req->complete = bot_err_compl; | ||
| 152 | req->context = cmd; | ||
| 153 | req->buf = fu->cmd.buf; | ||
| 154 | usb_ep_queue(ep, req, GFP_KERNEL); | ||
| 155 | } else { | ||
| 156 | bot_enqueue_sense_code(fu, cmd); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | |||
