Commit | Line | Data |
---|---|---|
71adf118 FC |
1 | /* |
2 | * hid.c -- HID Composite driver | |
3 | * | |
4 | * Based on multi.c | |
5 | * | |
6 | * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com> | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
71adf118 FC |
12 | */ |
13 | ||
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/platform_device.h> | |
17 | #include <linux/list.h> | |
721e2e91 SAS |
18 | #include <linux/module.h> |
19 | #include <linux/usb/composite.h> | |
4bc8a33f | 20 | #include <linux/usb/g_hid.h> |
71adf118 FC |
21 | |
22 | #define DRIVER_DESC "HID Gadget" | |
23 | #define DRIVER_VERSION "2010/03/16" | |
24 | ||
4bc8a33f AP |
25 | #include "u_hid.h" |
26 | ||
71adf118 FC |
27 | /*-------------------------------------------------------------------------*/ |
28 | ||
29 | #define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */ | |
30 | #define HIDG_PRODUCT_NUM 0xa4ac /* Linux-USB HID gadget */ | |
31 | ||
32 | /*-------------------------------------------------------------------------*/ | |
33 | ||
71adf118 | 34 | struct hidg_func_node { |
4bc8a33f AP |
35 | struct usb_function_instance *fi; |
36 | struct usb_function *f; | |
71adf118 FC |
37 | struct list_head node; |
38 | struct hidg_func_descriptor *func; | |
39 | }; | |
40 | ||
41 | static LIST_HEAD(hidg_func_list); | |
42 | ||
43 | /*-------------------------------------------------------------------------*/ | |
7d16e8d3 | 44 | USB_GADGET_COMPOSITE_OPTIONS(); |
71adf118 FC |
45 | |
46 | static struct usb_device_descriptor device_desc = { | |
47 | .bLength = sizeof device_desc, | |
48 | .bDescriptorType = USB_DT_DEVICE, | |
49 | ||
50 | .bcdUSB = cpu_to_le16(0x0200), | |
51 | ||
52 | /* .bDeviceClass = USB_CLASS_COMM, */ | |
53 | /* .bDeviceSubClass = 0, */ | |
54 | /* .bDeviceProtocol = 0, */ | |
33d2832a OF |
55 | .bDeviceClass = USB_CLASS_PER_INTERFACE, |
56 | .bDeviceSubClass = 0, | |
57 | .bDeviceProtocol = 0, | |
71adf118 FC |
58 | /* .bMaxPacketSize0 = f(hardware) */ |
59 | ||
60 | /* Vendor and product id can be overridden by module parameters. */ | |
61 | .idVendor = cpu_to_le16(HIDG_VENDOR_NUM), | |
62 | .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM), | |
63 | /* .bcdDevice = f(hardware) */ | |
64 | /* .iManufacturer = DYNAMIC */ | |
65 | /* .iProduct = DYNAMIC */ | |
66 | /* NO SERIAL NUMBER */ | |
67 | .bNumConfigurations = 1, | |
68 | }; | |
69 | ||
d9e18679 | 70 | static const struct usb_descriptor_header *otg_desc[2]; |
71adf118 FC |
71 | |
72 | /* string IDs are assigned dynamically */ | |
71adf118 | 73 | static struct usb_string strings_dev[] = { |
cc2683c3 | 74 | [USB_GADGET_MANUFACTURER_IDX].s = "", |
276e2e4f SAS |
75 | [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC, |
76 | [USB_GADGET_SERIAL_IDX].s = "", | |
71adf118 FC |
77 | { } /* end of list */ |
78 | }; | |
79 | ||
80 | static struct usb_gadget_strings stringtab_dev = { | |
81 | .language = 0x0409, /* en-us */ | |
82 | .strings = strings_dev, | |
83 | }; | |
84 | ||
85 | static struct usb_gadget_strings *dev_strings[] = { | |
86 | &stringtab_dev, | |
87 | NULL, | |
88 | }; | |
89 | ||
90 | ||
91 | ||
92 | /****************************** Configurations ******************************/ | |
93 | ||
c94e289f | 94 | static int do_config(struct usb_configuration *c) |
71adf118 | 95 | { |
4bc8a33f AP |
96 | struct hidg_func_node *e, *n; |
97 | int status = 0; | |
71adf118 FC |
98 | |
99 | if (gadget_is_otg(c->cdev->gadget)) { | |
100 | c->descriptors = otg_desc; | |
101 | c->bmAttributes |= USB_CONFIG_ATT_WAKEUP; | |
102 | } | |
103 | ||
104 | list_for_each_entry(e, &hidg_func_list, node) { | |
4bc8a33f AP |
105 | e->f = usb_get_function(e->fi); |
106 | if (IS_ERR(e->f)) | |
107 | goto put; | |
108 | status = usb_add_function(c, e->f); | |
109 | if (status < 0) { | |
110 | usb_put_function(e->f); | |
111 | goto put; | |
112 | } | |
71adf118 FC |
113 | } |
114 | ||
4bc8a33f AP |
115 | return 0; |
116 | put: | |
117 | list_for_each_entry(n, &hidg_func_list, node) { | |
118 | if (n == e) | |
119 | break; | |
120 | usb_remove_function(c, n->f); | |
121 | usb_put_function(n->f); | |
122 | } | |
71adf118 FC |
123 | return status; |
124 | } | |
125 | ||
126 | static struct usb_configuration config_driver = { | |
127 | .label = "HID Gadget", | |
71adf118 FC |
128 | .bConfigurationValue = 1, |
129 | /* .iConfiguration = DYNAMIC */ | |
130 | .bmAttributes = USB_CONFIG_ATT_SELFPOWER, | |
131 | }; | |
132 | ||
133 | /****************************** Gadget Bind ******************************/ | |
134 | ||
c94e289f | 135 | static int hid_bind(struct usb_composite_dev *cdev) |
71adf118 FC |
136 | { |
137 | struct usb_gadget *gadget = cdev->gadget; | |
138 | struct list_head *tmp; | |
4bc8a33f AP |
139 | struct hidg_func_node *n, *m; |
140 | struct f_hid_opts *hid_opts; | |
ed9cbda6 | 141 | int status, funcs = 0; |
71adf118 FC |
142 | |
143 | list_for_each(tmp, &hidg_func_list) | |
144 | funcs++; | |
145 | ||
146 | if (!funcs) | |
147 | return -ENODEV; | |
148 | ||
4bc8a33f AP |
149 | list_for_each_entry(n, &hidg_func_list, node) { |
150 | n->fi = usb_get_function_instance("hid"); | |
151 | if (IS_ERR(n->fi)) { | |
152 | status = PTR_ERR(n->fi); | |
153 | goto put; | |
154 | } | |
155 | hid_opts = container_of(n->fi, struct f_hid_opts, func_inst); | |
156 | hid_opts->subclass = n->func->subclass; | |
157 | hid_opts->protocol = n->func->protocol; | |
158 | hid_opts->report_length = n->func->report_length; | |
159 | hid_opts->report_desc_length = n->func->report_desc_length; | |
160 | hid_opts->report_desc = n->func->report_desc; | |
161 | } | |
162 | ||
71adf118 | 163 | |
71adf118 FC |
164 | /* Allocate string descriptor numbers ... note that string |
165 | * contents can be overridden by the composite_dev glue. | |
166 | */ | |
167 | ||
e1f15ccb | 168 | status = usb_string_ids_tab(cdev, strings_dev); |
71adf118 | 169 | if (status < 0) |
4bc8a33f | 170 | goto put; |
276e2e4f SAS |
171 | device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id; |
172 | device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id; | |
71adf118 | 173 | |
d9e18679 LJ |
174 | if (gadget_is_otg(gadget) && !otg_desc[0]) { |
175 | struct usb_descriptor_header *usb_desc; | |
176 | ||
177 | usb_desc = usb_otg_descriptor_alloc(gadget); | |
178 | if (!usb_desc) | |
179 | goto put; | |
180 | usb_otg_descriptor_init(gadget, usb_desc); | |
181 | otg_desc[0] = usb_desc; | |
182 | otg_desc[1] = NULL; | |
183 | } | |
184 | ||
71adf118 | 185 | /* register our configuration */ |
c9bfff9c | 186 | status = usb_add_config(cdev, &config_driver, do_config); |
71adf118 | 187 | if (status < 0) |
d9e18679 | 188 | goto free_otg_desc; |
71adf118 | 189 | |
7d16e8d3 | 190 | usb_composite_overwrite_options(cdev, &coverwrite); |
71adf118 FC |
191 | dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); |
192 | ||
193 | return 0; | |
4bc8a33f | 194 | |
d9e18679 LJ |
195 | free_otg_desc: |
196 | kfree(otg_desc[0]); | |
197 | otg_desc[0] = NULL; | |
4bc8a33f AP |
198 | put: |
199 | list_for_each_entry(m, &hidg_func_list, node) { | |
200 | if (m == n) | |
201 | break; | |
202 | usb_put_function_instance(m->fi); | |
203 | } | |
204 | return status; | |
71adf118 FC |
205 | } |
206 | ||
c94e289f | 207 | static int hid_unbind(struct usb_composite_dev *cdev) |
71adf118 | 208 | { |
4bc8a33f AP |
209 | struct hidg_func_node *n; |
210 | ||
211 | list_for_each_entry(n, &hidg_func_list, node) { | |
212 | usb_put_function(n->f); | |
213 | usb_put_function_instance(n->fi); | |
214 | } | |
d9e18679 LJ |
215 | |
216 | kfree(otg_desc[0]); | |
217 | otg_desc[0] = NULL; | |
218 | ||
71adf118 FC |
219 | return 0; |
220 | } | |
221 | ||
c94e289f | 222 | static int hidg_plat_driver_probe(struct platform_device *pdev) |
71adf118 | 223 | { |
e01ee9f5 | 224 | struct hidg_func_descriptor *func = dev_get_platdata(&pdev->dev); |
71adf118 FC |
225 | struct hidg_func_node *entry; |
226 | ||
227 | if (!func) { | |
228 | dev_err(&pdev->dev, "Platform data missing\n"); | |
229 | return -ENODEV; | |
230 | } | |
231 | ||
232 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | |
233 | if (!entry) | |
234 | return -ENOMEM; | |
235 | ||
236 | entry->func = func; | |
237 | list_add_tail(&entry->node, &hidg_func_list); | |
238 | ||
239 | return 0; | |
240 | } | |
241 | ||
fb4e98ab | 242 | static int hidg_plat_driver_remove(struct platform_device *pdev) |
71adf118 FC |
243 | { |
244 | struct hidg_func_node *e, *n; | |
245 | ||
246 | list_for_each_entry_safe(e, n, &hidg_func_list, node) { | |
247 | list_del(&e->node); | |
248 | kfree(e); | |
249 | } | |
250 | ||
251 | return 0; | |
252 | } | |
253 | ||
254 | ||
255 | /****************************** Some noise ******************************/ | |
256 | ||
257 | ||
c94e289f | 258 | static struct usb_composite_driver hidg_driver = { |
71adf118 FC |
259 | .name = "g_hid", |
260 | .dev = &device_desc, | |
261 | .strings = dev_strings, | |
35a0e0bf | 262 | .max_speed = USB_SPEED_HIGH, |
03e42bd5 | 263 | .bind = hid_bind, |
c94e289f | 264 | .unbind = hid_unbind, |
71adf118 FC |
265 | }; |
266 | ||
267 | static struct platform_driver hidg_plat_driver = { | |
7690417d | 268 | .remove = hidg_plat_driver_remove, |
71adf118 | 269 | .driver = { |
71adf118 FC |
270 | .name = "hidg", |
271 | }, | |
272 | }; | |
273 | ||
274 | ||
275 | MODULE_DESCRIPTION(DRIVER_DESC); | |
276 | MODULE_AUTHOR("Fabien Chouteau, Peter Korsgaard"); | |
277 | MODULE_LICENSE("GPL"); | |
278 | ||
279 | static int __init hidg_init(void) | |
280 | { | |
da01c7a4 PK |
281 | int status; |
282 | ||
283 | status = platform_driver_probe(&hidg_plat_driver, | |
284 | hidg_plat_driver_probe); | |
285 | if (status < 0) | |
286 | return status; | |
287 | ||
03e42bd5 | 288 | status = usb_composite_probe(&hidg_driver); |
da01c7a4 PK |
289 | if (status < 0) |
290 | platform_driver_unregister(&hidg_plat_driver); | |
291 | ||
292 | return status; | |
71adf118 FC |
293 | } |
294 | module_init(hidg_init); | |
295 | ||
296 | static void __exit hidg_cleanup(void) | |
297 | { | |
71adf118 | 298 | usb_composite_unregister(&hidg_driver); |
00896f66 | 299 | platform_driver_unregister(&hidg_plat_driver); |
71adf118 FC |
300 | } |
301 | module_exit(hidg_cleanup); |