aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/udc-core.c
diff options
context:
space:
mode:
authorFelipe Balbi <balbi@ti.com>2011-06-28 09:33:46 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-06-28 14:12:51 -0400
commit2ccea03a8f7ec93641791f2760d7cdc6cab6205f (patch)
tree2bdc0eae68d899420769584d64fa8282dfe1424b /drivers/usb/gadget/udc-core.c
parent664a51a81f6ba39db30cd7b7de61577ca0b2d20d (diff)
usb: gadget: introduce UDC Class
this class will be used to abstract away several of the duplicated operations scattered among the USB gadget controller drivers. Later, we can add an atomic notifier to tell interested drivers about what's happening with the controller. Notifications such as suspend, resume, enumerated, etc. will be useful, at a minimum, for implementing usb charger detection. As part of the converting process usb_gadget_probe_driver() is no longer part of each udc but pushed into the ->stap() callback. The same for his couterpart. The core is currently set explicit to 'n'. It will be changed to 'y' once all users are converted since it provides functions which clash with other drivers. Signed-off-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Acked-by: Michal Nazarewicz <mina86@mina86.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/gadget/udc-core.c')
-rw-r--r--drivers/usb/gadget/udc-core.c418
1 files changed, 418 insertions, 0 deletions
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
new file mode 100644
index 00000000000..83e9e5f99b4
--- /dev/null
+++ b/drivers/usb/gadget/udc-core.c
@@ -0,0 +1,418 @@
1/**
2 * udc.c - Core UDC Framework
3 *
4 * Copyright (C) 2010 Texas Instruments
5 * Author: Felipe Balbi <balbi@ti.com>
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 of
9 * the License as published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include <linux/kernel.h>
21#include <linux/module.h>
22#include <linux/device.h>
23#include <linux/list.h>
24#include <linux/err.h>
25
26#include <linux/usb/ch9.h>
27#include <linux/usb/gadget.h>
28
29/**
30 * struct usb_udc - describes one usb device controller
31 * @driver - the gadget driver pointer. For use by the class code
32 * @dev - the child device to the actual controller
33 * @gadget - the gadget. For use by the class code
34 * @list - for use by the udc class driver
35 *
36 * This represents the internal data structure which is used by the UDC-class
37 * to hold information about udc driver and gadget together.
38 */
39struct usb_udc {
40 struct usb_gadget_driver *driver;
41 struct usb_gadget *gadget;
42 struct device dev;
43 struct list_head list;
44};
45
46static struct class *udc_class;
47static struct device_type udc_device_type;
48static LIST_HEAD(udc_list);
49static DEFINE_MUTEX(udc_lock);
50
51/* ------------------------------------------------------------------------- */
52
53/**
54 * usb_gadget_start - tells usb device controller to start up
55 * @gadget: The gadget we want to get started
56 * @driver: The driver we want to bind to @gadget
57 * @bind: The bind function for @driver
58 *
59 * This call is issued by the UDC Class driver when it's about
60 * to register a gadget driver to the device controller, before
61 * calling gadget driver's bind() method.
62 *
63 * It allows the controller to be powered off until strictly
64 * necessary to have it powered on.
65 *
66 * Returns zero on success, else negative errno.
67 */
68static inline int usb_gadget_start(struct usb_gadget *gadget,
69 struct usb_gadget_driver *driver,
70 int (*bind)(struct usb_gadget *))
71{
72 return gadget->ops->start(driver, bind);
73}
74
75/**
76 * usb_gadget_stop - tells usb device controller we don't need it anymore
77 * @gadget: The device we want to stop activity
78 * @driver: The driver to unbind from @gadget
79 *
80 * This call is issued by the UDC Class driver after calling
81 * gadget driver's unbind() method.
82 *
83 * The details are implementation specific, but it can go as
84 * far as powering off UDC completely and disable its data
85 * line pullups.
86 */
87static inline void usb_gadget_stop(struct usb_gadget *gadget,
88 struct usb_gadget_driver *driver)
89{
90 gadget->ops->stop(driver);
91}
92
93/**
94 * usb_udc_release - release the usb_udc struct
95 * @dev: the dev member within usb_udc
96 *
97 * This is called by driver's core in order to free memory once the last
98 * reference is released.
99 */
100static void usb_udc_release(struct device *dev)
101{
102 struct usb_udc *udc;
103
104 udc = container_of(dev, struct usb_udc, dev);
105 dev_dbg(dev, "releasing '%s'\n", dev_name(dev));
106 kfree(udc);
107}
108
109/**
110 * usb_add_gadget_udc - adds a new gadget to the udc class driver list
111 * @parent: the parent device to this udc. Usually the controller
112 * driver's device.
113 * @gadget: the gadget to be added to the list
114 *
115 * Returns zero on success, negative errno otherwise.
116 */
117int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
118{
119 struct usb_udc *udc;
120 int ret = -ENOMEM;
121
122 udc = kzalloc(sizeof(*udc), GFP_KERNEL);
123 if (!udc)
124 goto err1;
125
126 device_initialize(&udc->dev);
127 udc->dev.release = usb_udc_release;
128 udc->dev.class = udc_class;
129 udc->dev.parent = parent;
130 ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj));
131 if (ret)
132 goto err2;
133
134 udc->gadget = gadget;
135
136 mutex_lock(&udc_lock);
137 list_add_tail(&udc->list, &udc_list);
138
139 ret = device_add(&udc->dev);
140 if (ret)
141 goto err3;
142
143 mutex_unlock(&udc_lock);
144
145 return 0;
146err3:
147 list_del(&udc->list);
148 mutex_unlock(&udc_lock);
149
150err2:
151 put_device(&udc->dev);
152
153err1:
154 return ret;
155}
156EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
157
158static void usb_gadget_remove_driver(struct usb_udc *udc)
159{
160 dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
161 udc->gadget->name);
162
163 kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
164
165 usb_gadget_stop(udc->gadget, udc->driver);
166
167 udc->driver = NULL;
168 udc->dev.driver = NULL;
169}
170
171/**
172 * usb_del_gadget_udc - deletes @udc from udc_list
173 * @gadget: the gadget to be removed.
174 *
175 * This, will call usb_gadget_unregister_driver() if
176 * the @udc is still busy.
177 */
178void usb_del_gadget_udc(struct usb_gadget *gadget)
179{
180 struct usb_udc *udc = NULL;
181
182 mutex_lock(&udc_lock);
183 list_for_each_entry(udc, &udc_list, list)
184 if (udc->gadget == gadget)
185 goto found;
186
187 dev_err(gadget->dev.parent, "gadget not registered.\n");
188 mutex_unlock(&udc_lock);
189
190 return;
191
192found:
193 dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
194
195 list_del(&udc->list);
196 mutex_unlock(&udc_lock);
197
198 if (udc->driver)
199 usb_gadget_remove_driver(udc);
200
201 kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
202 device_unregister(&udc->dev);
203}
204EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
205
206/* ------------------------------------------------------------------------- */
207
208int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
209 int (*bind)(struct usb_gadget *))
210{
211 struct usb_udc *udc = NULL;
212 int ret;
213
214 if (!driver || !bind || !driver->setup)
215 return -EINVAL;
216
217 mutex_lock(&udc_lock);
218 list_for_each_entry(udc, &udc_list, list) {
219 /* For now we take the first one */
220 if (!udc->driver)
221 goto found;
222 }
223
224 pr_debug("couldn't find an available UDC\n");
225 mutex_unlock(&udc_lock);
226 return -ENODEV;
227
228found:
229 dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
230 driver->function);
231
232 udc->driver = driver;
233 udc->dev.driver = &driver->driver;
234
235 ret = usb_gadget_start(udc->gadget, driver, bind);
236 if (ret)
237 goto err1;
238
239 kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
240 mutex_unlock(&udc_lock);
241 return 0;
242
243err1:
244 dev_err(&udc->dev, "failed to start %s: %d\n",
245 udc->driver->function, ret);
246 udc->driver = NULL;
247 udc->dev.driver = NULL;
248 mutex_unlock(&udc_lock);
249 return ret;
250}
251EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
252
253int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
254{
255 struct usb_udc *udc = NULL;
256 int ret = -ENODEV;
257
258 if (!driver || !driver->unbind)
259 return -EINVAL;
260
261 mutex_lock(&udc_lock);
262 list_for_each_entry(udc, &udc_list, list)
263 if (udc->driver == driver) {
264 usb_gadget_remove_driver(udc);
265 ret = 0;
266 break;
267 }
268
269 mutex_unlock(&udc_lock);
270 return ret;
271}
272EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
273
274/* ------------------------------------------------------------------------- */
275
276static ssize_t usb_udc_srp_store(struct device *dev,
277 struct device_attribute *attr, const char *buf, size_t n)
278{
279 struct usb_udc *udc = dev_get_drvdata(dev);
280
281 if (sysfs_streq(buf, "1"))
282 usb_gadget_wakeup(udc->gadget);
283
284 return n;
285}
286static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store);
287
288static ssize_t usb_udc_softconn_store(struct device *dev,
289 struct device_attribute *attr, const char *buf, size_t n)
290{
291 struct usb_udc *udc = dev_get_drvdata(dev);
292
293 if (sysfs_streq(buf, "connect")) {
294 usb_gadget_connect(udc->gadget);
295 } else if (sysfs_streq(buf, "disconnect")) {
296 usb_gadget_disconnect(udc->gadget);
297 } else {
298 dev_err(dev, "unsupported command '%s'\n", buf);
299 return -EINVAL;
300 }
301
302 return n;
303}
304static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store);
305
306static ssize_t usb_udc_speed_show(struct device *dev,
307 struct device_attribute *attr, char *buf)
308{
309 struct usb_udc *udc = dev_get_drvdata(dev);
310 struct usb_gadget *gadget = udc->gadget;
311
312 switch (gadget->speed) {
313 case USB_SPEED_LOW:
314 return snprintf(buf, PAGE_SIZE, "low-speed\n");
315 case USB_SPEED_FULL:
316 return snprintf(buf, PAGE_SIZE, "full-speed\n");
317 case USB_SPEED_HIGH:
318 return snprintf(buf, PAGE_SIZE, "high-speed\n");
319 case USB_SPEED_WIRELESS:
320 return snprintf(buf, PAGE_SIZE, "wireless\n");
321 case USB_SPEED_SUPER:
322 return snprintf(buf, PAGE_SIZE, "super-speed\n");
323 case USB_SPEED_UNKNOWN: /* FALLTHROUGH */
324 default:
325 return snprintf(buf, PAGE_SIZE, "UNKNOWN\n");
326 }
327}
328static DEVICE_ATTR(speed, S_IRUSR, usb_udc_speed_show, NULL);
329
330#define USB_UDC_ATTR(name) \
331ssize_t usb_udc_##name##_show(struct device *dev, \
332 struct device_attribute *attr, char *buf) \
333{ \
334 struct usb_udc *udc = dev_get_drvdata(dev); \
335 struct usb_gadget *gadget = udc->gadget; \
336 \
337 return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \
338} \
339static DEVICE_ATTR(name, S_IRUSR, usb_udc_##name##_show, NULL)
340
341static USB_UDC_ATTR(is_dualspeed);
342static USB_UDC_ATTR(is_otg);
343static USB_UDC_ATTR(is_a_peripheral);
344static USB_UDC_ATTR(b_hnp_enable);
345static USB_UDC_ATTR(a_hnp_support);
346static USB_UDC_ATTR(a_alt_hnp_support);
347
348static struct attribute *usb_udc_attrs[] = {
349 &dev_attr_srp.attr,
350 &dev_attr_soft_connect.attr,
351 &dev_attr_speed.attr,
352
353 &dev_attr_is_dualspeed.attr,
354 &dev_attr_is_otg.attr,
355 &dev_attr_is_a_peripheral.attr,
356 &dev_attr_b_hnp_enable.attr,
357 &dev_attr_a_hnp_support.attr,
358 &dev_attr_a_alt_hnp_support.attr,
359 NULL,
360};
361
362static const struct attribute_group usb_udc_attr_group = {
363 .attrs = usb_udc_attrs,
364};
365
366static const struct attribute_group *usb_udc_attr_groups[] = {
367 &usb_udc_attr_group,
368 NULL,
369};
370
371static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env)
372{
373 struct usb_udc *udc = container_of(dev, struct usb_udc, dev);
374 int ret;
375
376 ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name);
377 if (ret) {
378 dev_err(dev, "failed to add uevent USB_UDC_NAME\n");
379 return ret;
380 }
381
382 if (udc->driver) {
383 ret = add_uevent_var(env, "USB_UDC_DRIVER=%s",
384 udc->driver->function);
385 if (ret) {
386 dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n");
387 return ret;
388 }
389 }
390
391 return 0;
392}
393
394static int __init usb_udc_init(void)
395{
396 udc_class = class_create(THIS_MODULE, "udc");
397 if (IS_ERR(udc_class)) {
398 pr_err("failed to create udc class --> %ld\n",
399 PTR_ERR(udc_class));
400 return PTR_ERR(udc_class);
401 }
402
403 udc_class->dev_uevent = usb_udc_uevent;
404 udc_device_type.groups = usb_udc_attr_groups;
405
406 return 0;
407}
408subsys_initcall(usb_udc_init);
409
410static void __exit usb_udc_exit(void)
411{
412 class_destroy(udc_class);
413}
414module_exit(usb_udc_exit);
415
416MODULE_DESCRIPTION("UDC Framework");
417MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
418MODULE_LICENSE("GPL v2");