Commit 93ded9b8 authored by Linus Torvalds's avatar Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6

* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb-2.6: (100 commits)
  usb-storage: revert DMA-alignment change for Wireless USB
  USB: use reset_resume when normal resume fails
  usb_gadget: composite cdc gadget fault handling
  usb gadget: minor USBCV fix for composite framework
  USB: Fix bug with byte order in isp116x-hcd.c fio write/read
  USB: fix double kfree in ipaq in error case
  USB: fix build error in cdc-acm for CONFIG_PM=n
  USB: remove board-specific UP2OCR configuration from pxa27x-udc
  USB: EHCI: Reconciling USB register differences on MPC85xx vs MPC83xx
  USB: Fix pointer/int cast in USB devio code
  usb gadget: g_cdc dependso on NET
  USB: Au1xxx-usb: suspend/resume support.
  USB: Au1xxx-usb: clean up ohci/ehci bus glue sources.
  usbfs: don't store bad pointers in registration
  usbfs: fix race between open and unregister
  usbfs: simplify the lookup-by-minor routines
  usbfs: send disconnect signals when device is unregistered
  USB: Force unbinding of drivers lacking reset_resume or other methods
  USB: ohci-pnx4008: I2C cleanups and fixes
  USB: debug port converter does not accept more than 8 byte packets
  ...
