cregit-Linux how code gets into the kernel

Release 4.7 drivers/usb/usbip/vudc_dev.c

/*
 * 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>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#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); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski48100.00%1100.00%
Total48100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski58100.00%1100.00%
Total58100.00%1100.00%


static void free_urbp(struct urbp *urb_p) { kfree(urb_p); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski16100.00%1100.00%
Total16100.00%1100.00%


void free_urbp_and_urb(struct urbp *urb_p) { if (!urb_p) return; free_urb(urb_p->urb); free_urbp(urb_p); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski28100.00%1100.00%
Total28100.00%1100.00%

/* 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); } }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski95100.00%1100.00%
Total95100.00%1100.00%

/* 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); } }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski79100.00%1100.00%
Total79100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski8998.89%150.00%
krzysztof opasiakkrzysztof opasiak11.11%150.00%
Total90100.00%2100.00%

/* gadget ops */ /* FIXME - this will probably misbehave when suspend/resume is added */
static int vgadget_get_frame(struct usb_gadget *_gadget) { struct timeval now; struct vudc *udc = usb_gadget_to_vudc(_gadget); do_gettimeofday(&now); return ((now.tv_sec - udc->start_time.tv_sec) * 1000 + (now.tv_usec - udc->start_time.tv_usec) / 1000) % 0x7FF; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski64100.00%1100.00%
Total64100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski53100.00%1100.00%
Total53100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski208100.00%1100.00%
Total208100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski73100.00%1100.00%
Total73100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski54100.00%1100.00%
Total54100.00%1100.00%

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 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) & 0x7ff; _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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski150100.00%1100.00%
Total150100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski95100.00%1100.00%
Total95100.00%1100.00%


static struct usb_request *vep_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { struct vep *ep; struct vrequest *req; if (!_ep) return NULL; ep = to_vep(_ep); req = kzalloc(sizeof(*req), mem_flags); if (!req) return NULL; INIT_LIST_HEAD(&req->req_entry); return &req->req; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski76100.00%1100.00%
Total76100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski4293.33%150.00%
krzysztof opasiakkrzysztof opasiak36.67%150.00%
Total45100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski120100.00%1100.00%
Total120100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski165100.00%1100.00%
Total165100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski158100.00%1100.00%
Total158100.00%1100.00%


static int vep_set_halt(struct usb_ep *_ep, int value) { return vep_set_halt_and_wedge(_ep, value, 0); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski24100.00%1100.00%
Total24100.00%1100.00%


static int vep_set_wedge(struct usb_ep *_ep) { return vep_set_halt_and_wedge(_ep, 1, 1); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski21100.00%1100.00%
Total21100.00%1100.00%

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_tx) { 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); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski188100.00%1100.00%
Total188100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski111100.00%1100.00%
Total111100.00%1100.00%


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); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski41100.00%1100.00%
Total41100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski80100.00%1100.00%
Total80100.00%1100.00%


void put_vudc_device(struct vudc_device *udc_dev) { platform_device_put(udc_dev->pdev); kfree(udc_dev); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski22100.00%1100.00%
Total22100.00%1100.00%


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; if (i == 0) { ep->ep.caps.type_control = true; ep->ep.caps.dir_out = true; ep->ep.caps.dir_in = true; } 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; ep->ep.ops = &vep_ops; list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); ep->halted = ep->wedged = ep->already_seen = ep->setup_stage = 0; usb_ep_set_maxpacket_limit(&ep->ep, ~0); ep->ep.max_streams = 16; ep->gadget = &udc->gadget; ep->desc = NULL; INIT_LIST_HEAD(&ep->req_queue); } 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; udc->gadget.ep0 = &udc->ep[0].ep; list_del_init(&udc->ep[0].ep.ep_list); v_init_timer(udc); return 0; nomem_ep: return -ENOMEM; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski456100.00%1100.00%
Total456100.00%1100.00%


static void cleanup_vudc_hw(struct vudc *udc) { kfree(udc->ep); }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski18100.00%1100.00%
Total18100.00%1100.00%

/* 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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski199100.00%1100.00%
Total199100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski54100.00%1100.00%
Total54100.00%1100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
igor kotrasinskiigor kotrasinski302399.87%133.33%
krzysztof opasiakkrzysztof opasiak40.13%266.67%
Total3027100.00%3100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}