18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2015 Karol Kosik <karo9@interia.eu> 48c2ecf20Sopenharmony_ci * Copyright (C) 2015-2016 Samsung Electronics 58c2ecf20Sopenharmony_ci * Igor Kotrasinski <i.kotrasinsk@samsung.com> 68c2ecf20Sopenharmony_ci * Krzysztof Opasiak <k.opasiak@samsung.com> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/list.h> 118c2ecf20Sopenharmony_ci#include <linux/usb/gadget.h> 128c2ecf20Sopenharmony_ci#include <linux/usb/ch9.h> 138c2ecf20Sopenharmony_ci#include <linux/sysfs.h> 148c2ecf20Sopenharmony_ci#include <linux/kthread.h> 158c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "usbip_common.h" 188c2ecf20Sopenharmony_ci#include "vudc.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include <net/sock.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* called with udc->lock held */ 238c2ecf20Sopenharmony_ciint get_gadget_descs(struct vudc *udc) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct vrequest *usb_req; 268c2ecf20Sopenharmony_ci struct vep *ep0 = to_vep(udc->gadget.ep0); 278c2ecf20Sopenharmony_ci struct usb_device_descriptor *ddesc = &udc->dev_desc; 288c2ecf20Sopenharmony_ci struct usb_ctrlrequest req; 298c2ecf20Sopenharmony_ci int ret; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci if (!udc->driver || !udc->pullup) 328c2ecf20Sopenharmony_ci return -EINVAL; 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci req.bRequestType = USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE; 358c2ecf20Sopenharmony_ci req.bRequest = USB_REQ_GET_DESCRIPTOR; 368c2ecf20Sopenharmony_ci req.wValue = cpu_to_le16(USB_DT_DEVICE << 8); 378c2ecf20Sopenharmony_ci req.wIndex = cpu_to_le16(0); 388c2ecf20Sopenharmony_ci req.wLength = cpu_to_le16(sizeof(*ddesc)); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci spin_unlock(&udc->lock); 418c2ecf20Sopenharmony_ci ret = udc->driver->setup(&(udc->gadget), &req); 428c2ecf20Sopenharmony_ci spin_lock(&udc->lock); 438c2ecf20Sopenharmony_ci if (ret < 0) 448c2ecf20Sopenharmony_ci goto out; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* assuming request queue is empty; request is now on top */ 478c2ecf20Sopenharmony_ci usb_req = list_last_entry(&ep0->req_queue, struct vrequest, req_entry); 488c2ecf20Sopenharmony_ci list_del(&usb_req->req_entry); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci if (usb_req->req.length > sizeof(*ddesc)) { 518c2ecf20Sopenharmony_ci ret = -EOVERFLOW; 528c2ecf20Sopenharmony_ci goto giveback_req; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci memcpy(ddesc, usb_req->req.buf, sizeof(*ddesc)); 568c2ecf20Sopenharmony_ci udc->desc_cached = 1; 578c2ecf20Sopenharmony_ci ret = 0; 588c2ecf20Sopenharmony_cigiveback_req: 598c2ecf20Sopenharmony_ci usb_req->req.status = 0; 608c2ecf20Sopenharmony_ci usb_req->req.actual = usb_req->req.length; 618c2ecf20Sopenharmony_ci usb_gadget_giveback_request(&(ep0->ep), &(usb_req->req)); 628c2ecf20Sopenharmony_ciout: 638c2ecf20Sopenharmony_ci return ret; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* 678c2ecf20Sopenharmony_ci * Exposes device descriptor from the gadget driver. 688c2ecf20Sopenharmony_ci */ 698c2ecf20Sopenharmony_cistatic ssize_t dev_desc_read(struct file *file, struct kobject *kobj, 708c2ecf20Sopenharmony_ci struct bin_attribute *attr, char *out, 718c2ecf20Sopenharmony_ci loff_t off, size_t count) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 748c2ecf20Sopenharmony_ci struct vudc *udc = (struct vudc *)dev_get_drvdata(dev); 758c2ecf20Sopenharmony_ci char *desc_ptr = (char *) &udc->dev_desc; 768c2ecf20Sopenharmony_ci unsigned long flags; 778c2ecf20Sopenharmony_ci int ret; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 808c2ecf20Sopenharmony_ci if (!udc->desc_cached) { 818c2ecf20Sopenharmony_ci ret = -ENODEV; 828c2ecf20Sopenharmony_ci goto unlock; 838c2ecf20Sopenharmony_ci } 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci memcpy(out, desc_ptr + off, count); 868c2ecf20Sopenharmony_ci ret = count; 878c2ecf20Sopenharmony_ciunlock: 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 898c2ecf20Sopenharmony_ci return ret; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_cistatic BIN_ATTR_RO(dev_desc, sizeof(struct usb_device_descriptor)); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic ssize_t usbip_sockfd_store(struct device *dev, 948c2ecf20Sopenharmony_ci struct device_attribute *attr, 958c2ecf20Sopenharmony_ci const char *in, size_t count) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct vudc *udc = (struct vudc *) dev_get_drvdata(dev); 988c2ecf20Sopenharmony_ci int rv; 998c2ecf20Sopenharmony_ci int sockfd = 0; 1008c2ecf20Sopenharmony_ci int err; 1018c2ecf20Sopenharmony_ci struct socket *socket; 1028c2ecf20Sopenharmony_ci unsigned long flags; 1038c2ecf20Sopenharmony_ci int ret; 1048c2ecf20Sopenharmony_ci struct task_struct *tcp_rx = NULL; 1058c2ecf20Sopenharmony_ci struct task_struct *tcp_tx = NULL; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci rv = kstrtoint(in, 0, &sockfd); 1088c2ecf20Sopenharmony_ci if (rv != 0) 1098c2ecf20Sopenharmony_ci return -EINVAL; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!udc) { 1128c2ecf20Sopenharmony_ci dev_err(dev, "no device"); 1138c2ecf20Sopenharmony_ci return -ENODEV; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci mutex_lock(&udc->ud.sysfs_lock); 1168c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 1178c2ecf20Sopenharmony_ci /* Don't export what we don't have */ 1188c2ecf20Sopenharmony_ci if (!udc->driver || !udc->pullup) { 1198c2ecf20Sopenharmony_ci dev_err(dev, "gadget not bound"); 1208c2ecf20Sopenharmony_ci ret = -ENODEV; 1218c2ecf20Sopenharmony_ci goto unlock; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if (sockfd != -1) { 1258c2ecf20Sopenharmony_ci if (udc->connected) { 1268c2ecf20Sopenharmony_ci dev_err(dev, "Device already connected"); 1278c2ecf20Sopenharmony_ci ret = -EBUSY; 1288c2ecf20Sopenharmony_ci goto unlock; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci spin_lock_irq(&udc->ud.lock); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci if (udc->ud.status != SDEV_ST_AVAILABLE) { 1348c2ecf20Sopenharmony_ci ret = -EINVAL; 1358c2ecf20Sopenharmony_ci goto unlock_ud; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci socket = sockfd_lookup(sockfd, &err); 1398c2ecf20Sopenharmony_ci if (!socket) { 1408c2ecf20Sopenharmony_ci dev_err(dev, "failed to lookup sock"); 1418c2ecf20Sopenharmony_ci ret = -EINVAL; 1428c2ecf20Sopenharmony_ci goto unlock_ud; 1438c2ecf20Sopenharmony_ci } 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci if (socket->type != SOCK_STREAM) { 1468c2ecf20Sopenharmony_ci dev_err(dev, "Expecting SOCK_STREAM - found %d", 1478c2ecf20Sopenharmony_ci socket->type); 1488c2ecf20Sopenharmony_ci ret = -EINVAL; 1498c2ecf20Sopenharmony_ci goto sock_err; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* unlock and create threads and get tasks */ 1538c2ecf20Sopenharmony_ci spin_unlock_irq(&udc->ud.lock); 1548c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci tcp_rx = kthread_create(&v_rx_loop, &udc->ud, "vudc_rx"); 1578c2ecf20Sopenharmony_ci if (IS_ERR(tcp_rx)) { 1588c2ecf20Sopenharmony_ci sockfd_put(socket); 1598c2ecf20Sopenharmony_ci mutex_unlock(&udc->ud.sysfs_lock); 1608c2ecf20Sopenharmony_ci return -EINVAL; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci tcp_tx = kthread_create(&v_tx_loop, &udc->ud, "vudc_tx"); 1638c2ecf20Sopenharmony_ci if (IS_ERR(tcp_tx)) { 1648c2ecf20Sopenharmony_ci kthread_stop(tcp_rx); 1658c2ecf20Sopenharmony_ci sockfd_put(socket); 1668c2ecf20Sopenharmony_ci mutex_unlock(&udc->ud.sysfs_lock); 1678c2ecf20Sopenharmony_ci return -EINVAL; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci /* get task structs now */ 1718c2ecf20Sopenharmony_ci get_task_struct(tcp_rx); 1728c2ecf20Sopenharmony_ci get_task_struct(tcp_tx); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* lock and update udc->ud state */ 1758c2ecf20Sopenharmony_ci spin_lock_irqsave(&udc->lock, flags); 1768c2ecf20Sopenharmony_ci spin_lock_irq(&udc->ud.lock); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci udc->ud.tcp_socket = socket; 1798c2ecf20Sopenharmony_ci udc->ud.tcp_rx = tcp_rx; 1808c2ecf20Sopenharmony_ci udc->ud.tcp_tx = tcp_tx; 1818c2ecf20Sopenharmony_ci udc->ud.status = SDEV_ST_USED; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci spin_unlock_irq(&udc->ud.lock); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci ktime_get_ts64(&udc->start_time); 1868c2ecf20Sopenharmony_ci v_start_timer(udc); 1878c2ecf20Sopenharmony_ci udc->connected = 1; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci wake_up_process(udc->ud.tcp_rx); 1928c2ecf20Sopenharmony_ci wake_up_process(udc->ud.tcp_tx); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci mutex_unlock(&udc->ud.sysfs_lock); 1958c2ecf20Sopenharmony_ci return count; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci } else { 1988c2ecf20Sopenharmony_ci if (!udc->connected) { 1998c2ecf20Sopenharmony_ci dev_err(dev, "Device not connected"); 2008c2ecf20Sopenharmony_ci ret = -EINVAL; 2018c2ecf20Sopenharmony_ci goto unlock; 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci spin_lock_irq(&udc->ud.lock); 2058c2ecf20Sopenharmony_ci if (udc->ud.status != SDEV_ST_USED) { 2068c2ecf20Sopenharmony_ci ret = -EINVAL; 2078c2ecf20Sopenharmony_ci goto unlock_ud; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci spin_unlock_irq(&udc->ud.lock); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci usbip_event_add(&udc->ud, VUDC_EVENT_DOWN); 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 2158c2ecf20Sopenharmony_ci mutex_unlock(&udc->ud.sysfs_lock); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return count; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cisock_err: 2208c2ecf20Sopenharmony_ci sockfd_put(socket); 2218c2ecf20Sopenharmony_ciunlock_ud: 2228c2ecf20Sopenharmony_ci spin_unlock_irq(&udc->ud.lock); 2238c2ecf20Sopenharmony_ciunlock: 2248c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&udc->lock, flags); 2258c2ecf20Sopenharmony_ci mutex_unlock(&udc->ud.sysfs_lock); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci return ret; 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_cistatic DEVICE_ATTR_WO(usbip_sockfd); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_cistatic ssize_t usbip_status_show(struct device *dev, 2328c2ecf20Sopenharmony_ci struct device_attribute *attr, char *out) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct vudc *udc = (struct vudc *) dev_get_drvdata(dev); 2358c2ecf20Sopenharmony_ci int status; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (!udc) { 2388c2ecf20Sopenharmony_ci dev_err(dev, "no device"); 2398c2ecf20Sopenharmony_ci return -ENODEV; 2408c2ecf20Sopenharmony_ci } 2418c2ecf20Sopenharmony_ci spin_lock_irq(&udc->ud.lock); 2428c2ecf20Sopenharmony_ci status = udc->ud.status; 2438c2ecf20Sopenharmony_ci spin_unlock_irq(&udc->ud.lock); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci return snprintf(out, PAGE_SIZE, "%d\n", status); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(usbip_status); 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_cistatic struct attribute *dev_attrs[] = { 2508c2ecf20Sopenharmony_ci &dev_attr_usbip_sockfd.attr, 2518c2ecf20Sopenharmony_ci &dev_attr_usbip_status.attr, 2528c2ecf20Sopenharmony_ci NULL, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cistatic struct bin_attribute *dev_bin_attrs[] = { 2568c2ecf20Sopenharmony_ci &bin_attr_dev_desc, 2578c2ecf20Sopenharmony_ci NULL, 2588c2ecf20Sopenharmony_ci}; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic const struct attribute_group vudc_attr_group = { 2618c2ecf20Sopenharmony_ci .attrs = dev_attrs, 2628c2ecf20Sopenharmony_ci .bin_attrs = dev_bin_attrs, 2638c2ecf20Sopenharmony_ci}; 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ciconst struct attribute_group *vudc_groups[] = { 2668c2ecf20Sopenharmony_ci &vudc_attr_group, 2678c2ecf20Sopenharmony_ci NULL, 2688c2ecf20Sopenharmony_ci}; 269