Archived
14
0
Fork 0
This repository has been archived on 2022-02-17. You can view files and clone it, but cannot push or open issues or pull requests.
linux-2.6/drivers/staging/usbip/vhci_sysfs.c
Arnd Bergmann 9720b4bc76 staging/usbip: convert to kthread
usbip has its own infrastructure for managing kernel
threads, similar to kthread. By changing it to use
the standard functions, we can simplify the code
and get rid of one of the last BKL users at the
same time.

Includes changes suggested by Max Vozeler.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
Cc: Takahiro Hirofuchi <hirofuchi@users.sourceforge.net>
Cc: Max Vozeler <max@vozeler.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
2011-03-02 16:03:59 -05:00

247 lines
5.9 KiB
C

/*
* Copyright (C) 2003-2008 Takahiro Hirofuchi
*
* This 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 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
#include "usbip_common.h"
#include "vhci.h"
#include <linux/in.h>
/* TODO: refine locking ?*/
/* Sysfs entry to show port status */
static ssize_t show_status(struct device *dev, struct device_attribute *attr,
char *out)
{
char *s = out;
int i = 0;
BUG_ON(!the_controller || !out);
spin_lock(&the_controller->lock);
/*
* output example:
* prt sta spd dev socket local_busid
* 000 004 000 000 c5a7bb80 1-2.3
* 001 004 000 000 d8cee980 2-3.4
*
* IP address can be retrieved from a socket pointer address by looking
* up /proc/net/{tcp,tcp6}. Also, a userland program may remember a
* port number and its peer IP address.
*/
out += sprintf(out, "prt sta spd bus dev socket "
"local_busid\n");
for (i = 0; i < VHCI_NPORTS; i++) {
struct vhci_device *vdev = port_to_vdev(i);
spin_lock(&vdev->ud.lock);
out += sprintf(out, "%03u %03u ", i, vdev->ud.status);
if (vdev->ud.status == VDEV_ST_USED) {
out += sprintf(out, "%03u %08x ",
vdev->speed, vdev->devid);
out += sprintf(out, "%16p ", vdev->ud.tcp_socket);
out += sprintf(out, "%s", dev_name(&vdev->udev->dev));
} else
out += sprintf(out, "000 000 000 0000000000000000 0-0");
out += sprintf(out, "\n");
spin_unlock(&vdev->ud.lock);
}
spin_unlock(&the_controller->lock);
return out - s;
}
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
/* Sysfs entry to shutdown a virtual connection */
static int vhci_port_disconnect(__u32 rhport)
{
struct vhci_device *vdev;
usbip_dbg_vhci_sysfs("enter\n");
/* lock */
spin_lock(&the_controller->lock);
vdev = port_to_vdev(rhport);
spin_lock(&vdev->ud.lock);
if (vdev->ud.status == VDEV_ST_NULL) {
usbip_uerr("not connected %d\n", vdev->ud.status);
/* unlock */
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
return -EINVAL;
}
/* unlock */
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
usbip_event_add(&vdev->ud, VDEV_EVENT_DOWN);
return 0;
}
static ssize_t store_detach(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
int err;
__u32 rhport = 0;
sscanf(buf, "%u", &rhport);
/* check rhport */
if (rhport >= VHCI_NPORTS) {
usbip_uerr("invalid port %u\n", rhport);
return -EINVAL;
}
err = vhci_port_disconnect(rhport);
if (err < 0)
return -EINVAL;
usbip_dbg_vhci_sysfs("Leave\n");
return count;
}
static DEVICE_ATTR(detach, S_IWUSR, NULL, store_detach);
/* Sysfs entry to establish a virtual connection */
static int valid_args(__u32 rhport, enum usb_device_speed speed)
{
/* check rhport */
if ((rhport < 0) || (rhport >= VHCI_NPORTS)) {
usbip_uerr("port %u\n", rhport);
return -EINVAL;
}
/* check speed */
switch (speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
case USB_SPEED_HIGH:
case USB_SPEED_WIRELESS:
break;
default:
usbip_uerr("speed %d\n", speed);
return -EINVAL;
}
return 0;
}
/*
* To start a new USB/IP attachment, a userland program needs to setup a TCP
* connection and then write its socket descriptor with remote device
* information into this sysfs file.
*
* A remote device is virtually attached to the root-hub port of @rhport with
* @speed. @devid is embedded into a request to specify the remote device in a
* server host.
*
* write() returns 0 on success, else negative errno.
*/
static ssize_t store_attach(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct vhci_device *vdev;
struct socket *socket;
int sockfd = 0;
__u32 rhport = 0, devid = 0, speed = 0;
/*
* @rhport: port number of vhci_hcd
* @sockfd: socket descriptor of an established TCP connection
* @devid: unique device identifier in a remote host
* @speed: usb device speed in a remote host
*/
sscanf(buf, "%u %u %u %u", &rhport, &sockfd, &devid, &speed);
usbip_dbg_vhci_sysfs("rhport(%u) sockfd(%u) devid(%u) speed(%u)\n",
rhport, sockfd, devid, speed);
/* check received parameters */
if (valid_args(rhport, speed) < 0)
return -EINVAL;
/* check sockfd */
socket = sockfd_to_socket(sockfd);
if (!socket)
return -EINVAL;
/* now need lock until setting vdev status as used */
/* begin a lock */
spin_lock(&the_controller->lock);
vdev = port_to_vdev(rhport);
spin_lock(&vdev->ud.lock);
if (vdev->ud.status != VDEV_ST_NULL) {
/* end of the lock */
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
usbip_uerr("port %d already used\n", rhport);
return -EINVAL;
}
usbip_uinfo("rhport(%u) sockfd(%d) devid(%u) speed(%u)\n",
rhport, sockfd, devid, speed);
vdev->devid = devid;
vdev->speed = speed;
vdev->ud.tcp_socket = socket;
vdev->ud.status = VDEV_ST_NOTASSIGNED;
wake_up_process(vdev->ud.tcp_rx);
wake_up_process(vdev->ud.tcp_tx);
spin_unlock(&vdev->ud.lock);
spin_unlock(&the_controller->lock);
/* end the lock */
rh_port_connect(rhport, speed);
return count;
}
static DEVICE_ATTR(attach, S_IWUSR, NULL, store_attach);
static struct attribute *dev_attrs[] = {
&dev_attr_status.attr,
&dev_attr_detach.attr,
&dev_attr_attach.attr,
&dev_attr_usbip_debug.attr,
NULL,
};
struct attribute_group dev_attr_group = {
.attrs = dev_attrs,
};