| |
| |
| |
| |
| |
| |
| |
| |
| #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 + 15 + 15 ) |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| |
| |
| 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); |
| <------>} |
| } |
| |
| |
| 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; |
| } |
| |
| |
| |
| 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 { |
| <------><------> |
| <------><------>udc->desc_cached = 0; |
| |
| <------><------>spin_unlock_irqrestore(&udc->lock, flags); |
| <------><------>usbip_event_add(&udc->ud, VUDC_EVENT_REMOVED); |
| <------><------>usbip_stop_eh(&udc->ud); |
| <------>} |
| |
| <------>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, |
| }; |
| |
| |
| |
| |
| 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 (!_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, |
| }; |
| |
| |
| |
| |
| 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); |
| } |
| |
| |
| |
| 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); |
| |
| <------> |
| <------>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) { |
| <------><------><------> |
| <------><------><------>ep->ep.caps.type_control = true; |
| <------><------><------>ep->ep.caps.dir_out = true; |
| <------><------><------>ep->ep.caps.dir_in = true; |
| |
| <------><------><------>udc->gadget.ep0 = &ep->ep; |
| <------><------>} else { |
| <------><------><------> |
| <------><------><------>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); |
| <------>mutex_init(&ud->sysfs_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); |
| } |
| |
| |
| |
| 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; |
| |
| <------>platform_set_drvdata(pdev, udc); |
| |
| <------>return ret; |
| |
| 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); |
| |
| <------>usb_del_gadget_udc(&udc->gadget); |
| <------>cleanup_vudc_hw(udc); |
| <------>kfree(udc); |
| <------>return 0; |
| } |
| |