Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Igor Kotrasinski | 1079 | 82.68% | 2 | 14.29% |
Shuah Khan | 196 | 15.02% | 3 | 21.43% |
Greg Kroah-Hartman | 19 | 1.46% | 4 | 28.57% |
Dan Carpenter | 7 | 0.54% | 1 | 7.14% |
Colin Ian King | 2 | 0.15% | 2 | 14.29% |
Arnd Bergmann | 1 | 0.08% | 1 | 7.14% |
Irenge Jules Bashizi | 1 | 0.08% | 1 | 7.14% |
Total | 1305 | 14 |
// 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/list.h> #include <linux/usb/gadget.h> #include <linux/usb/ch9.h> #include <linux/sysfs.h> #include <linux/kthread.h> #include <linux/byteorder/generic.h> #include "usbip_common.h" #include "vudc.h" #include <net/sock.h> /* called with udc->lock held */ int get_gadget_descs(struct vudc *udc) { struct vrequest *usb_req; struct vep *ep0 = to_vep(udc->gadget.ep0); struct usb_device_descriptor *ddesc = &udc->dev_desc; struct usb_ctrlrequest req; int ret; if (!udc->driver || !udc->pullup) return -EINVAL; req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; req.bRequest = USB_REQ_GET_DESCRIPTOR; req.wValue = cpu_to_le16(USB_DT_DEVICE << 8); req.wIndex = cpu_to_le16(0); req.wLength = cpu_to_le16(sizeof(*ddesc)); spin_unlock(&udc->lock); ret = udc->driver->setup(&(udc->gadget), &req); spin_lock(&udc->lock); if (ret < 0) goto out; /* assuming request queue is empty; request is now on top */ usb_req = list_last_entry(&ep0->req_queue, struct vrequest, req_entry); list_del(&usb_req->req_entry); if (usb_req->req.length > sizeof(*ddesc)) { ret = -EOVERFLOW; goto giveback_req; } memcpy(ddesc, usb_req->req.buf, sizeof(*ddesc)); udc->desc_cached = 1; ret = 0; giveback_req: usb_req->req.status = 0; usb_req->req.actual = usb_req->req.length; usb_gadget_giveback_request(&(ep0->ep), &(usb_req->req)); out: return ret; } /* * Exposes device descriptor from the gadget driver. */ static ssize_t dev_desc_read(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *out, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct vudc *udc = (struct vudc *)dev_get_drvdata(dev); char *desc_ptr = (char *) &udc->dev_desc; unsigned long flags; int ret; spin_lock_irqsave(&udc->lock, flags); if (!udc->desc_cached) { ret = -ENODEV; goto unlock; } memcpy(out, desc_ptr + off, count); ret = count; unlock: spin_unlock_irqrestore(&udc->lock, flags); return ret; } static BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor)); static ssize_t usbip_sockfd_store(struct device *dev, struct device_attribute *attr, const char *in, size_t count) { struct vudc *udc = (struct vudc *) dev_get_drvdata(dev); int rv; int sockfd = 0; int err; struct socket *socket; unsigned long flags; int ret; struct task_struct *tcp_rx = NULL; struct task_struct *tcp_tx = NULL; rv = kstrtoint(in, 0, &sockfd); if (rv != 0) return -EINVAL; if (!udc) { dev_err(dev, "no device"); return -ENODEV; } mutex_lock(&udc->ud.sysfs_lock); spin_lock_irqsave(&udc->lock, flags); /* Don't export what we don't have */ if (!udc->driver || !udc->pullup) { dev_err(dev, "gadget not bound"); ret = -ENODEV; goto unlock; } if (sockfd != -1) { if (udc->connected) { dev_err(dev, "Device already connected"); ret = -EBUSY; goto unlock; } spin_lock(&udc->ud.lock); if (udc->ud.status != SDEV_ST_AVAILABLE) { ret = -EINVAL; goto unlock_ud; } socket = sockfd_lookup(sockfd, &err); if (!socket) { dev_err(dev, "failed to lookup sock"); ret = -EINVAL; goto unlock_ud; } if (socket->type != SOCK_STREAM) { dev_err(dev, "Expecting SOCK_STREAM - found %d", socket->type); ret = -EINVAL; goto sock_err; } /* unlock and create threads and get tasks */ spin_unlock(&udc->ud.lock); spin_unlock_irqrestore(&udc->lock, flags); tcp_rx = kthread_create(&v_rx_loop, &udc->ud, "vudc_rx"); if (IS_ERR(tcp_rx)) { sockfd_put(socket); mutex_unlock(&udc->ud.sysfs_lock); return -EINVAL; } tcp_tx = kthread_create(&v_tx_loop, &udc->ud, "vudc_tx"); if (IS_ERR(tcp_tx)) { kthread_stop(tcp_rx); sockfd_put(socket); mutex_unlock(&udc->ud.sysfs_lock); return -EINVAL; } /* get task structs now */ get_task_struct(tcp_rx); get_task_struct(tcp_tx); /* lock and update udc->ud state */ spin_lock_irqsave(&udc->lock, flags); spin_lock(&udc->ud.lock); udc->ud.tcp_socket = socket; udc->ud.tcp_rx = tcp_rx; udc->ud.tcp_tx = tcp_tx; udc->ud.status = SDEV_ST_USED; spin_unlock(&udc->ud.lock); ktime_get_ts64(&udc->start_time); v_start_timer(udc); udc->connected = 1; spin_unlock_irqrestore(&udc->lock, flags); wake_up_process(udc->ud.tcp_rx); wake_up_process(udc->ud.tcp_tx); mutex_unlock(&udc->ud.sysfs_lock); return count; } else { if (!udc->connected) { dev_err(dev, "Device not connected"); ret = -EINVAL; goto unlock; } spin_lock(&udc->ud.lock); if (udc->ud.status != SDEV_ST_USED) { ret = -EINVAL; goto unlock_ud; } spin_unlock(&udc->ud.lock); usbip_event_add(&udc->ud, VUDC_EVENT_DOWN); } spin_unlock_irqrestore(&udc->lock, flags); mutex_unlock(&udc->ud.sysfs_lock); return count; sock_err: sockfd_put(socket); unlock_ud: spin_unlock(&udc->ud.lock); unlock: spin_unlock_irqrestore(&udc->lock, flags); mutex_unlock(&udc->ud.sysfs_lock); return ret; } static DEVICE_ATTR_WO(usbip_sockfd); static ssize_t usbip_status_show(struct device *dev, struct device_attribute *attr, char *out) { struct vudc *udc = (struct vudc *) dev_get_drvdata(dev); int status; if (!udc) { dev_err(dev, "no device"); return -ENODEV; } spin_lock_irq(&udc->ud.lock); status = udc->ud.status; spin_unlock_irq(&udc->ud.lock); return sysfs_emit(out, "%d\n", status); } static DEVICE_ATTR_RO(usbip_status); static struct attribute *dev_attrs[] = { &dev_attr_usbip_sockfd.attr, &dev_attr_usbip_status.attr, NULL, }; static struct bin_attribute *dev_bin_attrs[] = { &bin_attr_dev_desc, NULL, }; static const struct attribute_group vudc_attr_group = { .attrs = dev_attrs, .bin_attrs = dev_bin_attrs, }; const struct attribute_group *vudc_groups[] = { &vudc_attr_group, NULL, };
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