1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Target based USB-Gadget |
3 | * |
4 | * UAS protocol handling, target callbacks, configfs handling, |
5 | * BBB (USB Mass Storage Class Bulk-Only (BBB) and Transport protocol handling. |
6 | * |
7 | * Author: Sebastian Andrzej Siewior <bigeasy at linutronix dot de> |
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_tcq.h> |
20 | #include <target/target_core_base.h> |
21 | #include <target/target_core_fabric.h> |
22 | #include <asm/unaligned.h> |
23 | |
24 | #include "u_tcm.h" |
25 | |
26 | USB_GADGET_COMPOSITE_OPTIONS(); |
27 | |
28 | #define UAS_VENDOR_ID 0x0525 /* NetChip */ |
29 | #define UAS_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */ |
30 | |
31 | static struct usb_device_descriptor usbg_device_desc = { |
32 | .bLength = sizeof(usbg_device_desc), |
33 | .bDescriptorType = USB_DT_DEVICE, |
34 | /* .bcdUSB = DYNAMIC */ |
35 | .bDeviceClass = USB_CLASS_PER_INTERFACE, |
36 | .idVendor = cpu_to_le16(UAS_VENDOR_ID), |
37 | .idProduct = cpu_to_le16(UAS_PRODUCT_ID), |
38 | .bNumConfigurations = 1, |
39 | }; |
40 | |
41 | #define USB_G_STR_CONFIG USB_GADGET_FIRST_AVAIL_IDX |
42 | |
43 | static struct usb_string usbg_us_strings[] = { |
44 | [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufacturer" , |
45 | [USB_GADGET_PRODUCT_IDX].s = "Target Product" , |
46 | [USB_GADGET_SERIAL_IDX].s = "000000000001" , |
47 | [USB_G_STR_CONFIG].s = "default config" , |
48 | { }, |
49 | }; |
50 | |
51 | static struct usb_gadget_strings usbg_stringtab = { |
52 | .language = 0x0409, |
53 | .strings = usbg_us_strings, |
54 | }; |
55 | |
56 | static struct usb_gadget_strings *usbg_strings[] = { |
57 | &usbg_stringtab, |
58 | NULL, |
59 | }; |
60 | |
61 | static struct usb_function_instance *fi_tcm; |
62 | static struct usb_function *f_tcm; |
63 | |
64 | static int guas_unbind(struct usb_composite_dev *cdev) |
65 | { |
66 | if (!IS_ERR_OR_NULL(ptr: f_tcm)) |
67 | usb_put_function(f: f_tcm); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | static int tcm_do_config(struct usb_configuration *c) |
73 | { |
74 | int status; |
75 | |
76 | f_tcm = usb_get_function(fi: fi_tcm); |
77 | if (IS_ERR(ptr: f_tcm)) |
78 | return PTR_ERR(ptr: f_tcm); |
79 | |
80 | status = usb_add_function(c, f_tcm); |
81 | if (status < 0) { |
82 | usb_put_function(f: f_tcm); |
83 | return status; |
84 | } |
85 | |
86 | return 0; |
87 | } |
88 | |
89 | static struct usb_configuration usbg_config_driver = { |
90 | .label = "Linux Target" , |
91 | .bConfigurationValue = 1, |
92 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, |
93 | }; |
94 | |
95 | static int usbg_attach(struct usb_function_instance *f); |
96 | static void usbg_detach(struct usb_function_instance *f); |
97 | |
98 | static int usb_target_bind(struct usb_composite_dev *cdev) |
99 | { |
100 | int ret; |
101 | |
102 | ret = usb_string_ids_tab(c: cdev, str: usbg_us_strings); |
103 | if (ret) |
104 | return ret; |
105 | |
106 | usbg_device_desc.iManufacturer = |
107 | usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id; |
108 | usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id; |
109 | usbg_device_desc.iSerialNumber = |
110 | usbg_us_strings[USB_GADGET_SERIAL_IDX].id; |
111 | usbg_config_driver.iConfiguration = |
112 | usbg_us_strings[USB_G_STR_CONFIG].id; |
113 | |
114 | ret = usb_add_config(cdev, &usbg_config_driver, tcm_do_config); |
115 | if (ret) |
116 | return ret; |
117 | usb_composite_overwrite_options(cdev, covr: &coverwrite); |
118 | return 0; |
119 | } |
120 | |
121 | static struct usb_composite_driver usbg_driver = { |
122 | .name = "g_target" , |
123 | .dev = &usbg_device_desc, |
124 | .strings = usbg_strings, |
125 | .max_speed = USB_SPEED_SUPER, |
126 | .bind = usb_target_bind, |
127 | .unbind = guas_unbind, |
128 | }; |
129 | |
130 | static int usbg_attach(struct usb_function_instance *f) |
131 | { |
132 | return usb_composite_probe(driver: &usbg_driver); |
133 | } |
134 | |
135 | static void usbg_detach(struct usb_function_instance *f) |
136 | { |
137 | usb_composite_unregister(driver: &usbg_driver); |
138 | } |
139 | |
140 | static int __init usb_target_gadget_init(void) |
141 | { |
142 | struct f_tcm_opts *tcm_opts; |
143 | |
144 | fi_tcm = usb_get_function_instance(name: "tcm" ); |
145 | if (IS_ERR(ptr: fi_tcm)) |
146 | return PTR_ERR(ptr: fi_tcm); |
147 | |
148 | tcm_opts = container_of(fi_tcm, struct f_tcm_opts, func_inst); |
149 | mutex_lock(&tcm_opts->dep_lock); |
150 | tcm_opts->tcm_register_callback = usbg_attach; |
151 | tcm_opts->tcm_unregister_callback = usbg_detach; |
152 | tcm_opts->dependent = THIS_MODULE; |
153 | tcm_opts->can_attach = true; |
154 | tcm_opts->has_dep = true; |
155 | mutex_unlock(lock: &tcm_opts->dep_lock); |
156 | |
157 | fi_tcm->set_inst_name(fi_tcm, "tcm-legacy" ); |
158 | |
159 | return 0; |
160 | } |
161 | module_init(usb_target_gadget_init); |
162 | |
163 | static void __exit usb_target_gadget_exit(void) |
164 | { |
165 | if (!IS_ERR_OR_NULL(ptr: fi_tcm)) |
166 | usb_put_function_instance(fi: fi_tcm); |
167 | |
168 | } |
169 | module_exit(usb_target_gadget_exit); |
170 | |
171 | MODULE_AUTHOR("Sebastian Andrzej Siewior <bigeasy@linutronix.de>" ); |
172 | MODULE_DESCRIPTION("usb-gadget fabric" ); |
173 | MODULE_LICENSE("GPL v2" ); |
174 | |