mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 05:11:48 +00:00
91148dbad8
Platform drivers now have the option to have the platform core create and remove any needed sysfs attribute files. So take advantage of that and do not register "by hand" any sysfs files. Cc: Valentina Manea <valentina.manea.m@gmail.com> Acked-by: Shuah Khan <skhan@linuxfoundation.org> Link: https://lore.kernel.org/r/20190805193636.25560-5-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
227 lines
5.0 KiB
C
227 lines
5.0 KiB
C
// 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;
|
|
|
|
rv = kstrtoint(in, 0, &sockfd);
|
|
if (rv != 0)
|
|
return -EINVAL;
|
|
|
|
if (!udc) {
|
|
dev_err(dev, "no device");
|
|
return -ENODEV;
|
|
}
|
|
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_irq(&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;
|
|
}
|
|
|
|
udc->ud.tcp_socket = socket;
|
|
|
|
spin_unlock_irq(&udc->ud.lock);
|
|
spin_unlock_irqrestore(&udc->lock, flags);
|
|
|
|
udc->ud.tcp_rx = kthread_get_run(&v_rx_loop,
|
|
&udc->ud, "vudc_rx");
|
|
udc->ud.tcp_tx = kthread_get_run(&v_tx_loop,
|
|
&udc->ud, "vudc_tx");
|
|
|
|
spin_lock_irqsave(&udc->lock, flags);
|
|
spin_lock_irq(&udc->ud.lock);
|
|
udc->ud.status = SDEV_ST_USED;
|
|
spin_unlock_irq(&udc->ud.lock);
|
|
|
|
ktime_get_ts64(&udc->start_time);
|
|
v_start_timer(udc);
|
|
udc->connected = 1;
|
|
} else {
|
|
if (!udc->connected) {
|
|
dev_err(dev, "Device not connected");
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
spin_lock_irq(&udc->ud.lock);
|
|
if (udc->ud.status != SDEV_ST_USED) {
|
|
ret = -EINVAL;
|
|
goto unlock_ud;
|
|
}
|
|
spin_unlock_irq(&udc->ud.lock);
|
|
|
|
usbip_event_add(&udc->ud, VUDC_EVENT_DOWN);
|
|
}
|
|
|
|
spin_unlock_irqrestore(&udc->lock, flags);
|
|
|
|
return count;
|
|
|
|
unlock_ud:
|
|
spin_unlock_irq(&udc->ud.lock);
|
|
unlock:
|
|
spin_unlock_irqrestore(&udc->lock, flags);
|
|
|
|
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 snprintf(out, PAGE_SIZE, "%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,
|
|
};
|