Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Igor Kotrasinski | 2899 | 97.54% | 1 | 10.00% |
Krzysztof Opasiak | 63 | 2.12% | 3 | 30.00% |
Arnd Bergmann | 6 | 0.20% | 2 | 20.00% |
Greg Kroah-Hartman | 2 | 0.07% | 2 | 20.00% |
Dave Jones | 1 | 0.03% | 1 | 10.00% |
Jai Krishna | 1 | 0.03% | 1 | 10.00% |
Total | 2972 | 10 |
// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> * Copyright (C) 2015-2016 Samsung Electronics * Igor Kotrasinski <i.kotrasinsk@samsung.com> * Krzysztof Opasiak <k.opasiak@samsung.com> */ #include <linux/device.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/platform_device.h> #include <linux/usb.h> #include <linux/usb/gadget.h> #include <linux/usb/hcd.h> #include <linux/kthread.h> #include <linux/file.h> #include <linux/byteorder/generic.h> #include "usbip_common.h" #include "vudc.h" #define VIRTUAL_ENDPOINTS (1 /* ep0 */ + 15 /* in eps */ + 15 /* out eps */) /* urb-related structures alloc / free */ static void free_urb(struct urb *urb) { if (!urb) return; kfree(urb->setup_packet); urb->setup_packet = NULL; kfree(urb->transfer_buffer); urb->transfer_buffer = NULL; usb_free_urb(urb); } struct urbp *alloc_urbp(void) { struct urbp *urb_p; urb_p = kzalloc(sizeof(*urb_p), GFP_KERNEL); if (!urb_p) return urb_p; urb_p->urb = NULL; urb_p->ep = NULL; INIT_LIST_HEAD(&urb_p->urb_entry); return urb_p; } static void free_urbp(struct urbp *urb_p) { kfree(urb_p); } void free_urbp_and_urb(struct urbp *urb_p) { if (!urb_p) return; free_urb(urb_p->urb); free_urbp(urb_p); } /* utilities ; almost verbatim from dummy_hcd.c */ /* called with spinlock held */ static void nuke(struct vudc *udc, struct vep *ep) { struct vrequest *req; while (!list_empty(&ep->req_queue)) { req = list_first_entry(&ep->req_queue, struct vrequest, req_entry); list_del_init(&req->req_entry); req->req.status = -ESHUTDOWN; spin_unlock(&udc->lock); usb_gadget_giveback_request(&ep->ep, &req->req); spin_lock(&udc->lock); } } /* caller must hold lock */ static void stop_activity(struct vudc *udc) { int i; struct urbp *urb_p, *tmp; udc->address = 0; for (i = 0; i < VIRTUAL_ENDPOINTS; i++) nuke(udc, &udc->ep[i]); list_for_each_entry_safe(urb_p, tmp, &udc->urb_queue, urb_entry) { list_del(&urb_p->urb_entry); free_urbp_and_urb(urb_p); } } struct vep *vudc_find_endpoint(struct vudc *udc, u8 address) { int i; if ((address & ~USB_DIR_IN) == 0) return &udc->ep[0]; for (i = 1; i < VIRTUAL_ENDPOINTS; i++) { struct vep *ep = &udc->ep[i]; if (!ep->desc) continue; if (ep->desc->bEndpointAddress == address) return ep; } return NULL; } /* gadget ops */ static int vgadget_get_frame(struct usb_gadget *_gadget) { struct timespec64 now; struct vudc *udc = usb_gadget_to_vudc(_gadget); ktime_get_ts64(&now); return ((now.tv_sec - udc->start_time.tv_sec) * 1000 + (now.tv_nsec - udc->start_time.tv_nsec) / NSEC_PER_MSEC) & 0x7FF; } static int vgadget_set_selfpowered(struct usb_gadget *_gadget, int value) { struct vudc *udc = usb_gadget_to_vudc(_gadget); if (value) udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); else udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); return 0; } static int vgadget_pullup(struct usb_gadget *_gadget, int value) { struct vudc *udc = usb_gadget_to_vudc(_gadget); unsigned long flags; int ret; spin_lock_irqsave(&udc->lock, flags); value = !!value; if (value == udc->pullup) goto unlock; udc->pullup = value; if (value) { udc->gadget.speed = min_t(u8, USB_SPEED_HIGH, udc->driver->max_speed); udc->ep[0].ep.maxpacket = 64; /* * This is the first place where we can ask our * gadget driver for descriptors. */ ret = get_gadget_descs(udc); if (ret) { dev_err(&udc->gadget.dev, "Unable go get desc: %d", ret); goto unlock; } spin_unlock_irqrestore(&udc->lock, flags); usbip_start_eh(&udc->ud); } else { /* Invalidate descriptors */ udc->desc_cached = 0; spin_unlock_irqrestore(&udc->lock, flags); usbip_event_add(&udc->ud, VUDC_EVENT_REMOVED); usbip_stop_eh(&udc->ud); /* Wait for eh completion */ } return 0; unlock: spin_unlock_irqrestore(&udc->lock, flags); return 0; } static int vgadget_udc_start(struct usb_gadget *g, struct usb_gadget_driver *driver) { struct vudc *udc = usb_gadget_to_vudc(g); unsigned long flags; spin_lock_irqsave(&udc->lock, flags); udc->driver = driver; udc->pullup = udc->connected = udc->desc_cached = 0; spin_unlock_irqrestore(&udc->lock, flags); return 0; } static int vgadget_udc_stop(struct usb_gadget *g) { struct vudc *udc = usb_gadget_to_vudc(g); unsigned long flags; spin_lock_irqsave(&udc->lock, flags); udc->driver = NULL; spin_unlock_irqrestore(&udc->lock, flags); return 0; } static const struct usb_gadget_ops vgadget_ops = { .get_frame = vgadget_get_frame, .set_selfpowered = vgadget_set_selfpowered, .pullup = vgadget_pullup, .udc_start = vgadget_udc_start, .udc_stop = vgadget_udc_stop, }; /* endpoint ops */ static int vep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { struct vep *ep; struct vudc *udc; unsigned int maxp; unsigned long flags; ep = to_vep(_ep); udc = ep_to_vudc(ep); if (!_ep || !desc || ep->desc || _ep->caps.type_control || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; if (!udc->driver) return -ESHUTDOWN; spin_lock_irqsave(&udc->lock, flags); maxp = usb_endpoint_maxp(desc); _ep->maxpacket = maxp; ep->desc = desc; ep->type = usb_endpoint_type(desc); ep->halted = ep->wedged = 0; spin_unlock_irqrestore(&udc->lock, flags); return 0; } static int vep_disable(struct usb_ep *_ep) { struct vep *ep; struct vudc *udc; unsigned long flags; ep = to_vep(_ep); udc = ep_to_vudc(ep); if (!_ep || !ep->desc || _ep->caps.type_control) return -EINVAL; spin_lock_irqsave(&udc->lock, flags); ep->desc = NULL; nuke(udc, ep); spin_unlock_irqrestore(&udc->lock, flags); return 0; } static struct usb_request *vep_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { struct vrequest *req; if (!_ep) return NULL; req = kzalloc(sizeof(*req), mem_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->req_entry); return &req->req; } static void vep_free_request(struct usb_ep *_ep, struct usb_request *_req) { struct vrequest *req; if (WARN_ON(!_ep || !_req)) return; req = to_vrequest(_req); kfree(req); } static int vep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t mem_flags) { struct vep *ep; struct vrequest *req; struct vudc *udc; unsigned long flags; if (!_ep || !_req) return -EINVAL; ep = to_vep(_ep); req = to_vrequest(_req); udc = ep_to_vudc(ep); spin_lock_irqsave(&udc->lock, flags); _req->actual = 0; _req->status = -EINPROGRESS; list_add_tail(&req->req_entry, &ep->req_queue); spin_unlock_irqrestore(&udc->lock, flags); return 0; } static int vep_dequeue(struct usb_ep *_ep, struct usb_request *_req) { struct vep *ep; struct vrequest *req; struct vudc *udc; struct vrequest *lst; unsigned long flags; int ret = -EINVAL; if (!_ep || !_req) return ret; ep = to_vep(_ep); req = to_vrequest(_req); udc = req->udc; if (!udc->driver) return -ESHUTDOWN; spin_lock_irqsave(&udc->lock, flags); list_for_each_entry(lst, &ep->req_queue, req_entry) { if (&lst->req == _req) { list_del_init(&lst->req_entry); _req->status = -ECONNRESET; ret = 0; break; } } spin_unlock_irqrestore(&udc->lock, flags); if (ret == 0) usb_gadget_giveback_request(_ep, _req); return ret; } static int vep_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) { struct vep *ep; struct vudc *udc; unsigned long flags; int ret = 0; ep = to_vep(_ep); if (!_ep) return -EINVAL; udc = ep_to_vudc(ep); if (!udc->driver) return -ESHUTDOWN; spin_lock_irqsave(&udc->lock, flags); if (!value) ep->halted = ep->wedged = 0; else if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) && !list_empty(&ep->req_queue)) ret = -EAGAIN; else { ep->halted = 1; if (wedged) ep->wedged = 1; } spin_unlock_irqrestore(&udc->lock, flags); return ret; } static int vep_set_halt(struct usb_ep *_ep, int value) { return vep_set_halt_and_wedge(_ep, value, 0); } static int vep_set_wedge(struct usb_ep *_ep) { return vep_set_halt_and_wedge(_ep, 1, 1); } static const struct usb_ep_ops vep_ops = { .enable = vep_enable, .disable = vep_disable, .alloc_request = vep_alloc_request, .free_request = vep_free_request, .queue = vep_queue, .dequeue = vep_dequeue, .set_halt = vep_set_halt, .set_wedge = vep_set_wedge, }; /* shutdown / reset / error handlers */ static void vudc_shutdown(struct usbip_device *ud) { struct vudc *udc = container_of(ud, struct vudc, ud); int call_disconnect = 0; unsigned long flags; dev_dbg(&udc->pdev->dev, "device shutdown"); if (ud->tcp_socket) kernel_sock_shutdown(ud->tcp_socket, SHUT_RDWR); if (ud->tcp_rx) { kthread_stop_put(ud->tcp_rx); ud->tcp_rx = NULL; } if (ud->tcp_tx) { kthread_stop_put(ud->tcp_tx); ud->tcp_tx = NULL; } if (ud->tcp_socket) { sockfd_put(ud->tcp_socket); ud->tcp_socket = NULL; } spin_lock_irqsave(&udc->lock, flags); stop_activity(udc); if (udc->connected && udc->driver->disconnect) call_disconnect = 1; udc->connected = 0; spin_unlock_irqrestore(&udc->lock, flags); if (call_disconnect) udc->driver->disconnect(&udc->gadget); } static void vudc_device_reset(struct usbip_device *ud) { struct vudc *udc = container_of(ud, struct vudc, ud); unsigned long flags; dev_dbg(&udc->pdev->dev, "device reset"); spin_lock_irqsave(&udc->lock, flags); stop_activity(udc); spin_unlock_irqrestore(&udc->lock, flags); if (udc->driver) usb_gadget_udc_reset(&udc->gadget, udc->driver); spin_lock_irqsave(&ud->lock, flags); ud->status = SDEV_ST_AVAILABLE; spin_unlock_irqrestore(&ud->lock, flags); } static void vudc_device_unusable(struct usbip_device *ud) { unsigned long flags; spin_lock_irqsave(&ud->lock, flags); ud->status = SDEV_ST_ERROR; spin_unlock_irqrestore(&ud->lock, flags); } /* device setup / cleanup */ struct vudc_device *alloc_vudc_device(int devid) { struct vudc_device *udc_dev = NULL; udc_dev = kzalloc(sizeof(*udc_dev), GFP_KERNEL); if (!udc_dev) goto out; INIT_LIST_HEAD(&udc_dev->dev_entry); udc_dev->pdev = platform_device_alloc(GADGET_NAME, devid); if (!udc_dev->pdev) { kfree(udc_dev); udc_dev = NULL; } out: return udc_dev; } void put_vudc_device(struct vudc_device *udc_dev) { platform_device_put(udc_dev->pdev); kfree(udc_dev); } static int init_vudc_hw(struct vudc *udc) { int i; struct usbip_device *ud = &udc->ud; struct vep *ep; udc->ep = kcalloc(VIRTUAL_ENDPOINTS, sizeof(*udc->ep), GFP_KERNEL); if (!udc->ep) goto nomem_ep; INIT_LIST_HEAD(&udc->gadget.ep_list); /* create ep0 and 15 in, 15 out general purpose eps */ for (i = 0; i < VIRTUAL_ENDPOINTS; ++i) { int is_out = i % 2; int num = (i + 1) / 2; ep = &udc->ep[i]; sprintf(ep->name, "ep%d%s", num, i ? (is_out ? "out" : "in") : ""); ep->ep.name = ep->name; ep->ep.ops = &vep_ops; usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.max_streams = 16; ep->gadget = &udc->gadget; INIT_LIST_HEAD(&ep->req_queue); if (i == 0) { /* ep0 */ ep->ep.caps.type_control = true; ep->ep.caps.dir_out = true; ep->ep.caps.dir_in = true; udc->gadget.ep0 = &ep->ep; } else { /* All other eps */ ep->ep.caps.type_iso = true; ep->ep.caps.type_int = true; ep->ep.caps.type_bulk = true; if (is_out) ep->ep.caps.dir_out = true; else ep->ep.caps.dir_in = true; list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); } } spin_lock_init(&udc->lock); spin_lock_init(&udc->lock_tx); INIT_LIST_HEAD(&udc->urb_queue); INIT_LIST_HEAD(&udc->tx_queue); init_waitqueue_head(&udc->tx_waitq); spin_lock_init(&ud->lock); ud->status = SDEV_ST_AVAILABLE; ud->side = USBIP_VUDC; ud->eh_ops.shutdown = vudc_shutdown; ud->eh_ops.reset = vudc_device_reset; ud->eh_ops.unusable = vudc_device_unusable; v_init_timer(udc); return 0; nomem_ep: return -ENOMEM; } static void cleanup_vudc_hw(struct vudc *udc) { kfree(udc->ep); } /* platform driver ops */ int vudc_probe(struct platform_device *pdev) { struct vudc *udc; int ret = -ENOMEM; udc = kzalloc(sizeof(*udc), GFP_KERNEL); if (!udc) goto out; udc->gadget.name = GADGET_NAME; udc->gadget.ops = &vgadget_ops; udc->gadget.max_speed = USB_SPEED_HIGH; udc->gadget.dev.parent = &pdev->dev; udc->pdev = pdev; ret = init_vudc_hw(udc); if (ret) goto err_init_vudc_hw; ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget); if (ret < 0) goto err_add_udc; ret = sysfs_create_group(&pdev->dev.kobj, &vudc_attr_group); if (ret) { dev_err(&udc->pdev->dev, "create sysfs files\n"); goto err_sysfs; } platform_set_drvdata(pdev, udc); return ret; err_sysfs: usb_del_gadget_udc(&udc->gadget); err_add_udc: cleanup_vudc_hw(udc); err_init_vudc_hw: kfree(udc); out: return ret; } int vudc_remove(struct platform_device *pdev) { struct vudc *udc = platform_get_drvdata(pdev); sysfs_remove_group(&pdev->dev.kobj, &vudc_attr_group); usb_del_gadget_udc(&udc->gadget); cleanup_vudc_hw(udc); kfree(udc); return 0; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1