aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/usb/gadget/Kconfig10
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.c2480
-rw-r--r--drivers/usb/gadget/tcm_usb_gadget.h146
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
801config 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
801config USB_G_SERIAL 811config 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
52g_webcam-y := webcam.o 52g_webcam-y := webcam.o
53g_ncm-y := ncm.o 53g_ncm-y := ncm.o
54g_acm_ms-y := acm_ms.o 54g_acm_ms-y := acm_ms.o
55g_tcm_usb_gadget-y := tcm_usb_gadget.o
55 56
56obj-$(CONFIG_USB_ZERO) += g_zero.o 57obj-$(CONFIG_USB_ZERO) += g_zero.o
57obj-$(CONFIG_USB_AUDIO) += g_audio.o 58obj-$(CONFIG_USB_AUDIO) += g_audio.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
71obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o 72obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
72obj-$(CONFIG_USB_G_NCM) += g_ncm.o 73obj-$(CONFIG_USB_G_NCM) += g_ncm.o
73obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o 74obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
75obj-$(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
35static struct target_fabric_configfs *usbg_fabric_configfs;
36
37static inline struct f_uas *to_f_uas(struct usb_function *f)
38{
39 return container_of(f, struct f_uas, function);
40}
41
42static void usbg_cmd_release(struct kref *);
43
44static 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
51static 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
64static 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
79static 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
103static 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
126static 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
160static int bot_send_status(struct usbg_cmd *cmd, bool moved_data)
161{
162 struct f_uas *fu = cmd->fu;
163 struct bulk_cs_wrap *csw = &fu->bot_status.csw;
164 int ret;
165
166 if (cmd->se_cmd.scsi_status == SAM_STAT_GOOD) {
167 if (!moved_data && cmd->data_len) {
168 /*
169 * the host wants to move data, we don't. Fill / empty