parents 6d52dcbe f756cbd4
......@@ -524,6 +524,44 @@ These utilities include endpoint autoconfiguration.
<!-- !Edrivers/usb/gadget/epautoconf.c -->
</sect1>
<sect1 id="composite"><title>Composite Device Framework</title>
<para>The core API is sufficient for writing drivers for composite
USB devices (with more than one function in a given configuration),
and also multi-configuration devices (also more than one function,
but not necessarily sharing a given configuration).
There is however an optional framework which makes it easier to
reuse and combine functions.
</para>
<para>Devices using this framework provide a <emphasis>struct
usb_composite_driver</emphasis>, which in turn provides one or
more <emphasis>struct usb_configuration</emphasis> instances.
Each such configuration includes at least one
<emphasis>struct usb_function</emphasis>, which packages a user
visible role such as "network link" or "mass storage device".
Management functions may also exist, such as "Device Firmware
Upgrade".
</para>
!Iinclude/linux/usb/composite.h
!Edrivers/usb/gadget/composite.c
</sect1>
<sect1 id="functions"><title>Composite Device Functions</title>
<para>At this writing, a few of the current gadget drivers have
been converted to this framework.
Near-term plans include converting all of them, except for "gadgetfs".
</para>
!Edrivers/usb/gadget/f_acm.c
!Edrivers/usb/gadget/f_serial.c
</sect1>
</chapter>
<chapter id="controllers"><title>Peripheral Controller Drivers</title>
......
Linux Gadget Serial Driver v2.0
11/20/2004
(updated 8-May-2008 for v2.3)
License and Disclaimer
......@@ -31,7 +32,7 @@ Prerequisites
-------------
Versions of the gadget serial driver are available for the
2.4 Linux kernels, but this document assumes you are using
version 2.0 or later of the gadget serial driver in a 2.6
version 2.3 or later of the gadget serial driver in a 2.6
Linux kernel.
This document assumes that you are familiar with Linux and
......@@ -40,6 +41,12 @@ standard utilities, use minicom and HyperTerminal, and work with
USB and serial devices. It also assumes you configure the Linux
gadget and usb drivers as modules.
With version 2.3 of the driver, major and minor device nodes are
no longer statically defined. Your Linux based system should mount
sysfs in /sys, and use "mdev" (in Busybox) or "udev" to make the
/dev nodes matching the sysfs /sys/class/tty files.
Overview
--------
......@@ -104,15 +111,8 @@ driver. All this are listed under "USB Gadget Support" when
configuring the kernel. Then rebuild and install the kernel or
modules.
The gadget serial driver uses major number 127, for now. So you
will need to create a device node for it, like this:
mknod /dev/ttygserial c 127 0
You only need to do this once.
Then you must load the gadget serial driver. To load it as an
ACM device, do this:
ACM device (recommended for interoperability), do this:
modprobe g_serial use_acm=1
......@@ -125,6 +125,23 @@ controller driver. This must be done each time you reboot the gadget
side Linux system. You can add this to the start up scripts, if
desired.
Your system should use mdev (from busybox) or udev to make the
device nodes. After this gadget driver has been set up you should
then see a /dev/ttyGS0 node:
# ls -l /dev/ttyGS0 | cat
crw-rw---- 1 root root 253, 0 May 8 14:10 /dev/ttyGS0
#
Note that the major number (253, above) is system-specific. If
you need to create /dev nodes by hand, the right numbers to use
will be in the /sys/class/tty/ttyGS0/dev file.
When you link this gadget driver early, perhaps even statically,
you may want to set up an /etc/inittab entry to run "getty" on it.
The /dev/ttyGS0 line should work like most any other serial port.
If gadget serial is loaded as an ACM device you will want to use
either the Windows or Linux ACM driver on the host side. If gadget
serial is loaded as a bulk in/out device, you will want to use the
......
......@@ -81,8 +81,11 @@ re-enumeration shows that the device now attached to that port has the
same descriptors as before, including the Vendor and Product IDs, then
the kernel continues to use the same device structure. In effect, the
kernel treats the device as though it had merely been reset instead of
unplugged. The same thing happens if the host controller is in the
expected state but a USB device was unplugged and then replugged.
unplugged.
The same thing happens if the host controller is in the expected state
but a USB device was unplugged and then replugged, or if a USB device
fails to carry out a normal resume.
If no device is now attached to the port, or if the descriptors are
different from what the kernel remembers, then the treatment is what
......
Specification and Internals for the New UHCI Driver (Whitepaper...)
brought to you by
Georg Acher, acher@in.tum.de (executive slave) (base guitar)
Deti Fliegl, deti@fliegl.de (executive slave) (lead voice)
Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader)
$Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $
This document and the new uhci sources can be found on
http://hotswap.in.tum.de/usb
1. General issues
1.1 Why a new UHCI driver, we already have one?!?
Correct, but its internal structure got more and more mixed up by the (still
ongoing) efforts to get isochronous transfers (ISO) to work.
Since there is an increasing need for reliable ISO-transfers (especially
for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF),
this state was a bit unsatisfying in our opinion, so we've decided (based
on knowledge and experiences with the old UHCI driver) to start
from scratch with a new approach, much simpler but at the same time more
powerful.
It is inspired by the way Win98/Win2000 handles USB requests via URBs,
but it's definitely 100% free of MS-code and doesn't crash while
unplugging an used ISO-device like Win98 ;-)
Some code for HW setup and root hub management was taken from the
original UHCI driver, but heavily modified to fit into the new code.
The invention of the basic concept, and major coding were completed in two
days (and nights) on the 16th and 17th of October 1999, now known as the
great USB-October-Revolution started by GA, DF, and TS ;-)
Since the concept is in no way UHCI dependent, we hope that it will also be
transferred to the OHCI-driver, so both drivers share a common API.
1.2. Advantages and disadvantages
+ All USB transfer types work now!
+ Asynchronous operation
+ Simple, but powerful interface (only two calls for start and cancel)
+ Easy migration to the new API, simplified by a compatibility API
+ Simple usage of ISO transfers
+ Automatic linking of requests
+ ISO transfers allow variable length for each frame and striping
+ No CPU dependent and non-portable atomic memory access, no asm()-inlines
+ Tested on x86 and Alpha
- Rewriting for ISO transfers needed
1.3. Is there some compatibility to the old API?
Yes, but only for control, bulk and interrupt transfers. We've implemented
some wrapper calls for these transfer types. The usbcore works fine with
these wrappers. For ISO there's no compatibility, because the old ISO-API
and its semantics were unnecessary complicated in our opinion.
1.4. What's really working?
As said above, CTRL and BULK already work fine even with the wrappers,
so legacy code wouldn't notice the change.
Regarding to Thomas, ISO transfers now run stable with USB audio.
INT transfers (e.g. mouse driver) work fine, too.
1.5. Are there any bugs?
No ;-)
Hm...
Well, of course this implementation needs extensive testing on all available
hardware, but we believe that any fixes shouldn't harm the overall concept.
1.6. What should be done next?
A large part of the request handling seems to be identical for UHCI and
OHCI, so it would be a good idea to extract the common parts and have only
the HW specific stuff in uhci.c. Furthermore, all other USB device drivers
should need URBification, if they use isochronous or interrupt transfers.
One thing missing in the current implementation (and the old UHCI driver)
is fair queueing for BULK transfers. Since this would need (in principle)
the alteration of already constructed TD chains (to switch from depth to
breadth execution), another way has to be found. Maybe some simple
heuristics work with the same effect.
---------------------------------------------------------------------------
2. Internal structure and mechanisms
To get quickly familiar with the internal structures, here's a short
description how the new UHCI driver works. However, the ultimate source of
truth is only uhci.c!
2.1. Descriptor structure (QHs and TDs)
During initialization, the following skeleton is allocated in init_skel:
framespecific | common chain
framelist[]
[ 0 ]-----> TD --> TD -------\
[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL
... TD --> TD -------/
[1023]-----> TD --> TD ------/
^^ ^^ ^^ ^^ ^^ ^^
1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain
ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain
For each CTRL or BULK transfer a new QH is allocated and the containing data
transfers are appended as (vertical) TDs. After building the whole QH with its
dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or
before the End Chain QH (for BULK). Since only the QH->next pointers are
affected, no atomic memory operation is required. The three QHs in the
common chain are never equipped with TDs!
For ISO or INT, the TD for each frame is simply inserted into the appropriate
ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered
among the 1024 frames similar to the old UHCI driver.
For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT,
every TD (there is only one...) has the IOC-bit set.
Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors
are double-linked through the .vertical and .horizontal elements in the
SW data of the descriptor (using the double-linked list structures and
operations), but SW-linking occurs only in closed domains, i.e. for each of
the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This
simplifies all insertions and unlinking operations and avoids costly
bus_to_virt()-calls.
2.2. URB structure and linking to QH/TDs
During assembly of the QH and TDs of the requested action, these descriptors
are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to
this URB.
If the assembly was successful and the descriptors were added to the HW chain,
the corresponding URB is inserted into a global URB list for this controller.
This list stores all pending URBs.
2.3. Interrupt processing
Since UHCI provides no means to directly detect completed transactions, the
following is done in each UHCI interrupt (uhci_interrupt()):
For each URB in the pending queue (process_urb()), the ACTIVE-flag of the
associated TDs are processed (depending on the transfer type
process_{transfer|interrupt|iso}()). If the TDs are not active anymore,
they indicate the completion of the transaction and the status is calculated.
Inactive QH/TDs are removed from the HW chain (since the host controller
already removed the TDs from the QH, no atomic access is needed) and
eventually the URB is marked as completed (OK or errors) and removed from the
pending queue. Then the next linked URB is submitted. After (or immediately
before) that, the completion handler is called.
2.4. Unlinking URBs
First, all QH/TDs stored in the URB are unlinked from the HW chain.
To ensure that the host controller really left a vertical TD chain, we
wait for one frame. After that, the TDs are physically destroyed.
2.5. URB linking and the consequences
Since URBs can be linked and the corresponding submit_urb is called in
the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be
interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt.
......@@ -122,7 +122,7 @@ static void hid_reset(struct work_struct *work)
dev_dbg(&usbhid->intf->dev, "resetting device\n");
rc = rc_lock = usb_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
if (rc_lock >= 0) {
rc = usb_reset_composite_device(hid_to_usb_dev(hid), usbhid->intf);
rc = usb_reset_device(hid_to_usb_dev(hid));
if (rc_lock)
usb_unlock_device(hid_to_usb_dev(hid));
}
......
......@@ -1052,7 +1052,6 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
memset(instance->card_info, 0, sizeof(instance->card_info));
mutex_init(&instance->poll_state_serialize);
instance->poll_state = CXPOLL_STOPPED;
......
......@@ -829,7 +829,6 @@ static int speedtch_bind(struct usbatm_data *usbatm,
if (use_isoc) {
const struct usb_host_interface *desc = data_intf->cur_altsetting;
const __u8 target_address = USB_DIR_IN | usbatm->driver->isoc_in;
int i;
use_isoc = 0; /* fall back to bulk if endpoint not found */
......
This diff is collapsed.
......@@ -107,10 +107,14 @@ struct acm {
struct list_head filled_read_bufs;
int write_used; /* number of non-empty write buffers */
int write_ready; /* write urb is not running */
int old_ready;
int processing;
int transmitting;
spinlock_t write_lock;
struct mutex mutex;
struct usb_cdc_line_coding line; /* bits, stop, parity */
struct work_struct work; /* work queue entry for line discipline waking up */
struct work_struct waker;
struct tasklet_struct urb_task; /* rx processing */
spinlock_t throttle_lock; /* synchronize throtteling and read callback */
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
......@@ -123,6 +127,7 @@ struct acm {
unsigned char clocal; /* termios CLOCAL */
unsigned int ctrl_caps; /* control capabilities from the class specific header */
unsigned int susp_count; /* number of suspended interfaces */
struct acm_wb *delayed_wb; /* write queued for a device about to be woken */
};
#define CDC_DATA_INTERFACE_TYPE 0x0a
......
......@@ -28,8 +28,9 @@
/*
* Version Information
*/
#define DRIVER_VERSION "v0.02"
#define DRIVER_VERSION "v0.03"
#define DRIVER_AUTHOR "Oliver Neukum"
#define DRIVER_DESC "USB Abstract Control Model driver for USB WCM Device Management"
static struct usb_device_id wdm_ids[] = {
{
......@@ -87,6 +88,7 @@ struct wdm_device {
dma_addr_t ihandle;
struct mutex wlock;
struct mutex rlock;
struct mutex plock;
wait_queue_head_t wait;
struct work_struct rxwork;
int werr;
......@@ -205,7 +207,7 @@ static void wdm_int_callback(struct urb *urb)
req->bRequest = USB_CDC_GET_ENCAPSULATED_RESPONSE;
req->wValue = 0;
req->wIndex = desc->inum;
req->wLength = cpu_to_le16(desc->bMaxPacketSize0);
req->wLength = cpu_to_le16(desc->wMaxCommand);
usb_fill_control_urb(
desc->response,
......@@ -214,7 +216,7 @@ static void wdm_int_callback(struct urb *urb)
usb_rcvctrlpipe(interface_to_usbdev(desc->intf), 0),
(unsigned char *)req,
desc->inbuf,
desc->bMaxPacketSize0,
desc->wMaxCommand,
wdm_in_callback,
desc
);
......@@ -247,6 +249,7 @@ static void wdm_int_callback(struct urb *urb)
static void kill_urbs(struct wdm_device *desc)
{
/* the order here is essential */
usb_kill_urb(desc->command);
usb_kill_urb(desc->validity);
usb_kill_urb(desc->response);
......@@ -266,7 +269,7 @@ static void cleanup(struct wdm_device *desc)
desc->sbuf,
desc->validity->transfer_dma);
usb_buffer_free(interface_to_usbdev(desc->intf),
desc->wMaxPacketSize,
desc->wMaxCommand,
desc->inbuf,
desc->response->transfer_dma);
kfree(desc->orq);
......@@ -299,6 +302,9 @@ static ssize_t wdm_write
if (r)
goto outnl;
r = usb_autopm_get_interface(desc->intf);
if (r < 0)
goto outnp;
r = wait_event_interruptible(desc->wait, !test_bit(WDM_IN_USE,
&desc->flags));
if (r < 0)
......@@ -347,11 +353,14 @@ static ssize_t wdm_write
if (rv < 0) {
kfree(buf);
clear_bit(WDM_IN_USE, &desc->flags);
err("Tx URB error: %d", rv);
} else {
dev_dbg(&desc->intf->dev, "Tx URB has been submitted index=%d",
req->wIndex);
}
out:
usb_autopm_put_interface(desc->intf);
outnp:
mutex_unlock(&desc->wlock);
outnl:
return rv < 0 ? rv : count;
......@@ -376,6 +385,11 @@ static ssize_t wdm_read
rv = wait_event_interruptible(desc->wait,
test_bit(WDM_READ, &desc->flags));
if (test_bit(WDM_DISCONNECTING, &desc->flags)) {
rv = -ENODEV;
goto err;
}
usb_mark_last_busy(interface_to_usbdev(desc->intf));
if (rv < 0) {
rv = -ERESTARTSYS;
goto err;
......@@ -418,6 +432,9 @@ static ssize_t wdm_read
desc->ubuf[i] = desc->ubuf[i + cntr];
desc->length -= cntr;
/* in case we had outstanding data */
if (!desc->length)
clear_bit(WDM_READ, &desc->flags);
rv = cntr;
err:
......@@ -480,18 +497,28 @@ static int wdm_open(struct inode *inode, struct file *file)
if (test_bit(WDM_DISCONNECTING, &desc->flags))
goto out;
desc->count++;
;
file->private_data = desc;
rv = usb_submit_urb(desc->validity, GFP_KERNEL);
rv = usb_autopm_get_interface(desc->intf);
if (rv < 0) {
desc->count--;
err("Error submitting int urb - %d", rv);
err("Error autopm - %d", rv);
goto out;
}
rv = 0;
intf->needs_remote_wakeup = 1;
mutex_lock(&desc->plock);
if (!desc->count++) {
rv = usb_submit_urb(desc->validity, GFP_KERNEL);
if (rv < 0) {
desc->count--;
err("Error submitting int urb - %d", rv);
}
} else {
rv = 0;
}
mutex_unlock(&desc->plock);
usb_autopm_put_interface(desc->intf);
out:
mutex_unlock(&wdm_mutex);
return rv;
......@@ -502,10 +529,15 @@ static int wdm_release(struct inode *inode, struct file *file)
struct wdm_device *desc = file->private_data;
mutex_lock(&wdm_mutex);
mutex_lock(&desc->plock);
desc->count--;
mutex_unlock(&desc->plock);
if (!desc->count) {
dev_dbg(&desc->intf->dev, "wdm_release: cleanup");
kill_urbs(desc);
if (!test_bit(WDM_DISCONNECTING, &desc->flags))
desc->intf->needs_remote_wakeup = 0;
}
mutex_unlock(&wdm_mutex);
return 0;
......@@ -597,6 +629,7 @@ static int wdm_probe(struct usb_interface *intf, const struct usb_device_id *id)
goto out;
mutex_init(&desc->wlock);
mutex_init(&desc->rlock);
mutex_init(&desc->plock);
spin_lock_init(&desc->iuspin);
init_waitqueue_head(&desc->wait);
desc->wMaxCommand = maxcom;
......@@ -698,6 +731,7 @@ static void wdm_disconnect(struct usb_interface *intf)
spin_lock_irqsave(&desc->iuspin, flags);
set_bit(WDM_DISCONNECTING, &desc->flags);
set_bit(WDM_READ, &desc->flags);
/* to terminate pending flushes */
clear_bit(WDM_IN_USE, &desc->flags);
spin_unlock_irqrestore(&desc->iuspin, flags);
cancel_work_sync(&desc->rxwork);
......@@ -708,11 +742,81 @@ static void wdm_disconnect(struct usb_interface *intf)
mutex_unlock(&wdm_mutex);
}
static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
{
struct wdm_device *desc = usb_get_intfdata(intf);
int rv = 0;
dev_dbg(&desc->intf->dev, "wdm%d_suspend\n", intf->minor);
mutex_lock(&desc->plock);
#ifdef CONFIG_PM
if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
rv = -EBUSY;
} else {
#endif
cancel_work_sync(&desc->rxwork);
kill_urbs(desc);
#ifdef CONFIG_PM
}
#endif
mutex_unlock(&desc->plock);
return rv;
}
static int recover_from_urb_loss(struct wdm_device *desc)
{
int rv = 0;
if (desc->count) {
rv = usb_submit_urb(desc->validity, GFP_NOIO);
if (rv < 0)
err("Error resume submitting int urb - %d", rv);
}
return rv;
}
static int wdm_resume(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
int rv;
dev_dbg(&desc->intf->dev, "wdm%d_resume\n", intf->minor);
mutex_lock(&desc->plock);
rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->plock);
return rv;
}
static int wdm_pre_reset(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
mutex_lock(&desc->plock);
return 0;
}
static int wdm_post_reset(struct usb_interface *intf)
{
struct wdm_device *desc = usb_get_intfdata(intf);
int rv;
rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->plock);
return 0;
}
static struct usb_driver wdm_driver = {
.name = "cdc_wdm",
.probe = wdm_probe,
.disconnect = wdm_disconnect,
.suspend = wdm_suspend,
.resume = wdm_resume,
.reset_resume = wdm_resume,
.pre_reset = wdm_pre_reset,
.post_reset = wdm_post_reset,
.id_table = wdm_ids,
.supports_autosuspend = 1,
};
/* --- low level module stuff --- */
......@@ -735,6 +839,5 @@ module_init(wdm_init);
module_exit(wdm_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION("USB Abstract Control Model driver for "
"USB WCM Device Management");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
......@@ -46,8 +46,6 @@
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
* Converted file reading routine to dump to buffer once
* per device, not per bus
*
* $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
*/
#include <linux/fs.h>
......@@ -63,8 +61,6 @@
#include "usb.h"
#include "hcd.h"
#define MAX_TOPO_LEVEL 6
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
......
......@@ -19,8 +19,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
*
* This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
*
......@@ -61,6 +59,22 @@
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
struct dev_state {
struct list_head list; /* state list */
struct usb_device *dev;
struct file *file;
spinlock_t lock; /* protects the async urb lists */
struct list_head async_pending;
struct list_head async_completed;
wait_queue_head_t wait; /* wake up if a request completed */
unsigned int discsignr;
struct pid *disc_pid;
uid_t disc_uid, disc_euid;
void __user *disccontext;
unsigned long ifclaimed;
u32 secid;
};
struct async {
struct list_head asynclist;
struct dev_state *ps;
......@@ -536,23 +550,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
return ret;
}
static int __match_minor(struct device *dev, void *data)
static int match_devt(struct device *dev, void *data)
{
int minor = *((int *)data);
if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
return 1;
return 0;
return dev->devt == (dev_t) (unsigned long) data;
}
static struct usb_device *usbdev_lookup_by_minor(int minor)
static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
{
struct device *dev;
dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
dev = bus_find_device(&usb_bus_type, NULL,
(void *) (unsigned long) devt, match_devt);
if (!dev)
return NULL;
put_device(dev);
return container_of(dev, struct usb_device, dev);
}
......@@ -575,21 +585,27 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_by_minor(iminor(inode));
dev = usbdev_lookup_by_devt(inode->i_rdev);
#ifdef CONFIG_USB_DEVICEFS
/* procfs file */
if (!dev)
if (!dev) {
dev = inode->i_private;
if (dev && dev->usbfs_dentry &&
dev->usbfs_dentry->d_inode == inode)