Logo Search packages:      
Sourcecode: usbip version File versions  Download package

vhci_sysfs.c

/*
 * $Id: vhci_sysfs.c 67 2008-04-24 11:51:43Z hirofuchi $
 *
 * 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 ?*/


/*-------------------------------------------------------------------------*/

/* client address can be retrieved from /proc/net/tcp with a socket value in
 * vhci's sysfs status */
#if 0
/* FIXME: ipv4 only code */
static int print_client_ip(struct device *dev,
                     struct device_attribute *attr,
                     char *out)
{
      int i;
      int ret;
      int size;
      __u32 ip;
      char *s = out;

      if (!the_controller || !out)
            BUG();

      spin_lock(&the_controller->lock);

      for (i=0; i < VHCI_NPORTS; i++) {
            struct socket *sock;
            struct sockaddr_in inaddr;
            struct vhci_device *vdev = port_to_vdev(i);

            spin_lock(&vdev->ud.lock);
            if( vdev->ud.status != VDEV_ST_USED ) {
                  out += snprintf(out, 15, "%d -\n", i );
                  spin_unlock(&vdev->ud.lock);
                  continue;
            }

            sock = vdev->ud.tcp_socket;
            size = sizeof(struct sockaddr);
            ret = sock->ops->getname(sock,
                        (struct sockaddr *)&inaddr,
                        &size, 
                        0);
            if( ! ret ) {
                  spin_unlock(&vdev->ud.lock);
                  spin_unlock(&the_controller->lock);
                  return -ENODEV;
            }

            ip = ntohl(inaddr.sin_addr.s_addr);
            out += snprintf( out, 15, "%d %u\n", i, ip );
            spin_unlock(&vdev->ud.lock);
      }

      spin_unlock(&the_controller->lock);

      return out-s;
}

static DEVICE_ATTR(client_ip, S_IRUGO, print_client_ip, NULL );
#endif



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

      if (!the_controller || !out)
            BUG();

      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", vdev->udev->dev.bus_id);

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

      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) {
            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) {
            uerr("invalid port %u\n", rhport);
            return -EINVAL;
      }

      err = vhci_port_disconnect(rhport);
      if (err < 0) {
            return -EINVAL;
      }

      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)) {
            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_VARIABLE:
                  break;

            default:
                  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);

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

            uerr("port %d already used\n", rhport);
            return -EINVAL;
      }

      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;

      spin_unlock(&vdev->ud.lock);
      spin_unlock(&the_controller->lock);
      /* end the lock */

      /*
       * this function will sleep, so should be out of the lock. but, it's ok
       * because we already marked vdev as being used. really?
       */
      usbip_start_threads(&vdev->ud);

      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,
      // &dev_attr_client_ip.attr,
      NULL,
};

struct attribute_group dev_attr_group = {
      .attrs = dev_attrs,
};

Generated by  Doxygen 1.6.0   Back to index