diff options
Diffstat (limited to 'drivers/usb/gadget/udc-core.c')
-rw-r--r-- | drivers/usb/gadget/udc-core.c | 90 |
1 files changed, 80 insertions, 10 deletions
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c index f8f62c3ed65e..ffd8fa541101 100644 --- a/drivers/usb/gadget/udc-core.c +++ b/drivers/usb/gadget/udc-core.c | |||
@@ -101,6 +101,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); | |||
101 | 101 | ||
102 | /* ------------------------------------------------------------------------- */ | 102 | /* ------------------------------------------------------------------------- */ |
103 | 103 | ||
104 | void usb_gadget_set_state(struct usb_gadget *gadget, | ||
105 | enum usb_device_state state) | ||
106 | { | ||
107 | gadget->state = state; | ||
108 | sysfs_notify(&gadget->dev.kobj, NULL, "status"); | ||
109 | } | ||
110 | EXPORT_SYMBOL_GPL(usb_gadget_set_state); | ||
111 | |||
112 | /* ------------------------------------------------------------------------- */ | ||
113 | |||
104 | /** | 114 | /** |
105 | * usb_gadget_udc_start - tells usb device controller to start up | 115 | * usb_gadget_udc_start - tells usb device controller to start up |
106 | * @gadget: The gadget we want to get started | 116 | * @gadget: The gadget we want to get started |
@@ -156,15 +166,23 @@ static void usb_udc_release(struct device *dev) | |||
156 | } | 166 | } |
157 | 167 | ||
158 | static const struct attribute_group *usb_udc_attr_groups[]; | 168 | static const struct attribute_group *usb_udc_attr_groups[]; |
169 | |||
170 | static void usb_udc_nop_release(struct device *dev) | ||
171 | { | ||
172 | dev_vdbg(dev, "%s\n", __func__); | ||
173 | } | ||
174 | |||
159 | /** | 175 | /** |
160 | * usb_add_gadget_udc - adds a new gadget to the udc class driver list | 176 | * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list |
161 | * @parent: the parent device to this udc. Usually the controller | 177 | * @parent: the parent device to this udc. Usually the controller driver's |
162 | * driver's device. | 178 | * device. |
163 | * @gadget: the gadget to be added to the list | 179 | * @gadget: the gadget to be added to the list. |
180 | * @release: a gadget release function. | ||
164 | * | 181 | * |
165 | * Returns zero on success, negative errno otherwise. | 182 | * Returns zero on success, negative errno otherwise. |
166 | */ | 183 | */ |
167 | int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) | 184 | int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, |
185 | void (*release)(struct device *dev)) | ||
168 | { | 186 | { |
169 | struct usb_udc *udc; | 187 | struct usb_udc *udc; |
170 | int ret = -ENOMEM; | 188 | int ret = -ENOMEM; |
@@ -173,6 +191,22 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) | |||
173 | if (!udc) | 191 | if (!udc) |
174 | goto err1; | 192 | goto err1; |
175 | 193 | ||
194 | dev_set_name(&gadget->dev, "gadget"); | ||
195 | gadget->dev.parent = parent; | ||
196 | |||
197 | dma_set_coherent_mask(&gadget->dev, parent->coherent_dma_mask); | ||
198 | gadget->dev.dma_parms = parent->dma_parms; | ||
199 | gadget->dev.dma_mask = parent->dma_mask; | ||
200 | |||
201 | if (release) | ||
202 | gadget->dev.release = release; | ||
203 | else | ||
204 | gadget->dev.release = usb_udc_nop_release; | ||
205 | |||
206 | ret = device_register(&gadget->dev); | ||
207 | if (ret) | ||
208 | goto err2; | ||
209 | |||
176 | device_initialize(&udc->dev); | 210 | device_initialize(&udc->dev); |
177 | udc->dev.release = usb_udc_release; | 211 | udc->dev.release = usb_udc_release; |
178 | udc->dev.class = udc_class; | 212 | udc->dev.class = udc_class; |
@@ -180,7 +214,7 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) | |||
180 | udc->dev.parent = parent; | 214 | udc->dev.parent = parent; |
181 | ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); | 215 | ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); |
182 | if (ret) | 216 | if (ret) |
183 | goto err2; | 217 | goto err3; |
184 | 218 | ||
185 | udc->gadget = gadget; | 219 | udc->gadget = gadget; |
186 | 220 | ||
@@ -189,21 +223,42 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) | |||
189 | 223 | ||
190 | ret = device_add(&udc->dev); | 224 | ret = device_add(&udc->dev); |
191 | if (ret) | 225 | if (ret) |
192 | goto err3; | 226 | goto err4; |
227 | |||
228 | usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); | ||
193 | 229 | ||
194 | mutex_unlock(&udc_lock); | 230 | mutex_unlock(&udc_lock); |
195 | 231 | ||
196 | return 0; | 232 | return 0; |
197 | err3: | 233 | |
234 | err4: | ||
198 | list_del(&udc->list); | 235 | list_del(&udc->list); |
199 | mutex_unlock(&udc_lock); | 236 | mutex_unlock(&udc_lock); |
200 | 237 | ||
201 | err2: | 238 | err3: |
202 | put_device(&udc->dev); | 239 | put_device(&udc->dev); |
203 | 240 | ||
241 | err2: | ||
242 | put_device(&gadget->dev); | ||
243 | kfree(udc); | ||
244 | |||
204 | err1: | 245 | err1: |
205 | return ret; | 246 | return ret; |
206 | } | 247 | } |
248 | EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); | ||
249 | |||
250 | /** | ||
251 | * usb_add_gadget_udc - adds a new gadget to the udc class driver list | ||
252 | * @parent: the parent device to this udc. Usually the controller | ||
253 | * driver's device. | ||
254 | * @gadget: the gadget to be added to the list | ||
255 | * | ||
256 | * Returns zero on success, negative errno otherwise. | ||
257 | */ | ||
258 | int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) | ||
259 | { | ||
260 | return usb_add_gadget_udc_release(parent, gadget, NULL); | ||
261 | } | ||
207 | EXPORT_SYMBOL_GPL(usb_add_gadget_udc); | 262 | EXPORT_SYMBOL_GPL(usb_add_gadget_udc); |
208 | 263 | ||
209 | static void usb_gadget_remove_driver(struct usb_udc *udc) | 264 | static void usb_gadget_remove_driver(struct usb_udc *udc) |
@@ -220,6 +275,7 @@ static void usb_gadget_remove_driver(struct usb_udc *udc) | |||
220 | 275 | ||
221 | udc->driver = NULL; | 276 | udc->driver = NULL; |
222 | udc->dev.driver = NULL; | 277 | udc->dev.driver = NULL; |
278 | udc->gadget->dev.driver = NULL; | ||
223 | } | 279 | } |
224 | 280 | ||
225 | /** | 281 | /** |
@@ -254,6 +310,7 @@ found: | |||
254 | 310 | ||
255 | kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); | 311 | kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); |
256 | device_unregister(&udc->dev); | 312 | device_unregister(&udc->dev); |
313 | device_unregister(&gadget->dev); | ||
257 | } | 314 | } |
258 | EXPORT_SYMBOL_GPL(usb_del_gadget_udc); | 315 | EXPORT_SYMBOL_GPL(usb_del_gadget_udc); |
259 | 316 | ||
@@ -268,6 +325,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri | |||
268 | 325 | ||
269 | udc->driver = driver; | 326 | udc->driver = driver; |
270 | udc->dev.driver = &driver->driver; | 327 | udc->dev.driver = &driver->driver; |
328 | udc->gadget->dev.driver = &driver->driver; | ||
271 | 329 | ||
272 | ret = driver->bind(udc->gadget, driver); | 330 | ret = driver->bind(udc->gadget, driver); |
273 | if (ret) | 331 | if (ret) |
@@ -286,6 +344,7 @@ err1: | |||
286 | udc->driver->function, ret); | 344 | udc->driver->function, ret); |
287 | udc->driver = NULL; | 345 | udc->driver = NULL; |
288 | udc->dev.driver = NULL; | 346 | udc->dev.driver = NULL; |
347 | udc->gadget->dev.driver = NULL; | ||
289 | return ret; | 348 | return ret; |
290 | } | 349 | } |
291 | 350 | ||
@@ -395,6 +454,16 @@ static ssize_t usb_udc_softconn_store(struct device *dev, | |||
395 | } | 454 | } |
396 | static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); | 455 | static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); |
397 | 456 | ||
457 | static ssize_t usb_gadget_state_show(struct device *dev, | ||
458 | struct device_attribute *attr, char *buf) | ||
459 | { | ||
460 | struct usb_udc *udc = container_of(dev, struct usb_udc, dev); | ||
461 | struct usb_gadget *gadget = udc->gadget; | ||
462 | |||
463 | return sprintf(buf, "%s\n", usb_state_string(gadget->state)); | ||
464 | } | ||
465 | static DEVICE_ATTR(state, S_IRUGO, usb_gadget_state_show, NULL); | ||
466 | |||
398 | #define USB_UDC_SPEED_ATTR(name, param) \ | 467 | #define USB_UDC_SPEED_ATTR(name, param) \ |
399 | ssize_t usb_udc_##param##_show(struct device *dev, \ | 468 | ssize_t usb_udc_##param##_show(struct device *dev, \ |
400 | struct device_attribute *attr, char *buf) \ | 469 | struct device_attribute *attr, char *buf) \ |
@@ -403,7 +472,7 @@ ssize_t usb_udc_##param##_show(struct device *dev, \ | |||
403 | return snprintf(buf, PAGE_SIZE, "%s\n", \ | 472 | return snprintf(buf, PAGE_SIZE, "%s\n", \ |
404 | usb_speed_string(udc->gadget->param)); \ | 473 | usb_speed_string(udc->gadget->param)); \ |
405 | } \ | 474 | } \ |
406 | static DEVICE_ATTR(name, S_IRUSR, usb_udc_##param##_show, NULL) | 475 | static DEVICE_ATTR(name, S_IRUGO, usb_udc_##param##_show, NULL) |
407 | 476 | ||
408 | static USB_UDC_SPEED_ATTR(current_speed, speed); | 477 | static USB_UDC_SPEED_ATTR(current_speed, speed); |
409 | static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); | 478 | static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); |
@@ -428,6 +497,7 @@ static USB_UDC_ATTR(a_alt_hnp_support); | |||
428 | static struct attribute *usb_udc_attrs[] = { | 497 | static struct attribute *usb_udc_attrs[] = { |
429 | &dev_attr_srp.attr, | 498 | &dev_attr_srp.attr, |
430 | &dev_attr_soft_connect.attr, | 499 | &dev_attr_soft_connect.attr, |
500 | &dev_attr_state.attr, | ||
431 | &dev_attr_current_speed.attr, | 501 | &dev_attr_current_speed.attr, |
432 | &dev_attr_maximum_speed.attr, | 502 | &dev_attr_maximum_speed.attr, |
433 | 503 | ||