symbian-qemu-0.9.1-12/qemu-symbian-svp/usb-bsd.c
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 /*
       
     2  * BSD host USB redirector
       
     3  *
       
     4  * Copyright (c) 2006 Lonnie Mendez
       
     5  * Portions of code and concepts borrowed from
       
     6  * usb-linux.c and libusb's bsd.c and are copyright their respective owners.
       
     7  *
       
     8  * Permission is hereby granted, free of charge, to any person obtaining a copy
       
     9  * of this software and associated documentation files (the "Software"), to deal
       
    10  * in the Software without restriction, including without limitation the rights
       
    11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       
    12  * copies of the Software, and to permit persons to whom the Software is
       
    13  * furnished to do so, subject to the following conditions:
       
    14  *
       
    15  * The above copyright notice and this permission notice shall be included in
       
    16  * all copies or substantial portions of the Software.
       
    17  *
       
    18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
       
    19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
       
    20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
       
    21  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
       
    22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
       
    23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
       
    24  * THE SOFTWARE.
       
    25  */
       
    26 
       
    27 #include "qemu-common.h"
       
    28 #include "console.h"
       
    29 #include "hw/usb.h"
       
    30 
       
    31 /* usb.h declares these */
       
    32 #undef USB_SPEED_HIGH
       
    33 #undef USB_SPEED_FULL
       
    34 #undef USB_SPEED_LOW
       
    35 
       
    36 #include <sys/ioctl.h>
       
    37 #include <dev/usb/usb.h>
       
    38 #include <signal.h>
       
    39 
       
    40 /* This value has maximum potential at 16.
       
    41  * You should also set hw.usb.debug to gain
       
    42  * more detailed view.
       
    43  */
       
    44 //#define DEBUG
       
    45 #define UGEN_DEBUG_LEVEL 0
       
    46 
       
    47 
       
    48 typedef int USBScanFunc(void *opaque, int bus_num, int addr, int class_id,
       
    49                         int vendor_id, int product_id,
       
    50                         const char *product_name, int speed);
       
    51 static int usb_host_find_device(int *pbus_num, int *paddr,
       
    52                                 const char *devname);
       
    53 
       
    54 typedef struct USBHostDevice {
       
    55     USBDevice dev;
       
    56     int ep_fd[USB_MAX_ENDPOINTS];
       
    57     int devfd;
       
    58     char devpath[32];
       
    59 } USBHostDevice;
       
    60 
       
    61 
       
    62 static int ensure_ep_open(USBHostDevice *dev, int ep, int mode)
       
    63 {
       
    64     char buf[32];
       
    65     int fd;
       
    66 
       
    67     /* Get the address for this endpoint */
       
    68     ep = UE_GET_ADDR(ep);
       
    69 
       
    70     if (dev->ep_fd[ep] < 0) {
       
    71 #ifdef __FreeBSD__
       
    72         snprintf(buf, sizeof(buf) - 1, "%s.%d", dev->devpath, ep);
       
    73 #else
       
    74         snprintf(buf, sizeof(buf) - 1, "%s.%02d", dev->devpath, ep);
       
    75 #endif
       
    76         /* Try to open it O_RDWR first for those devices which have in and out
       
    77          * endpoints with the same address (eg 0x02 and 0x82)
       
    78          */
       
    79         fd = open(buf, O_RDWR);
       
    80         if (fd < 0 && errno == ENXIO)
       
    81             fd = open(buf, mode);
       
    82         if (fd < 0) {
       
    83 #ifdef DEBUG
       
    84             printf("ensure_ep_open: failed to open device endpoint %s: %s\n",
       
    85                    buf, strerror(errno));
       
    86 #endif
       
    87         }
       
    88         dev->ep_fd[ep] = fd;
       
    89     }
       
    90 
       
    91     return dev->ep_fd[ep];
       
    92 }
       
    93 
       
    94 static void ensure_eps_closed(USBHostDevice *dev)
       
    95 {
       
    96     int epnum = 1;
       
    97 
       
    98     if (!dev)
       
    99         return;
       
   100 
       
   101     while (epnum < USB_MAX_ENDPOINTS) {
       
   102         if (dev->ep_fd[epnum] >= 0) {
       
   103             close(dev->ep_fd[epnum]);
       
   104             dev->ep_fd[epnum] = -1;
       
   105         }
       
   106         epnum++;
       
   107     }
       
   108 }
       
   109 
       
   110 static void usb_host_handle_reset(USBDevice *dev)
       
   111 {
       
   112 #if 0
       
   113     USBHostDevice *s = (USBHostDevice *)dev;
       
   114 #endif
       
   115 }
       
   116 
       
   117 /* XXX:
       
   118  * -check device states against transfer requests
       
   119  *  and return appropriate response
       
   120  */
       
   121 static int usb_host_handle_control(USBDevice *dev,
       
   122                                    int request,
       
   123                                    int value,
       
   124                                    int index,
       
   125                                    int length,
       
   126                                    uint8_t *data)
       
   127 {
       
   128     USBHostDevice *s = (USBHostDevice *)dev;
       
   129     struct usb_ctl_request req;
       
   130     struct usb_alt_interface aiface;
       
   131     int ret, timeout = 50;
       
   132 
       
   133     if ((request >> 8) == UT_WRITE_DEVICE &&
       
   134         (request & 0xff) == UR_SET_ADDRESS) {
       
   135 
       
   136         /* specific SET_ADDRESS support */
       
   137         dev->addr = value;
       
   138         return 0;
       
   139     } else if ((request >> 8) == UT_WRITE_DEVICE &&
       
   140                (request & 0xff) == UR_SET_CONFIG) {
       
   141 
       
   142         ensure_eps_closed(s); /* can't do this without all eps closed */
       
   143 
       
   144         ret = ioctl(s->devfd, USB_SET_CONFIG, &value);
       
   145         if (ret < 0) {
       
   146 #ifdef DEBUG
       
   147             printf("handle_control: failed to set configuration - %s\n",
       
   148                    strerror(errno));
       
   149 #endif
       
   150             return USB_RET_STALL;
       
   151         }
       
   152 
       
   153         return 0;
       
   154     } else if ((request >> 8) == UT_WRITE_INTERFACE &&
       
   155                (request & 0xff) == UR_SET_INTERFACE) {
       
   156 
       
   157         aiface.uai_interface_index = index;
       
   158         aiface.uai_alt_no = value;
       
   159 
       
   160         ensure_eps_closed(s); /* can't do this without all eps closed */
       
   161         ret = ioctl(s->devfd, USB_SET_ALTINTERFACE, &aiface);
       
   162         if (ret < 0) {
       
   163 #ifdef DEBUG
       
   164             printf("handle_control: failed to set alternate interface - %s\n",
       
   165                    strerror(errno));
       
   166 #endif
       
   167             return USB_RET_STALL;
       
   168         }
       
   169 
       
   170         return 0;
       
   171     } else {
       
   172         req.ucr_request.bmRequestType = request >> 8;
       
   173         req.ucr_request.bRequest = request & 0xff;
       
   174         USETW(req.ucr_request.wValue, value);
       
   175         USETW(req.ucr_request.wIndex, index);
       
   176         USETW(req.ucr_request.wLength, length);
       
   177         req.ucr_data = data;
       
   178         req.ucr_flags = USBD_SHORT_XFER_OK;
       
   179 
       
   180         ret = ioctl(s->devfd, USB_SET_TIMEOUT, &timeout);
       
   181 #if (__NetBSD__ || __OpenBSD__)
       
   182         if (ret < 0 && errno != EINVAL) {
       
   183 #else
       
   184         if (ret < 0) {
       
   185 #endif
       
   186 #ifdef DEBUG
       
   187             printf("handle_control: setting timeout failed - %s\n",
       
   188                    strerror(errno));
       
   189 #endif
       
   190         }
       
   191 
       
   192         ret = ioctl(s->devfd, USB_DO_REQUEST, &req);
       
   193         /* ugen returns EIO for usbd_do_request_ no matter what
       
   194          * happens with the transfer */
       
   195         if (ret < 0) {
       
   196 #ifdef DEBUG
       
   197             printf("handle_control: error after request - %s\n",
       
   198                    strerror(errno));
       
   199 #endif
       
   200             return USB_RET_NAK; // STALL
       
   201         } else {
       
   202             return req.ucr_actlen;
       
   203         }
       
   204     }
       
   205 }
       
   206 
       
   207 static int usb_host_handle_data(USBDevice *dev, USBPacket *p)
       
   208 {
       
   209     USBHostDevice *s = (USBHostDevice *)dev;
       
   210     int ret, fd, mode;
       
   211     int one = 1, shortpacket = 0, timeout = 50;
       
   212     sigset_t new_mask, old_mask;
       
   213     uint8_t devep = p->devep;
       
   214 
       
   215     /* protect data transfers from SIGALRM signal */
       
   216     sigemptyset(&new_mask);
       
   217     sigaddset(&new_mask, SIGALRM);
       
   218     sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
       
   219 
       
   220     if (p->pid == USB_TOKEN_IN) {
       
   221         devep |= 0x80;
       
   222         mode = O_RDONLY;
       
   223         shortpacket = 1;
       
   224     } else {
       
   225         mode = O_WRONLY;
       
   226     }
       
   227 
       
   228     fd = ensure_ep_open(s, devep, mode);
       
   229     if (fd < 0) {
       
   230         sigprocmask(SIG_SETMASK, &old_mask, NULL);
       
   231         return USB_RET_NODEV;
       
   232     }
       
   233 
       
   234     if (ioctl(fd, USB_SET_TIMEOUT, &timeout) < 0) {
       
   235 #ifdef DEBUG
       
   236         printf("handle_data: failed to set timeout - %s\n",
       
   237                strerror(errno));
       
   238 #endif
       
   239     }
       
   240 
       
   241     if (shortpacket) {
       
   242         if (ioctl(fd, USB_SET_SHORT_XFER, &one) < 0) {
       
   243 #ifdef DEBUG
       
   244             printf("handle_data: failed to set short xfer mode - %s\n",
       
   245                    strerror(errno));
       
   246 #endif
       
   247             sigprocmask(SIG_SETMASK, &old_mask, NULL);
       
   248         }
       
   249     }
       
   250 
       
   251     if (p->pid == USB_TOKEN_IN)
       
   252         ret = read(fd, p->data, p->len);
       
   253     else
       
   254         ret = write(fd, p->data, p->len);
       
   255 
       
   256     sigprocmask(SIG_SETMASK, &old_mask, NULL);
       
   257 
       
   258     if (ret < 0) {
       
   259 #ifdef DEBUG
       
   260         printf("handle_data: error after %s data - %s\n",
       
   261                pid == USB_TOKEN_IN ? "reading" : "writing", strerror(errno));
       
   262 #endif
       
   263         switch(errno) {
       
   264         case ETIMEDOUT:
       
   265         case EINTR:
       
   266             return USB_RET_NAK;
       
   267         default:
       
   268             return USB_RET_STALL;
       
   269         }
       
   270     } else {
       
   271         return ret;
       
   272     }
       
   273 }
       
   274 
       
   275 static void usb_host_handle_destroy(USBDevice *opaque)
       
   276 {
       
   277     USBHostDevice *s = (USBHostDevice *)opaque;
       
   278     int i;
       
   279 
       
   280     for (i = 0; i < USB_MAX_ENDPOINTS; i++)
       
   281         if (s->ep_fd[i] >= 0)
       
   282             close(s->ep_fd[i]);
       
   283 
       
   284     if (s->devfd < 0)
       
   285         return;
       
   286 
       
   287     close(s->devfd);
       
   288 
       
   289     qemu_free(s);
       
   290 }
       
   291 
       
   292 USBDevice *usb_host_device_open(const char *devname)
       
   293 {
       
   294     struct usb_device_info bus_info, dev_info;
       
   295     USBHostDevice *dev;
       
   296     char ctlpath[PATH_MAX + 1];
       
   297     char buspath[PATH_MAX + 1];
       
   298     int bfd, dfd, bus, address, i;
       
   299     int ugendebug = UGEN_DEBUG_LEVEL;
       
   300 
       
   301     if (usb_host_find_device(&bus, &address, devname) < 0)
       
   302         return NULL;
       
   303 
       
   304     snprintf(buspath, PATH_MAX, "/dev/usb%d", bus);
       
   305 
       
   306     bfd = open(buspath, O_RDWR);
       
   307     if (bfd < 0) {
       
   308 #ifdef DEBUG
       
   309         printf("usb_host_device_open: failed to open usb bus - %s\n",
       
   310                strerror(errno));
       
   311 #endif
       
   312         return NULL;
       
   313     }
       
   314 
       
   315     bus_info.udi_addr = address;
       
   316     if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0) {
       
   317 #ifdef DEBUG
       
   318         printf("usb_host_device_open: failed to grab bus information - %s\n",
       
   319                strerror(errno));
       
   320 #endif
       
   321         return NULL;
       
   322     }
       
   323 
       
   324 #ifdef __FreeBSD__
       
   325     snprintf(ctlpath, PATH_MAX, "/dev/%s", bus_info.udi_devnames[0]);
       
   326 #else
       
   327     snprintf(ctlpath, PATH_MAX, "/dev/%s.00", bus_info.udi_devnames[0]);
       
   328 #endif
       
   329 
       
   330     dfd  = open(ctlpath, O_RDWR);
       
   331     if (dfd < 0) {
       
   332         dfd = open(ctlpath, O_RDONLY);
       
   333         if (dfd < 0) {
       
   334 #ifdef DEBUG
       
   335             printf("usb_host_device_open: failed to open usb device %s - %s\n",
       
   336                    ctlpath, strerror(errno));
       
   337 #endif
       
   338         }
       
   339     }
       
   340 
       
   341     if (dfd >= 0) {
       
   342         dev = qemu_mallocz(sizeof(USBHostDevice));
       
   343         if (!dev)
       
   344             goto fail;
       
   345         dev->devfd = dfd;
       
   346 
       
   347         if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0) {
       
   348 #ifdef DEBUG
       
   349             printf("usb_host_device_open: failed to grab device info - %s\n",
       
   350                    strerror(errno));
       
   351 #endif
       
   352             goto fail;
       
   353         }
       
   354 
       
   355         if (dev_info.udi_speed == 1)
       
   356             dev->dev.speed = USB_SPEED_LOW - 1;
       
   357         else
       
   358             dev->dev.speed = USB_SPEED_FULL - 1;
       
   359 
       
   360         dev->dev.handle_packet = usb_generic_handle_packet;
       
   361 
       
   362         dev->dev.handle_reset = usb_host_handle_reset;
       
   363         dev->dev.handle_control = usb_host_handle_control;
       
   364         dev->dev.handle_data = usb_host_handle_data;
       
   365         dev->dev.handle_destroy = usb_host_handle_destroy;
       
   366 
       
   367         if (strncmp(dev_info.udi_product, "product", 7) != 0)
       
   368             pstrcpy(dev->dev.devname, sizeof(dev->dev.devname),
       
   369                     dev_info.udi_product);
       
   370         else
       
   371             snprintf(dev->dev.devname, sizeof(dev->dev.devname),
       
   372                      "host:%s", devname);
       
   373 
       
   374         pstrcpy(dev->devpath, sizeof(dev->devpath), "/dev/");
       
   375 	strcat(dev->devpath, dev_info.udi_devnames[0]);
       
   376 
       
   377         /* Mark the endpoints as not yet open */
       
   378         for (i = 0; i < USB_MAX_ENDPOINTS; i++)
       
   379            dev->ep_fd[i] = -1;
       
   380 
       
   381         ioctl(dfd, USB_SETDEBUG, &ugendebug);
       
   382 
       
   383         return (USBDevice *)dev;
       
   384     }
       
   385 
       
   386 fail:
       
   387     return NULL;
       
   388 }
       
   389 
       
   390 static int usb_host_scan(void *opaque, USBScanFunc *func)
       
   391 {
       
   392     struct usb_device_info bus_info;
       
   393     struct usb_device_info dev_info;
       
   394     uint16_t vendor_id, product_id, class_id, speed;
       
   395     int bfd, dfd, bus, address;
       
   396     char busbuf[20], devbuf[20], product_name[256];
       
   397     int ret = 0;
       
   398 
       
   399     for (bus = 0; bus < 10; bus++) {
       
   400 
       
   401         snprintf(busbuf, sizeof(busbuf) - 1, "/dev/usb%d", bus);
       
   402         bfd = open(busbuf, O_RDWR);
       
   403         if (bfd < 0)
       
   404 	    continue;
       
   405 
       
   406         for (address = 1; address < 127; address++) {
       
   407 
       
   408             bus_info.udi_addr = address;
       
   409             if (ioctl(bfd, USB_DEVICEINFO, &bus_info) < 0)
       
   410                 continue;
       
   411 
       
   412             /* only list devices that can be used by generic layer */
       
   413             if (strncmp(bus_info.udi_devnames[0], "ugen", 4) != 0)
       
   414                 continue;
       
   415 
       
   416 #ifdef __FreeBSD__
       
   417             snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s", bus_info.udi_devnames[0]);
       
   418 #else
       
   419             snprintf(devbuf, sizeof(devbuf) - 1, "/dev/%s.00", bus_info.udi_devnames[0]);
       
   420 #endif
       
   421 
       
   422             dfd = open(devbuf, O_RDONLY);
       
   423             if (dfd < 0) {
       
   424 #ifdef DEBUG
       
   425                 printf("usb_host_scan: couldn't open device %s - %s\n", devbuf,
       
   426                        strerror(errno));
       
   427 #endif
       
   428                 continue;
       
   429             }
       
   430 
       
   431             if (ioctl(dfd, USB_GET_DEVICEINFO, &dev_info) < 0)
       
   432                 printf("usb_host_scan: couldn't get device information for %s - %s\n",
       
   433                        devbuf, strerror(errno));
       
   434 
       
   435             // XXX: might need to fixup endianess of word values before copying over
       
   436 
       
   437             vendor_id = dev_info.udi_vendorNo;
       
   438             product_id = dev_info.udi_productNo;
       
   439             class_id = dev_info.udi_class;
       
   440             speed = dev_info.udi_speed;
       
   441 
       
   442             if (strncmp(dev_info.udi_product, "product", 7) != 0)
       
   443                 pstrcpy(product_name, sizeof(product_name),
       
   444                         dev_info.udi_product);
       
   445             else
       
   446                 product_name[0] = '\0';
       
   447 
       
   448             ret = func(opaque, bus, address, class_id, vendor_id,
       
   449                        product_id, product_name, speed);
       
   450 
       
   451             close(dfd);
       
   452 
       
   453             if (ret)
       
   454                 goto the_end;
       
   455         }
       
   456 
       
   457         close(bfd);
       
   458     }
       
   459 
       
   460 the_end:
       
   461     return ret;
       
   462 }
       
   463 
       
   464 typedef struct FindDeviceState {
       
   465     int vendor_id;
       
   466     int product_id;
       
   467     int bus_num;
       
   468     int addr;
       
   469 } FindDeviceState;
       
   470 
       
   471 static int usb_host_find_device_scan(void *opaque, int bus_num, int addr,
       
   472                                      int class_id,
       
   473                                      int vendor_id, int product_id,
       
   474                                      const char *product_name, int speed)
       
   475 {
       
   476     FindDeviceState *s = opaque;
       
   477     if (vendor_id == s->vendor_id &&
       
   478         product_id == s->product_id) {
       
   479         s->bus_num = bus_num;
       
   480         s->addr = addr;
       
   481         return 1;
       
   482      } else {
       
   483         return 0;
       
   484      }
       
   485 }
       
   486 
       
   487 
       
   488 /* the syntax is :
       
   489    'bus.addr' (decimal numbers) or
       
   490    'vendor_id:product_id' (hexa numbers) */
       
   491 static int usb_host_find_device(int *pbus_num, int *paddr,
       
   492                                 const char *devname)
       
   493 {
       
   494     const char *p;
       
   495     int ret;
       
   496     FindDeviceState fs;
       
   497 
       
   498     p = strchr(devname, '.');
       
   499     if (p) {
       
   500         *pbus_num = strtoul(devname, NULL, 0);
       
   501         *paddr = strtoul(p + 1, NULL, 0);
       
   502         return 0;
       
   503     }
       
   504     p = strchr(devname, ':');
       
   505     if (p) {
       
   506         fs.vendor_id = strtoul(devname, NULL, 16);
       
   507         fs.product_id = strtoul(p + 1, NULL, 16);
       
   508         ret = usb_host_scan(&fs, usb_host_find_device_scan);
       
   509         if (ret) {
       
   510             *pbus_num = fs.bus_num;
       
   511             *paddr = fs.addr;
       
   512             return 0;
       
   513         }
       
   514      }
       
   515      return -1;
       
   516 }
       
   517 
       
   518 /**********************/
       
   519 /* USB host device info */
       
   520 
       
   521 struct usb_class_info {
       
   522     int class;
       
   523     const char *class_name;
       
   524 };
       
   525 
       
   526 static const struct usb_class_info usb_class_info[] = {
       
   527     { USB_CLASS_AUDIO, "Audio"},
       
   528     { USB_CLASS_COMM, "Communication"},
       
   529     { USB_CLASS_HID, "HID"},
       
   530     { USB_CLASS_HUB, "Hub" },
       
   531     { USB_CLASS_PHYSICAL, "Physical" },
       
   532     { USB_CLASS_PRINTER, "Printer" },
       
   533     { USB_CLASS_MASS_STORAGE, "Storage" },
       
   534     { USB_CLASS_CDC_DATA, "Data" },
       
   535     { USB_CLASS_APP_SPEC, "Application Specific" },
       
   536     { USB_CLASS_VENDOR_SPEC, "Vendor Specific" },
       
   537     { USB_CLASS_STILL_IMAGE, "Still Image" },
       
   538     { USB_CLASS_CSCID, "Smart Card" },
       
   539     { USB_CLASS_CONTENT_SEC, "Content Security" },
       
   540     { -1, NULL }
       
   541 };
       
   542 
       
   543 static const char *usb_class_str(uint8_t class)
       
   544 {
       
   545     const struct usb_class_info *p;
       
   546     for (p = usb_class_info; p->class != -1; p++) {
       
   547         if (p->class == class)
       
   548             break;
       
   549     }
       
   550     return p->class_name;
       
   551 }
       
   552 
       
   553 void usb_info_device(int bus_num, int addr, int class_id,
       
   554                      int vendor_id, int product_id,
       
   555                      const char *product_name,
       
   556                      int speed)
       
   557 {
       
   558     const char *class_str, *speed_str;
       
   559 
       
   560     switch(speed) {
       
   561     case USB_SPEED_LOW:
       
   562         speed_str = "1.5";
       
   563         break;
       
   564     case USB_SPEED_FULL:
       
   565         speed_str = "12";
       
   566         break;
       
   567     case USB_SPEED_HIGH:
       
   568         speed_str = "480";
       
   569         break;
       
   570     default:
       
   571         speed_str = "?";
       
   572         break;
       
   573     }
       
   574 
       
   575     term_printf("  Device %d.%d, speed %s Mb/s\n",
       
   576                 bus_num, addr, speed_str);
       
   577     class_str = usb_class_str(class_id);
       
   578     if (class_str)
       
   579         term_printf("    %s:", class_str);
       
   580     else
       
   581         term_printf("    Class %02x:", class_id);
       
   582     term_printf(" USB device %04x:%04x", vendor_id, product_id);
       
   583     if (product_name[0] != '\0')
       
   584         term_printf(", %s", product_name);
       
   585     term_printf("\n");
       
   586 }
       
   587 
       
   588 static int usb_host_info_device(void *opaque, int bus_num, int addr,
       
   589                                 int class_id,
       
   590                                 int vendor_id, int product_id,
       
   591                                 const char *product_name,
       
   592                                 int speed)
       
   593 {
       
   594     usb_info_device(bus_num, addr, class_id, vendor_id, product_id,
       
   595                     product_name, speed);
       
   596     return 0;
       
   597 }
       
   598 
       
   599 void usb_host_info(void)
       
   600 {
       
   601     usb_host_scan(NULL, usb_host_info_device);
       
   602 }
       
   603 
       
   604 /* XXX add this */
       
   605 int usb_host_device_close(const char *devname)
       
   606 {
       
   607     return 0;
       
   608 }