| 1 | |
| 2 | |
| 3 | * Copyright (C) 2015 Karol Kosik <[email protected]>
|
| 4 | * Copyright (C) 2015-2016 Samsung Electronics
|
| 5 | * Igor Kotrasinski <[email protected]>
|
| 6 | * Krzysztof Opasiak <[email protected]>
|
| 7 |
|
| 8 |
|
| 9 | #include <linux/device.h>
|
| 10 | #include <linux/kernel.h>
|
| 11 | #include <linux/list.h>
|
| 12 | #include <linux/platform_device.h>
|
| 13 | #include <linux/usb.h>
|
| 14 | #include <linux/usb/gadget.h>
|
| 15 | #include <linux/usb/hcd.h>
|
| 16 | #include <linux/kthread.h>
|
| 17 | #include <linux/file.h>
|
| 18 | #include <linux/byteorder/generic.h>
|
| 19 |
|
| 20 | #include "usbip_common.h"
|
| 21 | #include "vudc.h"
|
| 22 |
|
| 23 | #define VIRTUAL_ENDPOINTS (1 + 15 + 15 )
|
| 24 |
|
| 25 |
|
| 26 |
|
| 27 |
|
| 28 | static void free_urb(struct urb *urb)
|
| 29 | {
|
| 30 | if (!urb)
|
| 31 | return;
|
| 32 |
|
| 33 | kfree(urb->setup_packet);
|
| 34 | urb->setup_packet = NULL;
|
| 35 |
|
| 36 | kfree(urb->transfer_buffer);
|
| 37 | urb->transfer_buffer = NULL;
|
| 38 |
|
| 39 | usb_free_urb(urb);
|
| 40 | }
|
| 41 |
|
| 42 | struct urbp *alloc_urbp(void)
|
| 43 | {
|
| 44 | struct urbp *urb_p;
|
| 45 |
|
| 46 | urb_p = kzalloc_obj(*urb_p);
|
| 47 | if (!urb_p)
|
| 48 | return urb_p;
|
| 49 |
|
| 50 | urb_p->urb = NULL;
|
| 51 | urb_p->ep = NULL;
|
| 52 | INIT_LIST_HEAD(&urb_p->urb_entry);
|
| 53 | return urb_p;
|
| 54 | }
|
| 55 |
|
| 56 | static void free_urbp(struct urbp *urb_p)
|
| 57 | {
|
| 58 | kfree(urb_p);
|
| 59 | }
|
| 60 |
|
| 61 | void free_urbp_and_urb(struct urbp *urb_p)
|
| 62 | {
|
| 63 | if (!urb_p)
|
| 64 | return;
|
| 65 | free_urb(urb_p->urb);
|
| 66 | free_urbp(urb_p);
|
| 67 | }
|
| 68 |
|
| 69 |
|
| 70 |
|
| 71 |
|
| 72 |
|
| 73 | static void nuke(struct vudc *udc, struct vep *ep)
|
| 74 | {
|
| 75 | struct vrequest *req;
|
| 76 |
|
| 77 | while (!list_empty(&ep->req_queue)) {
|
| 78 | req = list_first_entry(&ep->req_queue, struct vrequest,
|
| 79 | req_entry);
|
| 80 | list_del_init(&req->req_entry);
|
| 81 | req->req.status = -ESHUTDOWN;
|
| 82 |
|
| 83 | spin_unlock(&udc->lock);
|
| 84 | usb_gadget_giveback_request(&ep->ep, &req->req);
|
| 85 | spin_lock(&udc->lock);
|
| 86 | }
|
| 87 | }
|
| 88 |
|
| 89 |
|
| 90 | static void stop_activity(struct vudc *udc)
|
| 91 | {
|
| 92 | int i;
|
| 93 | struct urbp *urb_p, *tmp;
|
| 94 |
|
| 95 | udc->address = 0;
|
| 96 |
|
| 97 | for (i = 0; i < VIRTUAL_ENDPOINTS; i++)
|
| 98 | nuke(udc, &udc->ep[i]);
|
| 99 |
|
| 100 | list_for_each_entry_safe(urb_p, tmp, &udc->urb_queue, urb_entry) {
|
| 101 | list_del(&urb_p->urb_entry);
|
| 102 | free_urbp_and_urb(urb_p);
|
| 103 | }
|
| 104 | }
|
| 105 |
|
| 106 | struct vep *vudc_find_endpoint(struct vudc *udc, u8 address)
|
| 107 | {
|
| 108 | int i;
|
| 109 |
|
| 110 | if ((address & ~USB_DIR_IN) == 0)
|
| 111 | return &udc->ep[0];
|
| 112 |
|
| 113 | for (i = 1; i < VIRTUAL_ENDPOINTS; i++) {
|
| 114 | struct vep *ep = &udc->ep[i];
|
| 115 |
|
| 116 | if (!ep->desc)
|
| 117 | continue;
|
| 118 | if (ep->desc->bEndpointAddress == address)
|
| 119 | return ep;
|
| 120 | }
|
| 121 | return NULL;
|
| 122 | }
|
| 123 |
|
| 124 |
|
| 125 |
|
| 126 | static int vgadget_get_frame(struct usb_gadget *_gadget)
|
| 127 | {
|
| 128 | struct timespec64 now;
|
| 129 | struct vudc *udc = usb_gadget_to_vudc(_gadget);
|
| 130 |
|
| 131 | ktime_get_ts64(&now);
|
| 132 | return ((now.tv_sec - udc->start_time.tv_sec) * 1000 +
|
| 133 | (now.tv_nsec - udc->start_time.tv_nsec) / NSEC_PER_MSEC)
|
| 134 | & 0x7FF;
|
| 135 | }
|
| 136 |
|
| 137 | static int vgadget_set_selfpowered(struct usb_gadget *_gadget, int value)
|
| 138 | {
|
| 139 | struct vudc *udc = usb_gadget_to_vudc(_gadget);
|
| 140 |
|
| 141 | if (value)
|
| 142 | udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
|
| 143 | else
|
| 144 | udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
|
| 145 | return 0;
|
| 146 | }
|
| 147 |
|
| 148 | static int vgadget_pullup(struct usb_gadget *_gadget, int value)
|
| 149 | {
|
| 150 | struct vudc *udc = usb_gadget_to_vudc(_gadget);
|
| 151 | unsigned long flags;
|
| 152 | int ret;
|
| 153 |
|
| 154 |
|
| 155 | spin_lock_irqsave(&udc->lock, flags);
|
| 156 | value = !!value;
|
| 157 | if (value == udc->pullup)
|
| 158 | goto unlock;
|
| 159 |
|
| 160 | udc->pullup = value;
|
| 161 | if (value) {
|
| 162 | udc->gadget.speed = min_t(u8, USB_SPEED_HIGH,
|
| 163 | udc->driver->max_speed);
|
| 164 | udc->ep[0].ep.maxpacket = 64;
|
| 165 | |
| 166 | * This is the first place where we can ask our
|
| 167 | * gadget driver for descriptors.
|
| 168 |
|
| 169 | ret = get_gadget_descs(udc);
|
| 170 | if (ret) {
|
| 171 | dev_err(&udc->gadget.dev, "Unable go get desc: %d", ret);
|
| 172 | goto unlock;
|
| 173 | }
|
| 174 |
|
| 175 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 176 | usbip_start_eh(&udc->ud);
|
| 177 | } else {
|
| 178 |
|
| 179 | udc->desc_cached = 0;
|
| 180 |
|
| 181 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 182 | usbip_event_add(&udc->ud, VUDC_EVENT_REMOVED);
|
| 183 | usbip_stop_eh(&udc->ud);
|
| 184 | }
|
| 185 |
|
| 186 | return 0;
|
| 187 |
|
| 188 | unlock:
|
| 189 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 190 | return 0;
|
| 191 | }
|
| 192 |
|
| 193 | static int vgadget_udc_start(struct usb_gadget *g,
|
| 194 | struct usb_gadget_driver *driver)
|
| 195 | {
|
| 196 | struct vudc *udc = usb_gadget_to_vudc(g);
|
| 197 | unsigned long flags;
|
| 198 |
|
| 199 | spin_lock_irqsave(&udc->lock, flags);
|
| 200 | udc->driver = driver;
|
| 201 | udc->pullup = udc->connected = udc->desc_cached = 0;
|
| 202 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 203 |
|
| 204 | return 0;
|
| 205 | }
|
| 206 |
|
| 207 | static int vgadget_udc_stop(struct usb_gadget *g)
|
| 208 | {
|
| 209 | struct vudc *udc = usb_gadget_to_vudc(g);
|
| 210 | unsigned long flags;
|
| 211 |
|
| 212 | spin_lock_irqsave(&udc->lock, flags);
|
| 213 | udc->driver = NULL;
|
| 214 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 215 | return 0;
|
| 216 | }
|
| 217 |
|
| 218 | static const struct usb_gadget_ops vgadget_ops = {
|
| 219 | .get_frame = vgadget_get_frame,
|
| 220 | .set_selfpowered = vgadget_set_selfpowered,
|
| 221 | .pullup = vgadget_pullup,
|
| 222 | .udc_start = vgadget_udc_start,
|
| 223 | .udc_stop = vgadget_udc_stop,
|
| 224 | };
|
| 225 |
|
| 226 |
|
| 227 |
|
| 228 |
|
| 229 | static int vep_enable(struct usb_ep *_ep,
|
| 230 | const struct usb_endpoint_descriptor *desc)
|
| 231 | {
|
| 232 | struct vep *ep;
|
| 233 | struct vudc *udc;
|
| 234 | unsigned int maxp;
|
| 235 | unsigned long flags;
|
| 236 |
|
| 237 | ep = to_vep(_ep);
|
| 238 | udc = ep_to_vudc(ep);
|
| 239 |
|
| 240 | if (!_ep || !desc || ep->desc || _ep->caps.type_control
|
| 241 | || desc->bDescriptorType != USB_DT_ENDPOINT)
|
| 242 | return -EINVAL;
|
| 243 |
|
| 244 | if (!udc->driver)
|
| 245 | return -ESHUTDOWN;
|
| 246 |
|
| 247 | spin_lock_irqsave(&udc->lock, flags);
|
| 248 |
|
| 249 | maxp = usb_endpoint_maxp(desc);
|
| 250 | _ep->maxpacket = maxp;
|
| 251 | ep->desc = desc;
|
| 252 | ep->type = usb_endpoint_type(desc);
|
| 253 | ep->halted = ep->wedged = 0;
|
| 254 |
|
| 255 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 256 |
|
| 257 | return 0;
|
| 258 | }
|
| 259 |
|
| 260 | static int vep_disable(struct usb_ep *_ep)
|
| 261 | {
|
| 262 | struct vep *ep;
|
| 263 | struct vudc *udc;
|
| 264 | unsigned long flags;
|
| 265 |
|
| 266 | ep = to_vep(_ep);
|
| 267 | udc = ep_to_vudc(ep);
|
| 268 | if (!_ep || !ep->desc || _ep->caps.type_control)
|
| 269 | return -EINVAL;
|
| 270 |
|
| 271 | spin_lock_irqsave(&udc->lock, flags);
|
| 272 | ep->desc = NULL;
|
| 273 | nuke(udc, ep);
|
| 274 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 275 |
|
| 276 | return 0;
|
| 277 | }
|
| 278 |
|
| 279 | static struct usb_request *vep_alloc_request(struct usb_ep *_ep,
|
| 280 | gfp_t mem_flags)
|
| 281 | {
|
| 282 | struct vrequest *req;
|
| 283 |
|
| 284 | if (!_ep)
|
| 285 | return NULL;
|
| 286 |
|
| 287 | req = kzalloc_obj(*req, mem_flags);
|
| 288 | if (!req)
|
| 289 | return NULL;
|
| 290 |
|
| 291 | INIT_LIST_HEAD(&req->req_entry);
|
| 292 |
|
| 293 | return &req->req;
|
| 294 | }
|
| 295 |
|
| 296 | static void vep_free_request(struct usb_ep *_ep, struct usb_request *_req)
|
| 297 | {
|
| 298 | struct vrequest *req;
|
| 299 |
|
| 300 |
|
| 301 | if (!_req)
|
| 302 | return;
|
| 303 |
|
| 304 | req = to_vrequest(_req);
|
| 305 | kfree(req);
|
| 306 | }
|
| 307 |
|
| 308 | static int vep_queue(struct usb_ep *_ep, struct usb_request *_req,
|
| 309 | gfp_t mem_flags)
|
| 310 | {
|
| 311 | struct vep *ep;
|
| 312 | struct vrequest *req;
|
| 313 | struct vudc *udc;
|
| 314 | unsigned long flags;
|
| 315 |
|
| 316 | if (!_ep || !_req)
|
| 317 | return -EINVAL;
|
| 318 |
|
| 319 | ep = to_vep(_ep);
|
| 320 | req = to_vrequest(_req);
|
| 321 | udc = ep_to_vudc(ep);
|
| 322 |
|
| 323 | spin_lock_irqsave(&udc->lock, flags);
|
| 324 | _req->actual = 0;
|
| 325 | _req->status = -EINPROGRESS;
|
| 326 |
|
| 327 | list_add_tail(&req->req_entry, &ep->req_queue);
|
| 328 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 329 |
|
| 330 | return 0;
|
| 331 | }
|
| 332 |
|
| 333 | static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
|
| 334 | {
|
| 335 | struct vep *ep;
|
| 336 | struct vudc *udc;
|
| 337 | struct vrequest *lst;
|
| 338 | unsigned long flags;
|
| 339 | int ret = -EINVAL;
|
| 340 |
|
| 341 | if (!_ep || !_req)
|
| 342 | return ret;
|
| 343 |
|
| 344 | ep = to_vep(_ep);
|
| 345 | udc = ep_to_vudc(ep);
|
| 346 |
|
| 347 | if (!udc->driver)
|
| 348 | return -ESHUTDOWN;
|
| 349 |
|
| 350 | spin_lock_irqsave(&udc->lock, flags);
|
| 351 | list_for_each_entry(lst, &ep->req_queue, req_entry) {
|
| 352 | if (&lst->req == _req) {
|
| 353 | list_del_init(&lst->req_entry);
|
| 354 | _req->status = -ECONNRESET;
|
| 355 | ret = 0;
|
| 356 | break;
|
| 357 | }
|
| 358 | }
|
| 359 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 360 |
|
| 361 | if (ret == 0)
|
| 362 | usb_gadget_giveback_request(_ep, _req);
|
| 363 |
|
| 364 | return ret;
|
| 365 | }
|
| 366 |
|
| 367 | static int
|
| 368 | vep_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
|
| 369 | {
|
| 370 | struct vep *ep;
|
| 371 | struct vudc *udc;
|
| 372 | unsigned long flags;
|
| 373 | int ret = 0;
|
| 374 |
|
| 375 | ep = to_vep(_ep);
|
| 376 | if (!_ep)
|
| 377 | return -EINVAL;
|
| 378 |
|
| 379 | udc = ep_to_vudc(ep);
|
| 380 | if (!udc->driver)
|
| 381 | return -ESHUTDOWN;
|
| 382 |
|
| 383 | spin_lock_irqsave(&udc->lock, flags);
|
| 384 | if (!value)
|
| 385 | ep->halted = ep->wedged = 0;
|
| 386 | else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
|
| 387 | !list_empty(&ep->req_queue))
|
| 388 | ret = -EAGAIN;
|
| 389 | else {
|
| 390 | ep->halted = 1;
|
| 391 | if (wedged)
|
| 392 | ep->wedged = 1;
|
| 393 | }
|
| 394 |
|
| 395 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 396 | return ret;
|
| 397 | }
|
| 398 |
|
| 399 | static int
|
| 400 | vep_set_halt(struct usb_ep *_ep, int value)
|
| 401 | {
|
| 402 | return vep_set_halt_and_wedge(_ep, value, 0);
|
| 403 | }
|
| 404 |
|
| 405 | static int vep_set_wedge(struct usb_ep *_ep)
|
| 406 | {
|
| 407 | return vep_set_halt_and_wedge(_ep, 1, 1);
|
| 408 | }
|
| 409 |
|
| 410 | static const struct usb_ep_ops vep_ops = {
|
| 411 | .enable = vep_enable,
|
| 412 | .disable = vep_disable,
|
| 413 |
|
| 414 | .alloc_request = vep_alloc_request,
|
| 415 | .free_request = vep_free_request,
|
| 416 |
|
| 417 | .queue = vep_queue,
|
| 418 | .dequeue = vep_dequeue,
|
| 419 |
|
| 420 | .set_halt = vep_set_halt,
|
| 421 | .set_wedge = vep_set_wedge,
|
| 422 | };
|
| 423 |
|
| 424 |
|
| 425 |
|
| 426 |
|
| 427 | static void vudc_shutdown(struct usbip_device *ud)
|
| 428 | {
|
| 429 | struct vudc *udc = container_of(ud, struct vudc, ud);
|
| 430 | int call_disconnect = 0;
|
| 431 | unsigned long flags;
|
| 432 |
|
| 433 | dev_dbg(&udc->pdev->dev, "device shutdown");
|
| 434 | if (ud->tcp_socket)
|
| 435 | kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR);
|
| 436 |
|
| 437 | if (ud->tcp_rx) {
|
| 438 | kthread_stop_put(ud->tcp_rx);
|
| 439 | ud->tcp_rx = NULL;
|
| 440 | }
|
| 441 | if (ud->tcp_tx) {
|
| 442 | kthread_stop_put(ud->tcp_tx);
|
| 443 | ud->tcp_tx = NULL;
|
| 444 | }
|
| 445 |
|
| 446 | if (ud->tcp_socket) {
|
| 447 | sockfd_put(ud->tcp_socket);
|
| 448 | ud->tcp_socket = NULL;
|
| 449 | }
|
| 450 |
|
| 451 | spin_lock_irqsave(&udc->lock, flags);
|
| 452 | stop_activity(udc);
|
| 453 | if (udc->connected && udc->driver->disconnect)
|
| 454 | call_disconnect = 1;
|
| 455 | udc->connected = 0;
|
| 456 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 457 | if (call_disconnect)
|
| 458 | udc->driver->disconnect(&udc->gadget);
|
| 459 | }
|
| 460 |
|
| 461 | static void vudc_device_reset(struct usbip_device *ud)
|
| 462 | {
|
| 463 | struct vudc *udc = container_of(ud, struct vudc, ud);
|
| 464 | unsigned long flags;
|
| 465 |
|
| 466 | dev_dbg(&udc->pdev->dev, "device reset");
|
| 467 | spin_lock_irqsave(&udc->lock, flags);
|
| 468 | stop_activity(udc);
|
| 469 | spin_unlock_irqrestore(&udc->lock, flags);
|
| 470 | if (udc->driver)
|
| 471 | usb_gadget_udc_reset(&udc->gadget, udc->driver);
|
| 472 | spin_lock_irqsave(&ud->lock, flags);
|
| 473 | ud->status = SDEV_ST_AVAILABLE;
|
| 474 | spin_unlock_irqrestore(&ud->lock, flags);
|
| 475 | }
|
| 476 |
|
| 477 | static void vudc_device_unusable(struct usbip_device *ud)
|
| 478 | {
|
| 479 | unsigned long flags;
|
| 480 |
|
| 481 | spin_lock_irqsave(&ud->lock, flags);
|
| 482 | ud->status = SDEV_ST_ERROR;
|
| 483 | spin_unlock_irqrestore(&ud->lock, flags);
|
| 484 | }
|
| 485 |
|
| 486 |
|
| 487 |
|
| 488 | struct vudc_device *alloc_vudc_device(int devid)
|
| 489 | {
|
| 490 | struct vudc_device *udc_dev;
|
| 491 |
|
| 492 | udc_dev = kzalloc_obj(*udc_dev);
|
| 493 | if (!udc_dev)
|
| 494 | return NULL;
|
| 495 |
|
| 496 | INIT_LIST_HEAD(&udc_dev->dev_entry);
|
| 497 |
|
| 498 | udc_dev->pdev = platform_device_alloc(GADGET_NAME, devid);
|
| 499 | if (!udc_dev->pdev) {
|
| 500 | kfree(udc_dev);
|
| 501 | udc_dev = NULL;
|
| 502 | }
|
| 503 |
|
| 504 | return udc_dev;
|
| 505 | }
|
| 506 |
|
| 507 | void put_vudc_device(struct vudc_device *udc_dev)
|
| 508 | {
|
| 509 | platform_device_put(udc_dev->pdev);
|
| 510 | kfree(udc_dev);
|
| 511 | }
|
| 512 |
|
| 513 | static int init_vudc_hw(struct vudc *udc)
|
| 514 | {
|
| 515 | int i;
|
| 516 | struct usbip_device *ud = &udc->ud;
|
| 517 | struct vep *ep;
|
| 518 |
|
| 519 | udc->ep = kzalloc_objs(*udc->ep, VIRTUAL_ENDPOINTS);
|
| 520 | if (!udc->ep)
|
| 521 | goto nomem_ep;
|
| 522 |
|
| 523 | INIT_LIST_HEAD(&udc->gadget.ep_list);
|
| 524 |
|
| 525 |
|
| 526 | for (i = 0; i < VIRTUAL_ENDPOINTS; ++i) {
|
| 527 | int is_out = i % 2;
|
| 528 | int num = (i + 1) / 2;
|
| 529 |
|
| 530 | ep = &udc->ep[i];
|
| 531 |
|
| 532 | sprintf(ep->name, "ep%d%s", num,
|
| 533 | i ? (is_out ? "out" : "in") : "");
|
| 534 | ep->ep.name = ep->name;
|
| 535 |
|
| 536 | ep->ep.ops = &vep_ops;
|
| 537 |
|
| 538 | usb_ep_set_maxpacket_limit(&ep->ep, ~0);
|
| 539 | ep->ep.max_streams = 16;
|
| 540 | ep->gadget = &udc->gadget;
|
| 541 | INIT_LIST_HEAD(&ep->req_queue);
|
| 542 |
|
| 543 | if (i == 0) {
|
| 544 |
|
| 545 | ep->ep.caps.type_control = true;
|
| 546 | ep->ep.caps.dir_out = true;
|
| 547 | ep->ep.caps.dir_in = true;
|
| 548 |
|
| 549 | udc->gadget.ep0 = &ep->ep;
|
| 550 | } else {
|
| 551 |
|
| 552 | ep->ep.caps.type_iso = true;
|
| 553 | ep->ep.caps.type_int = true;
|
| 554 | ep->ep.caps.type_bulk = true;
|
| 555 |
|
| 556 | if (is_out)
|
| 557 | ep->ep.caps.dir_out = true;
|
| 558 | else
|
| 559 | ep->ep.caps.dir_in = true;
|
| 560 |
|
| 561 | list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
|
| 562 | }
|
| 563 | }
|
| 564 |
|
| 565 | spin_lock_init(&udc->lock);
|
| 566 | spin_lock_init(&udc->lock_tx);
|
| 567 | INIT_LIST_HEAD(&udc->urb_queue);
|
| 568 | INIT_LIST_HEAD(&udc->tx_queue);
|
| 569 | init_waitqueue_head(&udc->tx_waitq);
|
| 570 |
|
| 571 | spin_lock_init(&ud->lock);
|
| 572 | mutex_init(&ud->sysfs_lock);
|
| 573 | ud->status = SDEV_ST_AVAILABLE;
|
| 574 | ud->side = USBIP_VUDC;
|
| 575 |
|
| 576 | ud->eh_ops.shutdown = vudc_shutdown;
|
| 577 | ud->eh_ops.reset = vudc_device_reset;
|
| 578 | ud->eh_ops.unusable = vudc_device_unusable;
|
| 579 |
|
| 580 | v_init_timer(udc);
|
| 581 | return 0;
|
| 582 |
|
| 583 | nomem_ep:
|
| 584 | return -ENOMEM;
|
| 585 | }
|
| 586 |
|
| 587 | static void cleanup_vudc_hw(struct vudc *udc)
|
| 588 | {
|
| 589 | kfree(udc->ep);
|
| 590 | }
|
| 591 |
|
| 592 |
|
| 593 |
|
| 594 | int vudc_probe(struct platform_device *pdev)
|
| 595 | {
|
| 596 | struct vudc *udc;
|
| 597 | int ret = -ENOMEM;
|
| 598 |
|
| 599 | udc = kzalloc_obj(*udc);
|
| 600 | if (!udc)
|
| 601 | goto out;
|
| 602 |
|
| 603 | udc->gadget.name = GADGET_NAME;
|
| 604 | udc->gadget.ops = &vgadget_ops;
|
| 605 | udc->gadget.max_speed = USB_SPEED_HIGH;
|
| 606 | udc->gadget.dev.parent = &pdev->dev;
|
| 607 | udc->pdev = pdev;
|
| 608 |
|
| 609 | ret = init_vudc_hw(udc);
|
| 610 | if (ret)
|
| 611 | goto err_init_vudc_hw;
|
| 612 |
|
| 613 | ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
|
| 614 | if (ret < 0)
|
| 615 | goto err_add_udc;
|
| 616 |
|
| 617 | platform_set_drvdata(pdev, udc);
|
| 618 |
|
| 619 | return ret;
|
| 620 |
|
| 621 | err_add_udc:
|
| 622 | cleanup_vudc_hw(udc);
|
| 623 | err_init_vudc_hw:
|
| 624 | kfree(udc);
|
| 625 | out:
|
| 626 | return ret;
|
| 627 | }
|
| 628 |
|
| 629 | void vudc_remove(struct platform_device *pdev)
|
| 630 | {
|
| 631 | struct vudc *udc = platform_get_drvdata(pdev);
|
| 632 |
|
| 633 | v_stop_timer(udc);
|
| 634 | usb_del_gadget_udc(&udc->gadget);
|
| 635 | cleanup_vudc_hw(udc);
|
| 636 | kfree(udc);
|
| 637 | }
|