2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2024-12-19 10:44:14 +08:00

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
  ...
This commit is contained in:
Linus Torvalds 2008-07-21 15:42:20 -07:00
commit 93ded9b8fd
142 changed files with 11673 additions and 9001 deletions

View File

@ -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>

View File

@ -1,6 +1,7 @@
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

View File

@ -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

View File

@ -1,165 +0,0 @@
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.

View File

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

View File

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

View File

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

View File

@ -159,12 +159,34 @@ static void acm_write_done(struct acm *acm, struct acm_wb *wb)
spin_lock_irqsave(&acm->write_lock, flags);
acm->write_ready = 1;
wb->use = 0;
acm->transmitting--;
spin_unlock_irqrestore(&acm->write_lock, flags);
}
/*
* Poke write.
*
* the caller is responsible for locking
*/
static int acm_start_wb(struct acm *acm, struct acm_wb *wb)
{
int rc;
acm->transmitting++;
wb->urb->transfer_buffer = wb->buf;
wb->urb->transfer_dma = wb->dmah;
wb->urb->transfer_buffer_length = wb->len;
wb->urb->dev = acm->dev;
if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
dbg("usb_submit_urb(write bulk) failed: %d", rc);
acm_write_done(acm, wb);
}
return rc;
}
static int acm_write_start(struct acm *acm, int wbn)
{
unsigned long flags;
@ -182,26 +204,31 @@ static int acm_write_start(struct acm *acm, int wbn)
return 0; /* A white lie */
}
wb = &acm->wb[wbn];
if(acm_wb_is_avail(acm) <= 1)
acm->write_ready = 0;
dbg("%s susp_count: %d", __func__, acm->susp_count);
if (acm->susp_count) {
acm->old_ready = acm->write_ready;
acm->delayed_wb = wb;
acm->write_ready = 0;
schedule_work(&acm->waker);
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0; /* A white lie */
}
usb_mark_last_busy(acm->dev);
if (!acm_wb_is_used(acm, wbn)) {
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0;
}
wb = &acm->wb[wbn];
if(acm_wb_is_avail(acm) <= 1)
acm->write_ready = 0;
rc = acm_start_wb(acm, wb);
spin_unlock_irqrestore(&acm->write_lock, flags);
wb->urb->transfer_buffer = wb->buf;
wb->urb->transfer_dma = wb->dmah;
wb->urb->transfer_buffer_length = wb->len;
wb->urb->dev = acm->dev;
if ((rc = usb_submit_urb(wb->urb, GFP_ATOMIC)) < 0) {
dbg("usb_submit_urb(write bulk) failed: %d", rc);
acm_write_done(acm, wb);
}
return rc;
}
/*
* attributes exported through sysfs
@ -304,6 +331,7 @@ static void acm_ctrl_irq(struct urb *urb)
break;
}
exit:
usb_mark_last_busy(acm->dev);
retval = usb_submit_urb (urb, GFP_ATOMIC);
if (retval)
err ("%s - usb_submit_urb failed with result %d",
@ -320,8 +348,11 @@ static void acm_read_bulk(struct urb *urb)
dbg("Entering acm_read_bulk with status %d", status);
if (!ACM_READY(acm))
if (!ACM_READY(acm)) {
dev_dbg(&acm->data->dev, "Aborting, acm not ready");
return;
}
usb_mark_last_busy(acm->dev);
if (status)
dev_dbg(&acm->data->dev, "bulk rx status %d\n", status);
@ -331,6 +362,7 @@ static void acm_read_bulk(struct urb *urb)
if (likely(status == 0)) {
spin_lock(&acm->read_lock);
acm->processing++;
list_add_tail(&rcv->list, &acm->spare_read_urbs);
list_add_tail(&buf->list, &acm->filled_read_bufs);
spin_unlock(&acm->read_lock);
@ -343,7 +375,8 @@ static void acm_read_bulk(struct urb *urb)
/* nevertheless the tasklet must be kicked unconditionally
so the queue cannot dry up */
}
tasklet_schedule(&acm->urb_task);
if (likely(!acm->susp_count))
tasklet_schedule(&acm->urb_task);
}
static void acm_rx_tasklet(unsigned long _acm)
@ -354,16 +387,23 @@ static void acm_rx_tasklet(unsigned long _acm)
struct acm_ru *rcv;
unsigned long flags;
unsigned char throttled;
dbg("Entering acm_rx_tasklet");
if (!ACM_READY(acm))
{
dbg("acm_rx_tasklet: ACM not ready");
return;
}
spin_lock_irqsave(&acm->throttle_lock, flags);
throttled = acm->throttle;
spin_unlock_irqrestore(&acm->throttle_lock, flags);
if (throttled)
{
dbg("acm_rx_tasklet: throttled");
return;
}
next_buffer:
spin_lock_irqsave(&acm->read_lock, flags);
@ -403,6 +443,7 @@ urbs:
while (!list_empty(&acm->spare_read_bufs)) {
spin_lock_irqsave(&acm->read_lock, flags);
if (list_empty(&acm->spare_read_urbs)) {
acm->processing = 0;
spin_unlock_irqrestore(&acm->read_lock, flags);
return;
}
@ -425,18 +466,23 @@ urbs:
rcv->urb->transfer_dma = buf->dma;
rcv->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
/* This shouldn't kill the driver as unsuccessful URBs are returned to the
free-urbs-pool and resubmited ASAP */
if (usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
spin_lock_irqsave(&acm->read_lock, flags);
if (acm->susp_count || usb_submit_urb(rcv->urb, GFP_ATOMIC) < 0) {
list_add(&buf->list, &acm->spare_read_bufs);
spin_lock_irqsave(&acm->read_lock, flags);
list_add(&rcv->list, &acm->spare_read_urbs);
acm->processing = 0;
spin_unlock_irqrestore(&acm->read_lock, flags);
return;
} else {
spin_unlock_irqrestore(&acm->read_lock, flags);
dbg("acm_rx_tasklet: sending urb 0x%p, rcv 0x%p, buf 0x%p", rcv->urb, rcv, buf);
}
}
spin_lock_irqsave(&acm->read_lock, flags);
acm->processing = 0;
spin_unlock_irqrestore(&acm->read_lock, flags);
}
/* data interface wrote those outgoing bytes */
@ -463,6 +509,27 @@ static void acm_softint(struct work_struct *work)
tty_wakeup(acm->tty);
}
static void acm_waker(struct work_struct *waker)
{
struct acm *acm = container_of(waker, struct acm, waker);
long flags;
int rv;
rv = usb_autopm_get_interface(acm->control);
if (rv < 0) {
err("Autopm failure in %s", __func__);
return;
}
if (acm->delayed_wb) {
acm_start_wb(acm, acm->delayed_wb);
acm->delayed_wb = NULL;
}
spin_lock_irqsave(&acm->write_lock, flags);
acm->write_ready = acm->old_ready;
spin_unlock_irqrestore(&acm->write_lock, flags);
usb_autopm_put_interface(acm->control);
}
/*
* TTY handlers
*/
@ -492,6 +559,8 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
if (usb_autopm_get_interface(acm->control) < 0)
goto early_bail;
else
acm->control->needs_remote_wakeup = 1;
mutex_lock(&acm->mutex);
if (acm->used++) {
@ -509,6 +578,7 @@ static int acm_tty_open(struct tty_struct *tty, struct file *filp)
if (0 > acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS) &&
(acm->ctrl_caps & USB_CDC_CAP_LINE))
goto full_bailout;
usb_autopm_put_interface(acm->control);
INIT_LIST_HEAD(&acm->spare_read_urbs);
INIT_LIST_HEAD(&acm->spare_read_bufs);
@ -570,12 +640,14 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
mutex_lock(&open_mutex);
if (!--acm->used) {
if (acm->dev) {
usb_autopm_get_interface(acm->control);
acm_set_control(acm, acm->ctrlout = 0);
usb_kill_urb(acm->ctrlurb);
for (i = 0; i < ACM_NW; i++)
usb_kill_urb(acm->wb[i].urb);
for (i = 0; i < nr; i++)
usb_kill_urb(acm->ru[i].urb);
acm->control->needs_remote_wakeup = 0;
usb_autopm_put_interface(acm->control);
} else
acm_tty_unregister(acm);
@ -766,7 +838,7 @@ static void acm_tty_set_termios(struct tty_struct *tty, struct ktermios *termios
* USB probe and disconnect routines.
*/
/* Little helper: write buffers free */
/* Little helpers: write/read buffers free */
static void acm_write_buffers_free(struct acm *acm)
{
int i;
@ -777,6 +849,15 @@ static void acm_write_buffers_free(struct acm *acm)
}
}
static void acm_read_buffers_free(struct acm *acm)
{
struct usb_device *usb_dev = interface_to_usbdev(acm->control);
int i, n = acm->rx_buflimit;
for (i = 0; i < n; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
}
/* Little helper: write buffers allocate */
static int acm_write_buffers_alloc(struct acm *acm)
{
@ -987,6 +1068,7 @@ skip_normal_probe:
acm->urb_task.func = acm_rx_tasklet;
acm->urb_task.data = (unsigned long) acm;
INIT_WORK(&acm->work, acm_softint);
INIT_WORK(&acm->waker, acm_waker);
spin_lock_init(&acm->throttle_lock);
spin_lock_init(&acm->write_lock);
spin_lock_init(&acm->read_lock);
@ -1098,8 +1180,7 @@ alloc_fail8:
for (i = 0; i < ACM_NW; i++)
usb_free_urb(acm->wb[i].urb);
alloc_fail7:
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
acm_read_buffers_free(acm);
for (i = 0; i < num_rx_buf; i++)
usb_free_urb(acm->ru[i].urb);
usb_free_urb(acm->ctrlurb);
@ -1116,6 +1197,7 @@ alloc_fail:
static void stop_data_traffic(struct acm *acm)
{
int i;
dbg("Entering stop_data_traffic");
tasklet_disable(&acm->urb_task);
@ -1128,21 +1210,16 @@ static void stop_data_traffic(struct acm *acm)
tasklet_enable(&acm->urb_task);
cancel_work_sync(&acm->work);
cancel_work_sync(&acm->waker);
}
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
int i;
if (!acm || !acm->dev) {
dbg("disconnect on nonexisting interface");
return;
}
mutex_lock(&open_mutex);
if (!usb_get_intfdata(intf)) {
if (!acm || !acm->dev) {
mutex_unlock(&open_mutex);
return;
}
@ -1161,10 +1238,10 @@ static void acm_disconnect(struct usb_interface *intf)
acm_write_buffers_free(acm);
usb_buffer_free(usb_dev, acm->ctrlsize, acm->ctrl_buffer, acm->ctrl_dma);
for (i = 0; i < acm->rx_buflimit; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
acm_read_buffers_free(acm);
usb_driver_release_interface(&acm_driver, intf == acm->control ? acm->data : intf);
usb_driver_release_interface(&acm_driver, intf == acm->control ?
acm->data : acm->control);
if (!acm->used) {
acm_tty_unregister(acm);
@ -1178,11 +1255,31 @@ static void acm_disconnect(struct usb_interface *intf)
tty_hangup(acm->tty);
}
#ifdef CONFIG_PM
static int acm_suspend(struct usb_interface *intf, pm_message_t message)
{
struct acm *acm = usb_get_intfdata(intf);
int cnt;
if (acm->susp_count++)
if (acm->dev->auto_pm) {
int b;
spin_lock_irq(&acm->read_lock);
spin_lock(&acm->write_lock);
b = acm->processing + acm->transmitting;
spin_unlock(&acm->write_lock);
spin_unlock_irq(&acm->read_lock);
if (b)
return -EBUSY;
}
spin_lock_irq(&acm->read_lock);
spin_lock(&acm->write_lock);
cnt = acm->susp_count++;
spin_unlock(&acm->write_lock);
spin_unlock_irq(&acm->read_lock);
if (cnt)
return 0;
/*
we treat opened interfaces differently,
@ -1201,15 +1298,21 @@ static int acm_resume(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata(intf);
int rv = 0;
int cnt;
if (--acm->susp_count)
spin_lock_irq(&acm->read_lock);
acm->susp_count -= 1;
cnt = acm->susp_count;
spin_unlock_irq(&acm->read_lock);
if (cnt)
return 0;
mutex_lock(&acm->mutex);
if (acm->used) {
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
if (rv < 0)
goto err_out;
goto err_out;
tasklet_schedule(&acm->urb_task);
}
@ -1218,6 +1321,8 @@ err_out:
mutex_unlock(&acm->mutex);
return rv;
}
#endif /* CONFIG_PM */
/*
* USB driver structure.
*/
@ -1273,10 +1378,14 @@ static struct usb_driver acm_driver = {
.name = "cdc_acm",
.probe = acm_probe,
.disconnect = acm_disconnect,
#ifdef CONFIG_PM
.suspend = acm_suspend,
.resume = acm_resume,
#endif
.id_table = acm_ids,
#ifdef CONFIG_PM
.supports_autosuspend = 1,
#endif
};
/*

View File

@ -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

View File

@ -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 @@ exit:
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 @@ retry:
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 @@ retry:
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 @@ next_desc:
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");

View File

@ -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

View File

@ -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)
usb_get_dev(dev);
else
dev = NULL;
}
#endif
if (!dev)
if (!dev || dev->state == USB_STATE_NOTATTACHED)
goto out;
ret = usb_autoresume_device(dev);
if (ret)
goto out;
usb_get_dev(dev);
ret = 0;
ps->dev = dev;
ps->file = file;
@ -609,8 +625,10 @@ static int usbdev_open(struct inode *inode, struct file *file)
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
if (ret)
if (ret) {
kfree(ps);
usb_put_dev(dev);
}
mutex_unlock(&usbfs_mutex);
unlock_kernel();
return ret;
@ -874,7 +892,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
static int proc_resetdevice(struct dev_state *ps)
{
return usb_reset_composite_device(ps->dev, NULL);
return usb_reset_device(ps->dev);
}
static int proc_setintf(struct dev_state *ps, void __user *arg)
@ -1682,25 +1700,49 @@ const struct file_operations usbdev_file_operations = {
.release = usbdev_release,
};
void usb_fs_classdev_common_remove(struct usb_device *udev)
{
struct dev_state *ps;
struct siginfo sinfo;
while (!list_empty(&udev->filelist)) {
ps = list_entry(udev->filelist.next, struct dev_state, list);
destroy_all_async(ps);
wake_up_all(&ps->wait);
list_del_init(&ps->list);
if (ps->discsignr) {
sinfo.si_signo = ps->discsignr;
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ps->disccontext;
kill_pid_info_as_uid(ps->discsignr, &sinfo,
ps->disc_pid, ps->disc_uid,
ps->disc_euid, ps->secid);
}
}
}
#ifdef CONFIG_USB_DEVICE_CLASS
static struct class *usb_classdev_class;
static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
if (IS_ERR(dev->usb_classdev))
return PTR_ERR(dev->usb_classdev);
struct device *cldev;
cldev = device_create(usb_classdev_class, &dev->dev, dev->dev.devt,
"usbdev%d.%d", dev->bus->busnum,
dev->devnum);
if (IS_ERR(cldev))
return PTR_ERR(cldev);
dev->usb_classdev = cldev;
return 0;
}
static void usb_classdev_remove(struct usb_device *dev)
{
device_unregister(dev->usb_classdev);
if (dev->usb_classdev)
device_unregister(dev->usb_classdev);
usb_fs_classdev_common_remove(dev);
}
static int usb_classdev_notify(struct notifier_block *self,

View File

@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev)
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
intf->needs_binding = 0;
if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
@ -257,15 +258,16 @@ static int usb_unbind_interface(struct device *dev)
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
/* Terminate all URBs for this interface unless the driver
* supports "soft" unbinding.
*/
if (!driver->soft_unbind)
usb_disable_interface(udev, intf);
driver->disconnect(intf);
/* reset other interface state */
usb_set_interface(interface_to_usbdev(intf),
intf->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
@ -310,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
iface->needs_binding = 0;
usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
@ -586,7 +589,7 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
struct usb_device *usb_dev;
/* driver is often null here; dev_dbg() would oops */
pr_debug("usb %s: uevent\n", dev->bus_id);
pr_debug("usb %s: uevent\n", dev_name(dev));
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
@ -596,11 +599,11 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
}
if (usb_dev->devnum < 0) {
pr_debug("usb %s: already deleted?\n", dev->bus_id);
pr_debug("usb %s: already deleted?\n", dev_name(dev));
return -ENODEV;
}
if (!usb_dev->bus) {
pr_debug("usb %s: bus removed?\n", dev->bus_id);
pr_debug("usb %s: bus removed?\n", dev_name(dev));
return -ENODEV;
}
@ -771,6 +774,104 @@ void usb_deregister(struct usb_driver *driver)
}
EXPORT_SYMBOL_GPL(usb_deregister);
/* Forced unbinding of a USB interface driver, either because
* it doesn't support pre_reset/post_reset/reset_resume or
* because it doesn't support suspend/resume.
*
* The caller must hold @intf's device's lock, but not its pm_mutex
* and not @intf->dev.sem.
*/
void usb_forced_unbind_intf(struct usb_interface *intf)
{
struct usb_driver *driver = to_usb_driver(intf->dev.driver);
dev_dbg(&intf->dev, "forced unbind\n");
usb_driver_release_interface(driver, intf);
/* Mark the interface for later rebinding */
intf->needs_binding = 1;
}
/* Delayed forced unbinding of a USB interface driver and scan
* for rebinding.
*
* The caller must hold @intf's device's lock, but not its pm_mutex
* and not @intf->dev.sem.
*
* FIXME: The caller must block system sleep transitions.
*/
void usb_rebind_intf(struct usb_interface *intf)
{
int rc;
/* Delayed unbind of an existing driver */
if (intf->dev.driver) {
struct usb_driver *driver =
to_usb_driver(intf->dev.driver);
dev_dbg(&intf->dev, "forced unbind\n");
usb_driver_release_interface(driver, intf);
}
/* Try to rebind the interface */
intf->needs_binding = 0;
rc = device_attach(&intf->dev);
if (rc < 0)
dev_warn(&intf->dev, "rebind failed: %d\n", rc);
}
#define DO_UNBIND 0
#define DO_REBIND 1
/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
* or rebind interfaces that have been unbound, according to @action.
*
* The caller must hold @udev's device lock.
* FIXME: For rebinds, the caller must block system sleep transitions.
*/
static void do_unbind_rebind(struct usb_device *udev, int action)
{
struct usb_host_config *config;
int i;
struct usb_interface *intf;
struct usb_driver *drv;
config = udev->actconfig;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
intf = config->interface[i];
switch (action) {
case DO_UNBIND:
if (intf->dev.driver) {
drv = to_usb_driver(intf->dev.driver);
if (!drv->suspend || !drv->resume)
usb_forced_unbind_intf(intf);
}
break;
case DO_REBIND:
if (intf->needs_binding) {
/* FIXME: The next line is needed because we are going to probe
* the interface, but as far as the PM core is concerned the
* interface is still suspended. The problem wouldn't exist
* if we could rebind the interface during the interface's own
* resume() call, but at the time the usb_device isn't locked!
*
* The real solution will be to carry this out during the device's
* complete() callback. Until that is implemented, we have to
* use this hack.
*/
// intf->dev.power.sleeping = 0;
usb_rebind_intf(intf);
}
break;
}
}
}
}
#ifdef CONFIG_PM
/* Caller has locked udev's pm_mutex */
@ -805,8 +906,6 @@ static int usb_resume_device(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED)
goto done;
if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
goto done;
/* Can't resume it if it doesn't have a driver. */
if (udev->dev.driver == NULL) {
@ -842,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
goto done;
driver = to_usb_driver(intf->dev.driver);
if (driver->suspend && driver->resume) {
if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
@ -850,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
/*
* FIXME else if there's no suspend method, disconnect...
* Not possible if auto_pm is set...
*/
dev_warn(&intf->dev, "no suspend for driver %s?\n",
driver->name);
/* Later we will unbind the driver and reprobe */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"suspend", driver->name);
mark_quiesced(intf);
}
@ -879,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
goto done;
/* Can't resume it if it doesn't have a driver. */
if (intf->condition == USB_INTERFACE_UNBOUND) {
status = -ENOTCONN;
if (intf->condition == USB_INTERFACE_UNBOUND)
goto done;
/* Don't resume if the interface is marked for rebinding */
if (intf->needs_binding)
goto done;
}
driver = to_usb_driver(intf->dev.driver);
if (reset_resume) {
@ -892,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"reset_resume", status);
} else {
/* status = -EOPNOTSUPP; */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"reset_resume", driver->name);
}
@ -903,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
} else {
/* status = -EOPNOTSUPP; */
intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"resume", driver->name);
}
@ -911,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
done:
dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
if (status == 0)
if (status == 0 && intf->condition == USB_INTERFACE_BOUND)
mark_active(intf);
/* FIXME: Unbind the driver and reprobe if the resume failed
* (not possible if auto_pm is set) */
/* Later we will unbind the driver and/or reprobe, if necessary */
return status;
}
@ -1173,11 +1271,8 @@ static int usb_resume_both(struct usb_device *udev)
* then we're stuck. */
status = usb_resume_device(udev);
}
} else {
/* Needed for reset-resume */
} else if (udev->reset_resume)
status = usb_resume_device(udev);
}
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
@ -1474,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
int status;
do_unbind_rebind(udev, DO_UNBIND);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
@ -1501,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev)
status = usb_resume_both(udev);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
do_unbind_rebind(udev, DO_REBIND);
/* Now that the device is awake, we can start trying to autosuspend
* it again. */
@ -1542,14 +1639,11 @@ static int usb_resume(struct device *dev)
udev = to_usb_device(dev);
/* If udev->skip_sys_resume is set then udev was already suspended
* when the system suspend started, so we don't want to resume
* udev during this system wakeup. However a reset-resume counts
* as a wakeup event, so allow a reset-resume to occur if remote
* wakeup is enabled. */
if (udev->skip_sys_resume) {
if (!(udev->reset_resume && udev->do_remote_wakeup))
return -EHOSTUNREACH;
}
* when the system sleep started, so we don't want to resume it
* during this system wakeup.
*/
if (udev->skip_sys_resume)
return 0;
return usb_external_resume_device(udev);
}

View File

@ -296,7 +296,7 @@ int usb_create_ep_files(struct device *parent,
retval = endpoint_get_minor(ep_dev);
if (retval) {
dev_err(parent, "can not allocate minor number for %s\n",
ep_dev->dev.bus_id);
dev_name(&ep_dev->dev));
goto error_register;
}
@ -307,7 +307,7 @@ int usb_create_ep_files(struct device *parent,
ep_dev->dev.class = ep_class->class;
ep_dev->dev.parent = parent;
ep_dev->dev.release = ep_device_release;
snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
udev->bus->busnum, udev->devnum,
endpoint->desc.bEndpointAddress);

View File

@ -150,7 +150,7 @@ int usb_register_dev(struct usb_interface *intf,
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
char name[BUS_ID_SIZE];
char name[20];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
@ -190,9 +190,9 @@ int usb_register_dev(struct usb_interface *intf,
intf->minor = minor;
/* create a usb class device for this usb interface */
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
if (temp && (temp[1] != 0x00))
if (temp && (temp[1] != '\0'))
++temp;
else
temp = name;
@ -227,7 +227,7 @@ void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
char name[BUS_ID_SIZE];
char name[20];
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
@ -242,7 +242,7 @@ void usb_deregister_dev(struct usb_interface *intf,
usb_minors[intf->minor] = NULL;
up_write(&minor_rwsem);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base);
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
intf->usb_dev = NULL;
intf->minor = -1;

View File

@ -900,14 +900,14 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval != sizeof usb_dev->descriptor) {
mutex_unlock(&usb_bus_list_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
usb_dev->dev.bus_id, retval);
dev_name(&usb_dev->dev), retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
retval = usb_new_device (usb_dev);
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
usb_dev->dev.bus_id, retval);
dev_name(&usb_dev->dev), retval);
}
mutex_unlock(&usb_bus_list_lock);
@ -1764,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
* If memory is unavailable, returns NULL.
*/
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name)
struct device *dev, const char *bus_name)
{
struct usb_hcd *hcd;

View File

@ -21,6 +21,8 @@
#include <linux/rwsem.h>
#define MAX_TOPO_LEVEL 6
/* This file contains declarations of usbcore internals that are mostly
* used or exposed by Host Controller Drivers.
*/
@ -235,7 +237,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
extern int usb_hcd_get_frame_number(struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
struct device *dev, char *bus_name);
struct device *dev, const char *bus_name);
extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd);
extern void usb_put_hcd(struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,

View File

@ -72,7 +72,6 @@ struct usb_hub {
unsigned limited_power:1;
unsigned quiescing:1;
unsigned activating:1;
unsigned disconnected:1;
unsigned has_indicators:1;
@ -131,6 +130,12 @@ MODULE_PARM_DESC(use_both_schemes,
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_TIMEOUT 1500
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
static int usb_reset_and_verify_device(struct usb_device *udev);
static inline char *portspeed(int portstatus)
{
@ -535,37 +540,6 @@ static void hub_power_on(struct usb_hub *hub)
msleep(max(pgood_delay, (unsigned) 100));
}
static void hub_quiesce(struct usb_hub *hub)
{
/* (nonblocking) khubd and related activity won't re-trigger */
hub->quiescing = 1;
hub->activating = 0;
/* (blocking) stop khubd and related activity */
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
cancel_work_sync(&hub->tt.kevent);
}
static void hub_activate(struct usb_hub *hub)
{
int status;
hub->quiescing = 0;
hub->activating = 1;
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
dev_err(hub->intfdev, "activate --> %d\n", status);
if (hub->has_indicators && blinkenlights)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
/* scan all ports ASAP */
kick_khubd(hub);
}
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
@ -624,136 +598,149 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
/* caller has locked the hub device */
static void hub_stop(struct usb_hub *hub)
{
struct usb_device *hdev = hub->hdev;
int i;
enum hub_activation_type {
HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
};
/* Disconnect all the children */
for (i = 0; i < hdev->maxchild; ++i) {
if (hdev->children[i])
usb_disconnect(&hdev->children[i]);
}
hub_quiesce(hub);
}
#define HUB_RESET 1
#define HUB_RESUME 2
#define HUB_RESET_RESUME 3
#ifdef CONFIG_PM
/* Try to identify which devices need USB-PERSIST handling */
static int persistent_device(struct usb_device *udev)
{
int i;
int retval;
struct usb_host_config *actconfig;
/* Explicitly not marked persistent? */
if (!udev->persist_enabled)
return 0;
/* No active config? */
actconfig = udev->actconfig;
if (!actconfig)
return 0;
/* FIXME! We should check whether it's open here or not! */
/*
* Check that all the interface drivers have a
* 'reset_resume' entrypoint
*/
retval = 0;
for (i = 0; i < actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf;
struct usb_driver *driver;
intf = actconfig->interface[i];
if (!intf->dev.driver)
continue;
driver = to_usb_driver(intf->dev.driver);
if (!driver->reset_resume)
return 0;
/*
* We have at least one driver, and that one
* has a reset_resume method.
*/
retval = 1;
}
return retval;
}
static void hub_restart(struct usb_hub *hub, int type)
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
int status;
bool need_debounce_delay = false;
/* Check each of the children to see if they require
* USB-PERSIST handling or disconnection. Also check
* each unoccupied port to make sure it is still disabled.
/* After a resume, port power should still be on.
* For any other type of activation, turn it on.
*/
if (type != HUB_RESUME)
hub_power_on(hub);
/* Check each port and set hub->change_bits to let khubd know
* which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hdev->children[port1-1];
int status = 0;
u16 portstatus, portchange;
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
if (type != HUB_RESET) {
status = hub_port_status(hub, port1,
&portstatus, &portchange);
if (status == 0 && (portstatus &
USB_PORT_STAT_ENABLE))
clear_port_feature(hdev, port1,
USB_PORT_FEAT_ENABLE);
}
continue;
}
/* Was the power session lost while we were suspended? */
portstatus = portchange = 0;
status = hub_port_status(hub, port1, &portstatus, &portchange);
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
dev_dbg(hub->intfdev,
"port %d: status %04x change %04x\n",
port1, portstatus, portchange);
/* If the device is gone, khubd will handle it later */
if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION))
continue;
/* For "USB_PERSIST"-enabled children we must
* mark the child device for reset-resume and
* turn off the various status changes to prevent
* khubd from disconnecting it later.
/* After anything other than HUB_RESUME (i.e., initialization
* or any sort of reset), every port should be disabled.
* Unconnected ports should likewise be disabled (paranoia),
* and so should ports for which we have no usb_device.
*/
if (status == 0 && !(portstatus & USB_PORT_STAT_ENABLE) &&
persistent_device(udev)) {
if (portchange & USB_PORT_STAT_C_ENABLE)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
if (portchange & USB_PORT_STAT_C_CONNECTION)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
udev->reset_resume = 1;
if ((portstatus & USB_PORT_STAT_ENABLE) && (
type != HUB_RESUME ||
!(portstatus & USB_PORT_STAT_CONNECTION) ||
!udev ||
udev->state == USB_STATE_NOTATTACHED)) {
clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
portstatus &= ~USB_PORT_STAT_ENABLE;
}
/* Otherwise for a reset_resume we must disconnect the child,
* but as we may not lock the child device here
* we have to do a "logical" disconnect.
*/
else if (type == HUB_RESET_RESUME)
hub_port_logical_disconnect(hub, port1);
/* Clear status-change flags; we'll debounce later */
if (portchange & USB_PORT_STAT_C_CONNECTION) {
need_debounce_delay = true;
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
}
if (portchange & USB_PORT_STAT_C_ENABLE) {
need_debounce_delay = true;
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
if (!udev || udev->state == USB_STATE_NOTATTACHED) {
/* Tell khubd to disconnect the device or
* check for a new connection
*/
if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
set_bit(port1, hub->change_bits);
} else if (portstatus & USB_PORT_STAT_ENABLE) {
/* The power session apparently survived the resume.
* If there was an overcurrent or suspend change
* (i.e., remote wakeup request), have khubd
* take care of it.
*/
if (portchange)
set_bit(port1, hub->change_bits);
} else if (udev->persist_enabled) {
#ifdef CONFIG_PM
udev->reset_resume = 1;
#endif
set_bit(port1, hub->change_bits);
} else {
/* The power session is gone; tell khubd */
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
set_bit(port1, hub->change_bits);
}
}
hub_activate(hub);
/* If no port-status-change flags were set, we don't need any
* debouncing. If flags were set we can try to debounce the
* ports all at once right now, instead of letting khubd do them
* one at a time later on.
*
* If any port-status changes do occur during this delay, khubd
* will see them later and handle them normally.
*/
if (need_debounce_delay)
msleep(HUB_DEBOUNCE_STABLE);
hub->quiescing = 0;
status = usb_submit_urb(hub->urb, GFP_NOIO);
if (status < 0)
dev_err(hub->intfdev, "activate --> %d\n", status);
if (hub->has_indicators && blinkenlights)
schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
/* Scan all ports that need attention */
kick_khubd(hub);
}
#endif /* CONFIG_PM */
enum hub_quiescing_type {
HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
};
static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
{
struct usb_device *hdev = hub->hdev;
int i;
/* khubd and related activity won't re-trigger */
hub->quiescing = 1;
if (type != HUB_SUSPEND) {
/* Disconnect all the children */
for (i = 0; i < hdev->maxchild; ++i) {
if (hdev->children[i])
usb_disconnect(&hdev->children[i]);
}
}
/* Stop khubd and related activity */
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
if (hub->tt.hub)
cancel_work_sync(&hub->tt.kevent);
}
/* caller has locked the hub device */
static int hub_pre_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
hub_stop(hub);
hub_quiesce(hub, HUB_PRE_RESET);
return 0;
}
@ -762,8 +749,7 @@ static int hub_post_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
hub_power_on(hub);
hub_activate(hub);
hub_activate(hub, HUB_POST_RESET);
return 0;
}
@ -1009,8 +995,7 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
hub_power_on(hub);
hub_activate(hub);
hub_activate(hub, HUB_INIT);
return 0;
fail:
@ -1042,7 +1027,7 @@ static void hub_disconnect(struct usb_interface *intf)
/* Disconnect all children and quiesce the hub */
hub->error = 0;
hub_stop(hub);
hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
@ -1068,6 +1053,12 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev, "Unsupported bus topology: "
"hub nested too deep\n");
return -E2BIG;
}
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
@ -1814,6 +1805,51 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
#ifdef CONFIG_PM
#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
USB_PORT_STAT_SUSPEND)
#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
/* Determine whether the device on a port is ready for a normal resume,
* is ready for a reset-resume, or should be disconnected.
*/
static int check_port_resume_type(struct usb_device *udev,
struct usb_hub *hub, int port1,
int status, unsigned portchange, unsigned portstatus)
{
/* Is the device still present? */
if (status || (portstatus & MASK_BITS) != WANT_BITS) {
if (status >= 0)
status = -ENODEV;
}
/* Can't do a normal resume if the port isn't enabled,
* so try a reset-resume instead.
*/
else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
if (udev->persist_enabled)
udev->reset_resume = 1;
else
status = -ENODEV;
}
if (status) {
dev_dbg(hub->intfdev,
"port %d status %04x.%04x after resume, %d\n",
port1, portchange, portstatus, status);
} else if (udev->reset_resume) {
/* Late port handoff can set status-change bits */
if (portchange & USB_PORT_STAT_C_CONNECTION)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_CONNECTION);
if (portchange & USB_PORT_STAT_C_ENABLE)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_ENABLE);
}
return status;
}
#ifdef CONFIG_USB_SUSPEND
/*
@ -1943,7 +1979,8 @@ static int finish_port_resume(struct usb_device *udev)
* resumed.
*/
if (udev->reset_resume)
status = usb_reset_device(udev);
retry_reset_resume:
status = usb_reset_and_verify_device(udev);
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
@ -1954,6 +1991,13 @@ static int finish_port_resume(struct usb_device *udev)
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
if (status >= 0)
status = (status > 0 ? 0 : -ENODEV);
/* If a normal resume failed, try doing a reset-resume */
if (status && !udev->reset_resume && udev->persist_enabled) {
dev_dbg(&udev->dev, "retry with reset-resume\n");
udev->reset_resume = 1;
goto retry_reset_resume;
}
}
if (status) {
@ -2002,7 +2046,7 @@ static int finish_port_resume(struct usb_device *udev)
* to it will be lost. Using the USB_PERSIST facility, the device can be
* made to appear as if it had not disconnected.
*
* This facility can be dangerous. Although usb_reset_device() makes
* This facility can be dangerous. Although usb_reset_and_verify_device() makes
* every effort to insure that the same device is present after the
* reset as before, it cannot provide a 100% guarantee. Furthermore it's
* quite possible for a device to remain unaltered but its media to be
@ -2018,7 +2062,6 @@ int usb_port_resume(struct usb_device *udev)
int port1 = udev->portnum;
int status;
u16 portchange, portstatus;
unsigned mask_flags, want_flags;
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
@ -2047,35 +2090,23 @@ int usb_port_resume(struct usb_device *udev)
*/
status = hub_port_status(hub, port1, &portstatus, &portchange);
SuspendCleared:
if (udev->reset_resume)
want_flags = USB_PORT_STAT_POWER
| USB_PORT_STAT_CONNECTION;
else
want_flags = USB_PORT_STAT_POWER
| USB_PORT_STAT_CONNECTION
| USB_PORT_STAT_ENABLE;
mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
/* TRSMRCY = 10 msec */
msleep(10);
}
if (status < 0 || (portstatus & mask_flags) != want_flags) {
dev_dbg(hub->intfdev,
"port %d status %04x.%04x after resume, %d\n",
port1, portchange, portstatus, status);
if (status >= 0)
status = -ENODEV;
} else {
if (portchange & USB_PORT_STAT_C_SUSPEND)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_SUSPEND);
/* TRSMRCY = 10 msec */
msleep(10);
}
SuspendCleared:
if (status == 0) {
if (portchange & USB_PORT_STAT_C_SUSPEND)
clear_port_feature(hub->hdev, port1,
USB_PORT_FEAT_C_SUSPEND);
}
clear_bit(port1, hub->busy_bits);
if (!hub->hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hub->hdev->bus);
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
if (status == 0)
status = finish_port_resume(udev);
if (status < 0) {
@ -2085,17 +2116,16 @@ int usb_port_resume(struct usb_device *udev)
return status;
}
/* caller has locked udev */
static int remote_wakeup(struct usb_device *udev)
{
int status = 0;
usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
usb_mark_last_busy(udev);
status = usb_external_resume_device(udev);
}
usb_unlock_device(udev);
return status;
}
@ -2108,14 +2138,25 @@ int usb_port_suspend(struct usb_device *udev)
return 0;
}
/* However we may need to do a reset-resume */
int usb_port_resume(struct usb_device *udev)
{
int status = 0;
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
int status;
u16 portchange, portstatus;
/* However we may need to do a reset-resume */
if (udev->reset_resume) {
status = hub_port_status(hub, port1, &portstatus, &portchange);
status = check_port_resume_type(udev,
hub, port1, status, portchange, portstatus);
if (status) {
dev_dbg(&udev->dev, "can't resume, status %d\n", status);
hub_port_logical_disconnect(hub, port1);
} else if (udev->reset_resume) {
dev_dbg(&udev->dev, "reset-resume\n");
status = usb_reset_device(udev);
status = usb_reset_and_verify_device(udev);
}
return status;
}
@ -2149,7 +2190,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
dev_dbg(&intf->dev, "%s\n", __func__);
/* stop khubd and related activity */
hub_quiesce(hub);
hub_quiesce(hub, HUB_SUSPEND);
return 0;
}
@ -2158,7 +2199,7 @@ static int hub_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
hub_restart(hub, HUB_RESUME);
hub_activate(hub, HUB_RESUME);
return 0;
}
@ -2167,8 +2208,7 @@ static int hub_reset_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
hub_power_on(hub);
hub_restart(hub, HUB_RESET_RESUME);
hub_activate(hub, HUB_RESET_RESUME);
return 0;
}
@ -2218,11 +2258,6 @@ static inline int remote_wakeup(struct usb_device *udev)
* every 25ms for transient disconnects. When the port status has been
* unchanged for 100ms it returns the port status.
*/
#define HUB_DEBOUNCE_TIMEOUT 1500
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
int ret;
@ -2302,7 +2337,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
* Returns device in USB_STATE_ADDRESS, except on error.
*
* If this is called for an already-existing device (as part of
* usb_reset_device), the caller must own the device lock. For a
* usb_reset_and_verify_device), the caller must own the device lock. For a
* newly detected device that is not accessible through any global
* pointers, it's not necessary to lock the device.
*/
@ -2619,7 +2654,7 @@ hub_power_remaining (struct usb_hub *hub)
* This routine is called when:
* a port connection-change occurs;
* a port enable-change occurs (often caused by EMI);
* usb_reset_device() encounters changed descriptors (as from
* usb_reset_and_verify_device() encounters changed descriptors (as from
* a firmware download)
* caller already locked the hub
*/
@ -2629,9 +2664,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
unsigned wHubCharacteristics =
le16_to_cpu(hub->descriptor->wHubCharacteristics);
struct usb_device *udev;
int status, i;
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
@ -2640,30 +2677,73 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
}
/* Disconnect any existing devices under this port */
if (hdev->children[port1-1])
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
portchange &= ~USB_PORT_STAT_C_CONNECTION;
portchange &= ~(USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE);
#endif
if (portchange & USB_PORT_STAT_C_CONNECTION) {
/* Try to use the debounce delay for protection against
* port-enable changes caused, for example, by EMI.
*/
if (portchange & (USB_PORT_STAT_C_CONNECTION |
USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
if (status < 0) {
if (printk_ratelimit())
dev_err (hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
goto done;
portstatus &= ~USB_PORT_STAT_CONNECTION;
} else {
portstatus = status;
}
portstatus = status;
}
/* Return now if nothing is connected */
/* Try to resuscitate an existing device */
udev = hdev->children[port1-1];
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
usb_lock_device(udev);
if (portstatus & USB_PORT_STAT_ENABLE) {
status = 0; /* Nothing to do */
} else if (!udev->persist_enabled) {
status = -ENODEV; /* Mustn't resuscitate */
#ifdef CONFIG_USB_SUSPEND
} else if (udev->state == USB_STATE_SUSPENDED) {
/* For a suspended device, treat this as a
* remote wakeup event.
*/
if (udev->do_remote_wakeup)
status = remote_wakeup(udev);
/* Otherwise leave it be; devices can't tell the
* difference between suspended and disabled.
*/
else
status = 0;
#endif
} else {
status = usb_reset_device(udev);
}
usb_unlock_device(udev);
if (status == 0) {
clear_bit(port1, hub->change_bits);
return;
}
}
/* Disconnect any existing devices under this port */
if (udev)
usb_disconnect(&hdev->children[port1-1]);
clear_bit(port1, hub->change_bits);
/* Return now if debouncing failed or nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/* maybe switch power back on (e.g. root hub was reset) */
@ -2677,7 +2757,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
for (i = 0; i < SET_CONFIG_TRIES; i++) {
struct usb_device *udev;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
@ -2858,7 +2937,7 @@ static void hub_events(void)
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
hub_stop(hub);
hub_quiesce(hub, HUB_DISCONNECT);
goto loop;
}
@ -2877,7 +2956,7 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
ret = usb_reset_composite_device(hdev, intf);
ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev,
"error resetting hub: %d\n", ret);
@ -2894,7 +2973,7 @@ static void hub_events(void)
continue;
connect_change = test_bit(i, hub->change_bits);
if (!test_and_clear_bit(i, hub->event_bits) &&
!connect_change && !hub->activating)
!connect_change)
continue;
ret = hub_port_status(hub, i,
@ -2902,11 +2981,6 @@ static void hub_events(void)
if (ret < 0)
continue;
if (hub->activating && !hdev->children[i-1] &&
(portstatus &
USB_PORT_STAT_CONNECTION))
connect_change = 1;
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
@ -2941,11 +3015,16 @@ static void hub_events(void)
}
if (portchange & USB_PORT_STAT_C_SUSPEND) {
struct usb_device *udev;
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
if (hdev->children[i-1]) {
udev = hdev->children[i-1];
if (udev) {
usb_lock_device(udev);
ret = remote_wakeup(hdev->
children[i-1]);
usb_unlock_device(udev);
if (ret < 0)
connect_change = 1;
} else {
@ -3002,8 +3081,6 @@ static void hub_events(void)
}
}
hub->activating = 0;
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
if (!hdev->parent && !hub->busy_bits[0])
@ -3172,12 +3249,12 @@ static int descriptors_changed(struct usb_device *udev,
}
/**
* usb_reset_device - perform a USB port reset to reinitialize a device
* usb_reset_and_verify_device - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
*
* WARNING - don't use this routine to reset a composite device
* (one with multiple interfaces owned by separate drivers)!
* Use usb_reset_composite_device() instead.
* Use usb_reset_device() instead.
*
* Do a port reset, reassign the device's address, and establish its
* former operating configuration. If the reset fails, or the device's
@ -3201,7 +3278,7 @@ static int descriptors_changed(struct usb_device *udev,
* holding the device lock because these tasks should always call
* usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
*/
int usb_reset_device(struct usb_device *udev)
static int usb_reset_and_verify_device(struct usb_device *udev)
{
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
@ -3289,26 +3366,28 @@ re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
EXPORT_SYMBOL_GPL(usb_reset_device);
/**
* usb_reset_composite_device - warn interface drivers and perform a USB port reset
* usb_reset_device - warn interface drivers and perform a USB port reset
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
* @iface: interface bound to the driver making the request (optional)
*
* Warns all drivers bound to registered interfaces (using their pre_reset
* method), performs the port reset, and then lets the drivers know that
* the reset is over (using their post_reset method).
*
* Return value is the same as for usb_reset_device().
* Return value is the same as for usb_reset_and_verify_device().
*
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
* the device using usb_lock_device_for_reset().
*
* If an interface is currently being probed or disconnected, we assume
* its driver knows how to handle resets. For all other interfaces,
* if the driver doesn't have pre_reset and post_reset methods then
* we attempt to unbind it and rebind afterward.
*/
int usb_reset_composite_device(struct usb_device *udev,
struct usb_interface *iface)
int usb_reset_device(struct usb_device *udev)
{
int ret;
int i;
@ -3324,40 +3403,47 @@ int usb_reset_composite_device(struct usb_device *udev,
/* Prevent autosuspend during the reset */
usb_autoresume_device(udev);
if (iface && iface->condition != USB_INTERFACE_BINDING)
iface = NULL;
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
int unbind = 0;
if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->pre_reset)
(drv->pre_reset)(cintf);
/* FIXME: Unbind if pre_reset returns an error or isn't defined */
if (drv->pre_reset && drv->post_reset)
unbind = (drv->pre_reset)(cintf);
else if (cintf->condition ==
USB_INTERFACE_BOUND)
unbind = 1;
if (unbind)
usb_forced_unbind_intf(cintf);
}
}
}
ret = usb_reset_device(udev);
ret = usb_reset_and_verify_device(udev);
if (config) {
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
int rebind = cintf->needs_binding;
if (cintf->dev.driver) {
if (!rebind && cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->post_reset)
(drv->post_reset)(cintf);
/* FIXME: Unbind if post_reset returns an error or isn't defined */
rebind = (drv->post_reset)(cintf);
else if (cintf->condition ==
USB_INTERFACE_BOUND)
rebind = 1;
}
if (rebind)
usb_rebind_intf(cintf);
}
}
usb_autosuspend_device(udev);
return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_composite_device);
EXPORT_SYMBOL_GPL(usb_reset_device);

View File

@ -712,25 +712,11 @@ static void usbfs_add_device(struct usb_device *dev)
static void usbfs_remove_device(struct usb_device *dev)
{
struct dev_state *ds;
struct siginfo sinfo;
if (dev->usbfs_dentry) {
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
while (!list_empty(&dev->filelist)) {
ds = list_entry(dev->filelist.next, struct dev_state, list);
wake_up_all(&ds->wait);
list_del_init(&ds->list);
if (ds->discsignr) {
sinfo.si_signo = ds->discsignr;
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ds->disccontext;
kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
}
}
usb_fs_classdev_common_remove(dev);
}
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)

View File

@ -400,7 +400,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (usb_pipein(pipe))
urb_flags |= URB_SHORT_NOT_OK;
for (i = 0; i < io->entries; i++) {
for_each_sg(sg, sg, io->entries, i) {
unsigned len;
io->urbs[i] = usb_alloc_urb(0, mem_flags);
@ -434,17 +434,17 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
* to prevent stale pointers and to help spot bugs.
*/
if (dma) {
io->urbs[i]->transfer_dma = sg_dma_address(sg + i);
len = sg_dma_len(sg + i);
io->urbs[i]->transfer_dma = sg_dma_address(sg);
len = sg_dma_len(sg);
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
io->urbs[i]->transfer_buffer = NULL;
#else
io->urbs[i]->transfer_buffer = sg_virt(&sg[i]);
io->urbs[i]->transfer_buffer = sg_virt(sg);
#endif
} else {
/* hc may use _only_ transfer_buffer */
io->urbs[i]->transfer_buffer = sg_virt(&sg[i]);
len = sg[i].length;
io->urbs[i]->transfer_buffer = sg_virt(sg);
len = sg->length;
}
if (length) {
@ -1090,7 +1090,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
if (!device_is_registered(&interface->dev))
continue;
dev_dbg(&dev->dev, "unregistering interface %s\n",
interface->dev.bus_id);
dev_name(&interface->dev));
device_del(&interface->dev);
usb_remove_sysfs_intf_files(interface);
}
@ -1476,7 +1476,7 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
*
* This call is synchronous. The calling context must be able to sleep,
* must own the device lock, and must not hold the driver model's USB
* bus mutex; usb device driver probe() methods cannot use this routine.
* bus mutex; usb interface driver probe() methods cannot use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On successful completion, each interface
@ -1611,7 +1611,7 @@ free_interfaces:
intf->dev.dma_mask = dev->dev.dma_mask;
device_initialize(&intf->dev);
mark_quiesced(intf);
sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",
dev_set_name(&intf->dev, "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
@ -1631,12 +1631,12 @@ free_interfaces:
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
intf->dev.bus_id, configuration,
dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
intf->dev.bus_id, ret);
dev_name(&intf->dev), ret);
continue;
}
usb_create_sysfs_intf_files(intf);

View File

@ -308,7 +308,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
* by location for diagnostics, tools, driver model, etc. The
* string is a path along hub ports, from the root. Each device's
* dev->devpath will be stable until USB is re-cabled, and hubs
* are often labeled with these port numbers. The bus_id isn't
* are often labeled with these port numbers. The name isn't
* as stable: bus->busnum changes easily from modprobe order,
* cardbus or pci hotplugging, and so on.
*/
@ -316,7 +316,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->devpath[0] = '0';
dev->dev.parent = bus->controller;
sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
dev_set_name(&dev->dev, "usb%d", bus->busnum);
root_hub = 1;
} else {
/* match any labeling on the hubs; it's one-based */
@ -328,8 +328,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
"%s.%d", parent->devpath, port1);
dev->dev.parent = &parent->dev;
sprintf(&dev->dev.bus_id[0], "%d-%s",
bus->busnum, dev->devpath);
dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
/* hub driver sets up TT records */
}

View File

@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev);
extern void usb_kick_khubd(struct usb_device *dev);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
extern void usb_forced_unbind_intf(struct usb_interface *intf);
extern void usb_rebind_intf(struct usb_interface *intf);
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
@ -140,26 +142,11 @@ extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
extern void usb_fs_classdev_common_remove(struct usb_device *udev);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
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;
};
/* internal notify stuff */
extern void usb_notify_add_device(struct usb_device *udev);
extern void usb_notify_remove_device(struct usb_device *udev);

View File

@ -586,6 +586,20 @@ config USB_G_PRINTER
For more information, see Documentation/usb/gadget_printer.txt
which includes sample code for accessing the device file.
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
help
This driver provides two functions in one configuration:
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
This driver requires four bulk and two interrupt endpoints,
plus the ability to handle altsettings. Not all peripheral
controllers are that capable.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module.
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.

View File

@ -22,18 +22,22 @@ obj-$(CONFIG_USB_M66592) += m66592-udc.o
#
# USB gadget drivers
#
g_zero-objs := zero.o usbstring.o config.o epautoconf.o
g_ether-objs := ether.o usbstring.o config.o epautoconf.o
g_serial-objs := serial.o usbstring.o config.o epautoconf.o
C_UTILS = composite.o usbstring.o config.o epautoconf.o
g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS)
g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS)
g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS)
g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
epautoconf.o
g_printer-objs := printer.o usbstring.o config.o \
epautoconf.o
g_cdc-objs := cdc2.o u_ether.o f_ecm.o \
u_serial.o f_acm.o $(C_UTILS)
ifeq ($(CONFIG_USB_ETH_RNDIS),y)
g_ether-objs += rndis.o
g_ether-objs += f_rndis.o rndis.o
endif
obj-$(CONFIG_USB_ZERO) += g_zero.o
@ -43,4 +47,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o

View File

@ -3342,7 +3342,7 @@ static int udc_probe(struct udc *dev)
spin_lock_init(&dev->lock);
dev->gadget.ops = &udc_ops;
strcpy(dev->gadget.dev.bus_id, "gadget");
dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.release = gadget_release;
dev->gadget.name = name;
dev->gadget.name = name;

View File

@ -1687,6 +1687,19 @@ static int __init at91udc_probe(struct platform_device *pdev)
udc->board.pullup_active_low);
}
/* newer chips have more FIFO memory than rm9200 */
if (cpu_is_at91sam9260()) {
udc->ep[0].maxpacket = 64;
udc->ep[3].maxpacket = 64;
udc->ep[4].maxpacket = 512;
udc->ep[5].maxpacket = 512;
} else if (cpu_is_at91sam9261()) {
udc->ep[3].maxpacket = 64;
} else if (cpu_is_at91sam9263()) {
udc->ep[0].maxpacket = 64;
udc->ep[3].maxpacket = 64;
}
udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1);
if (!udc->udp_baseaddr) {
retval = -ENOMEM;

246
drivers/usb/gadget/cdc2.c Normal file
View File

@ -0,0 +1,246 @@
/*
* cdc2.c -- CDC Composite driver, with ECM and ACM support
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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 <linux/kernel.h>
#include <linux/utsname.h>
#include "u_ether.h"
#include "u_serial.h"
#define DRIVER_DESC "CDC Composite Gadget"
#define DRIVER_VERSION "King Kamehameha Day 2008"
/*-------------------------------------------------------------------------*/
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
*/
/* Thanks to NetChip Technologies for donating this product ID.
* It's for devices with only this composite CDC configuration.
*/
#define CDC_VENDOR_NUM 0x0525 /* NetChip */
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
/*-------------------------------------------------------------------------*/
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
.bcdUSB = __constant_cpu_to_le16(0x0200),
.bDeviceClass = USB_CLASS_COMM,
.bDeviceSubClass = 0,
.bDeviceProtocol = 0,
/* .bMaxPacketSize0 = f(hardware) */
/* Vendor and product id can be overridden by module parameters. */
.idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM),
.idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM),
/* .bcdDevice = f(hardware) */
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
/* NO SERIAL NUMBER */
.bNumConfigurations = 1,
};
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
/* REVISIT SRP-only hardware is possible, although
* it would not be called "OTG" ...
*/
.bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
};
static const struct usb_descriptor_header *otg_desc[] = {
(struct usb_descriptor_header *) &otg_descriptor,
NULL,
};
/* string IDs are assigned dynamically */
#define STRING_MANUFACTURER_IDX 0
#define STRING_PRODUCT_IDX 1
static char manufacturer[50];
static struct usb_string strings_dev[] = {
[STRING_MANUFACTURER_IDX].s = manufacturer,
[STRING_PRODUCT_IDX].s = DRIVER_DESC,
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_dev = {
.language = 0x0409, /* en-us */
.strings = strings_dev,
};
static struct usb_gadget_strings *dev_strings[] = {
&stringtab_dev,
NULL,
};
static u8 hostaddr[ETH_ALEN];
/*-------------------------------------------------------------------------*/
/*
* We _always_ have both CDC ECM and CDC ACM functions.
*/
static int __init cdc_do_config(struct usb_configuration *c)
{
int status;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
status = ecm_bind_config(c, hostaddr);
if (status < 0)
return status;
status = acm_bind_config(c, 0);
if (status < 0)
return status;
return 0;
}
static struct usb_configuration cdc_config_driver = {
.label = "CDC Composite (ECM + ACM)",
.bind = cdc_do_config,
.bConfigurationValue = 1,
/* .iConfiguration = DYNAMIC */
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1, /* 2 mA, minimal */
};
/*-------------------------------------------------------------------------*/
static int __init cdc_bind(struct usb_composite_dev *cdev)
{
int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
if (!can_support_ecm(cdev->gadget)) {
ERROR(cdev, "controller '%s' not usable\n", gadget->name);
return -EINVAL;
}
/* set up network link layer */
status = gether_setup(cdev->gadget, hostaddr);
if (status < 0)
return status;
/* set up serial link layer */
status = gserial_setup(cdev->gadget, 1);
if (status < 0)
goto fail0;
gcnum = usb_gadget_controller_number(gadget);
if (gcnum >= 0)
device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
else {
/* We assume that can_support_ecm() tells the truth;
* but if the controller isn't recognized at all then
* that assumption is a bit more likely to be wrong.
*/
WARN(cdev, "controller '%s' not recognized; trying %s\n",
gadget->name,
cdc_config_driver.label);
device_desc.bcdDevice =
__constant_cpu_to_le16(0x0300 | 0x0099);
}
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
/* device descriptor strings: manufacturer, product */
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
init_utsname()->sysname, init_utsname()->release,
gadget->name);
status = usb_string_id(cdev);
if (status < 0)
goto fail1;
strings_dev[STRING_MANUFACTURER_IDX].id = status;
device_desc.iManufacturer = status;
status = usb_string_id(cdev);
if (status < 0)
goto fail1;
strings_dev[STRING_PRODUCT_IDX].id = status;
device_desc.iProduct = status;
/* register our configuration */
status = usb_add_config(cdev, &cdc_config_driver);
if (status < 0)
goto fail1;
INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC);
return 0;
fail1:
gserial_cleanup();
fail0:
gether_cleanup();
return status;
}
static int __exit cdc_unbind(struct usb_composite_dev *cdev)
{
gserial_cleanup();
gether_cleanup();
return 0;
}
static struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
.bind = cdc_bind,
.unbind = __exit_p(cdc_unbind),
};
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
static int __init init(void)
{
return usb_composite_register(&cdc_driver);
}
module_init(init);
static void __exit cleanup(void)
{
usb_composite_unregister(&cdc_driver);
}
module_exit(cleanup);

File diff suppressed because it is too large Load Diff

View File

@ -96,7 +96,7 @@ int usb_gadget_config_buf(
/* config descriptor first */
if (length < USB_DT_CONFIG_SIZE || !desc)
return -EINVAL;
*cp = *config;
*cp = *config;
/* then interface/endpoint/class/vendor/... */
len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
@ -115,3 +115,77 @@ int usb_gadget_config_buf(
return len;
}
/**
* usb_copy_descriptors - copy a vector of USB descriptors
* @src: null-terminated vector to copy
* Context: initialization code, which may sleep
*
* This makes a copy of a vector of USB descriptors. Its primary use
* is to support usb_function objects which can have multiple copies,
* each needing different descriptors. Functions may have static
* tables of descriptors, which are used as templates and customized
* with identifiers (for interfaces, strings, endpoints, and more)
* as needed by a given function instance.
*/
struct usb_descriptor_header **__init
usb_copy_descriptors(struct usb_descriptor_header **src)
{
struct usb_descriptor_header **tmp;
unsigned bytes;
unsigned n_desc;
void *mem;
struct usb_descriptor_header **ret;
/* count descriptors and their sizes; then add vector size */
for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
bytes += (*tmp)->bLength;
bytes += (n_desc + 1) * sizeof(*tmp);
mem = kmalloc(bytes, GFP_KERNEL);
if (!mem)
return NULL;
/* fill in pointers starting at "tmp",
* to descriptors copied starting at "mem";
* and return "ret"
*/
tmp = mem;
ret = mem;
mem += (n_desc + 1) * sizeof(*tmp);
while (*src) {
memcpy(mem, *src, (*src)->bLength);
*tmp = mem;
tmp++;
mem += (*src)->bLength;
src++;
}
*tmp = NULL;
return ret;
}
/**
* usb_find_endpoint - find a copy of an endpoint descriptor
* @src: original vector of descriptors
* @copy: copy of @src
* @ep: endpoint descriptor found in @src
*
* This returns the copy of the @match descriptor made for @copy. Its
* intended use is to help remembering the endpoint descriptor to use
* when enabling a given endpoint.
*/
struct usb_endpoint_descriptor *__init
usb_find_endpoint(
struct usb_descriptor_header **src,
struct usb_descriptor_header **copy,
struct usb_endpoint_descriptor *match
)
{
while (*src) {
if (*src == (void *) match)
return (void *)*copy;
src++;
copy++;
}
return NULL;
}

View File

@ -862,7 +862,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
/* maybe claim OTG support, though we won't complete HNP */
dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
strcpy (dum->gadget.dev.bus_id, "gadget");
dev_set_name(&dum->gadget.dev, "gadget");
dum->gadget.dev.parent = &pdev->dev;
dum->gadget.dev.release = dummy_gadget_release;
rc = device_register (&dum->gadget.dev);
@ -1865,7 +1865,7 @@ static int dummy_hcd_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
if (!hcd)
return -ENOMEM;
the_controller = hcd_to_dummy (hcd);

View File

@ -159,6 +159,7 @@ ep_matches (
/* MATCH!! */
/* report address */
desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtol (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;

File diff suppressed because it is too large Load Diff

589
drivers/usb/gadget/f_acm.c Normal file
View File

@ -0,0 +1,589 @@
/*
* f_acm.c -- USB CDC serial (ACM) function driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/device.h>
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This CDC ACM function support just wraps control functions and
* notifications around the generic serial-over-usb code.
*
* Because CDC ACM is standardized by the USB-IF, many host operating
* systems have drivers for it. Accordingly, ACM is the preferred
* interop solution for serial-port type connections. The control
* models are often not necessary, and in any case don't do much in
* this bare-bones implementation.
*
* Note that even MS-Windows has some support for ACM. However, that
* support is somewhat broken because when you use ACM in a composite
* device, having multiple interfaces confuses the poor OS. It doesn't
* seem to understand CDC Union descriptors. The new "association"
* descriptors (roughly equivalent to CDC Unions) may sometimes help.
*/
struct acm_ep_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
struct usb_endpoint_descriptor *notify;
};
struct f_acm {
struct gserial port;
u8 ctrl_id, data_id;
u8 port_num;
struct usb_descriptor_header **fs_function;
struct acm_ep_descs fs;
struct usb_descriptor_header **hs_function;
struct acm_ep_descs hs;
struct usb_ep *notify;
struct usb_endpoint_descriptor *notify_desc;
struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
u16 port_handshake_bits;
#define RS232_RTS (1 << 1) /* unused with full duplex */
#define RS232_DTR (1 << 0) /* host is ready for data r/w */
};
static inline struct f_acm *func_to_acm(struct usb_function *f)
{
return container_of(f, struct f_acm, port.func);
}
/*-------------------------------------------------------------------------*/
/* notification endpoint uses smallish and infrequent fixed-size messages */
#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
#define GS_NOTIFY_MAXPACKET 8
/* interface and class descriptors: */
static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
/* .iInterface = DYNAMIC */
};
static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc acm_header_desc __initdata = {
.bLength = sizeof(acm_header_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = __constant_cpu_to_le16(0x0110),
};
static struct usb_cdc_call_mgmt_descriptor
acm_call_mgmt_descriptor __initdata = {
.bLength = sizeof(acm_call_mgmt_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
.bmCapabilities = 0,
/* .bDataInterface = DYNAMIC */
};
static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
.bLength = sizeof(acm_descriptor),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = (1 << 1),
};
static struct usb_cdc_union_desc acm_union_desc __initdata = {
.bLength = sizeof(acm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
};
static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *acm_fs_function[] __initdata = {
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_fs_notify_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_fs_in_desc,
(struct usb_descriptor_header *) &acm_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
.bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
};
static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *acm_hs_function[] __initdata = {
(struct usb_descriptor_header *) &acm_control_interface_desc,
(struct usb_descriptor_header *) &acm_header_desc,
(struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &acm_union_desc,
(struct usb_descriptor_header *) &acm_hs_notify_desc,
(struct usb_descriptor_header *) &acm_data_interface_desc,
(struct usb_descriptor_header *) &acm_hs_in_desc,
(struct usb_descriptor_header *) &acm_hs_out_desc,
NULL,
};
/* string descriptors: */
#define ACM_CTRL_IDX 0
#define ACM_DATA_IDX 1
/* static strings, in UTF-8 */
static struct usb_string acm_string_defs[] = {
[ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
[ACM_DATA_IDX].s = "CDC ACM Data",
{ /* ZEROES END LIST */ },
};
static struct usb_gadget_strings acm_string_table = {
.language = 0x0409, /* en-us */
.strings = acm_string_defs,
};
static struct usb_gadget_strings *acm_strings[] = {
&acm_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
/* ACM control ... data handling is delegated to tty library code.
* The main task of this function is to activate and deactivate
* that code based on device state; track parameters like line
* speed, handshake state, and so on; and issue notifications.
*/
static void acm_complete_set_line_coding(struct usb_ep *ep,
struct usb_request *req)
{
struct f_acm *acm = ep->driver_data;
struct usb_composite_dev *cdev = acm->port.func.config->cdev;
if (req->status != 0) {
DBG(cdev, "acm ttyGS%d completion, err %d\n",
acm->port_num, req->status);
return;
}
/* normal completion */
if (req->actual != sizeof(acm->port_line_coding)) {
DBG(cdev, "acm ttyGS%d short resp, len %d\n",
acm->port_num, req->actual);
usb_ep_set_halt(ep);
} else {
struct usb_cdc_line_coding *value = req->buf;
/* REVISIT: we currently just remember this data.
* If we change that, (a) validate it first, then
* (b) update whatever hardware needs updating,
* (c) worry about locking. This is information on
* the order of 9600-8-N-1 ... most of which means
* nothing unless we control a real RS232 line.
*/
acm->port_line_coding = *value;
}
}
static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
/* SET_LINE_CODING ... just read and save what the host sends */
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_LINE_CODING:
if (w_length != sizeof(struct usb_cdc_line_coding)
|| w_index != acm->ctrl_id)
goto invalid;
value = w_length;
cdev->gadget->ep0->driver_data = acm;
req->complete = acm_complete_set_line_coding;
break;
/* GET_LINE_CODING ... return what host sent, or initial value */
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_GET_LINE_CODING:
if (w_index != acm->ctrl_id)
goto invalid;
value = min_t(unsigned, w_length,
sizeof(struct usb_cdc_line_coding));
memcpy(req->buf, &acm->port_line_coding, value);
break;
/* SET_CONTROL_LINE_STATE ... save what the host sent */
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_REQ_SET_CONTROL_LINE_STATE:
if (w_index != acm->ctrl_id)
goto invalid;
value = 0;
/* FIXME we should not allow data to flow until the
* host sets the RS232_DTR bit; and when it clears
* that bit, we should return to that no-flow state.
*/
acm->port_handshake_bits = w_value;
break;
default:
invalid:
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
acm->port_num, ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "acm response on ttyGS%d, err %d\n",
acm->port_num, value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0, so this is an activation or a reset */
if (intf == acm->ctrl_id) {
/* REVISIT this may need more work when we start to
* send notifications ...
*/
if (acm->notify->driver_data) {
VDBG(cdev, "reset acm control interface %d\n", intf);
usb_ep_disable(acm->notify);
} else {
VDBG(cdev, "init acm ctrl interface %d\n", intf);
acm->notify_desc = ep_choose(cdev->gadget,
acm->hs.notify,
acm->fs.notify);
}
usb_ep_enable(acm->notify, acm->notify_desc);
acm->notify->driver_data = acm;
} else if (intf == acm->data_id) {
if (acm->port.in->driver_data) {
DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
gserial_disconnect(&acm->port);
} else {
DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
acm->port.in_desc = ep_choose(cdev->gadget,
acm->hs.in, acm->fs.in);
acm->port.out_desc = ep_choose(cdev->gadget,
acm->hs.out, acm->fs.out);
}
gserial_connect(&acm->port, acm->port_num);
} else
return -EINVAL;
return 0;
}
static void acm_disable(struct usb_function *f)
{
struct f_acm *acm = func_to_acm(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
gserial_disconnect(&acm->port);
usb_ep_disable(acm->notify);
acm->notify->driver_data = NULL;
}
/*-------------------------------------------------------------------------*/
/* ACM function driver setup/binding */
static int __init
acm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_acm *acm = func_to_acm(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
acm->ctrl_id = status;
acm_control_interface_desc.bInterfaceNumber = status;
acm_union_desc .bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
acm->data_id = status;
acm_data_interface_desc.bInterfaceNumber = status;
acm_union_desc.bSlaveInterface0 = status;
acm_call_mgmt_descriptor.bDataInterface = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
if (!ep)
goto fail;
acm->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
if (!ep)
goto fail;
acm->port.out = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
if (!ep)
goto fail;
acm->notify = ep;
ep->driver_data = cdev; /* claim */
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(acm_fs_function);
acm->fs.in = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_in_desc);
acm->fs.out = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_out_desc);
acm->fs.notify = usb_find_endpoint(acm_fs_function,
f->descriptors, &acm_fs_notify_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
acm_hs_in_desc.bEndpointAddress =
acm_fs_in_desc.bEndpointAddress;
acm_hs_out_desc.bEndpointAddress =
acm_fs_out_desc.bEndpointAddress;
acm_hs_notify_desc.bEndpointAddress =
acm_fs_notify_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
acm->hs.in = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_in_desc);
acm->hs.out = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_out_desc);
acm->hs.notify = usb_find_endpoint(acm_hs_function,
f->hs_descriptors, &acm_hs_notify_desc);
}
/* FIXME provide a callback for triggering notifications */
DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
acm->port_num,
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
acm->port.in->name, acm->port.out->name,
acm->notify->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (acm->notify)
acm->notify->driver_data = NULL;
if (acm->port.out)
acm->port.out->driver_data = NULL;
if (acm->port.in)
acm->port.in->driver_data = NULL;
ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
return status;
}
static void
acm_unbind(struct usb_configuration *c, struct usb_function *f)
{
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
kfree(func_to_acm(f));
}
/* Some controllers can't support CDC ACM ... */
static inline bool can_support_cdc(struct usb_configuration *c)
{
/* SH3 doesn't support multiple interfaces */
if (gadget_is_sh(c->cdev->gadget))
return false;
/* sa1100 doesn't have a third interrupt endpoint */
if (gadget_is_sa1100(c->cdev->gadget))
return false;
/* everything else is *probably* fine ... */
return true;
}
/**
* acm_bind_config - add a CDC ACM function to a configuration
* @c: the configuration to support the CDC ACM instance
* @port_num: /dev/ttyGS* port this interface will use
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gserial_setup() with enough ports to
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_acm *acm;
int status;
if (!can_support_cdc(c))
return -EINVAL;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string IDs, and patch descriptors */
if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_CTRL_IDX].id = status;
acm_control_interface_desc.iInterface = status;
status = usb_string_id(c->cdev);
if (status < 0)
return status;
acm_string_defs[ACM_DATA_IDX].id = status;
acm_data_interface_desc.iInterface = status;
}
/* allocate and initialize one new instance */
acm = kzalloc(sizeof *acm, GFP_KERNEL);
if (!acm)
return -ENOMEM;
acm->port_num = port_num;
acm->port.func.name = "acm";
acm->port.func.strings = acm_strings;
/* descriptors are per-instance copies */
acm->port.func.bind = acm_bind;
acm->port.func.unbind = acm_unbind;
acm->port.func.set_alt = acm_set_alt;
acm->port.func.setup = acm_setup;
acm->port.func.disable = acm_disable;
status = usb_add_function(c, &acm->port.func);
if (status)
kfree(acm);
return status;
}

833
drivers/usb/gadget/f_ecm.c Normal file
View File

@ -0,0 +1,833 @@
/*
* f_ecm.c -- USB CDC Ethernet (ECM) link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
/*
* This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
* Ethernet link. The data transfer model is simple (packets sent and
* received over bulk endpoints using normal short packet termination),
* and the control model exposes various data and optional notifications.
*
* ECM is well standardized and (except for Microsoft) supported by most
* operating systems with USB host support. It's the preferred interop
* solution for Ethernet over USB, at least for firmware based solutions.
* (Hardware solutions tend to be more minimalist.) A newer and simpler
* "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
*
* Note that ECM requires the use of "alternate settings" for its data
* interface. This means that the set_alt() method has real work to do,
* and also means that a get_alt() method is required.
*/
struct ecm_ep_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
struct usb_endpoint_descriptor *notify;
};
enum ecm_notify_state {
ECM_NOTIFY_NONE, /* don't notify */
ECM_NOTIFY_CONNECT, /* issue CONNECT next */
ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */
};
struct f_ecm {
struct gether port;
u8 ctrl_id, data_id;
char ethaddr[14];
struct usb_descriptor_header **fs_function;
struct ecm_ep_descs fs;
struct usb_descriptor_header **hs_function;
struct ecm_ep_descs hs;
struct usb_ep *notify;
struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
u8 notify_state;
bool is_open;
/* FIXME is_open needs some irq-ish locking
* ... possibly the same as port.ioport
*/
};
static inline struct f_ecm *func_to_ecm(struct usb_function *f)
{
return container_of(f, struct f_ecm, port.func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
static inline unsigned bitrate(struct usb_gadget *g)
{
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
/*
* Include the status endpoint if we can, even though it's optional.
*
* Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
* packet, to simplify cancellation; and a big transfer interval, to
* waste less bandwidth.
*
* Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
* if they ignore the connect/disconnect notifications that real aether
* can provide. More advanced cdc configurations might want to support
* encapsulated commands (vendor-specific, using control-OUT).
*/
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
/* interface descriptor: */
static struct usb_interface_descriptor ecm_control_intf __initdata = {
.bLength = sizeof ecm_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
.bInterfaceProtocol = USB_CDC_PROTO_NONE,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc header_desc __initdata = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = __constant_cpu_to_le16(0x0110),
};
static struct usb_cdc_union_desc ecm_union_desc __initdata = {
.bLength = sizeof(ecm_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
static struct usb_cdc_ether_desc ether_desc __initdata = {
.bLength = sizeof ether_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
.wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = __constant_cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
/* the default data interface has no endpoints ... */
static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
.bLength = sizeof ecm_data_nop_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 0,
.bNumEndpoints = 0,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* ... but the "real" data interface has two bulk endpoints */
static struct usb_interface_descriptor ecm_data_intf __initdata = {
.bLength = sizeof ecm_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bInterfaceNumber = 1,
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
};
static struct usb_endpoint_descriptor fs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *eth_fs_function[] __initdata = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ether_desc,
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &fs_notify_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
(struct usb_descriptor_header *) &fs_in_desc,
(struct usb_descriptor_header *) &fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
static struct usb_endpoint_descriptor hs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *eth_hs_function[] __initdata = {
/* CDC ECM control descriptors */
(struct usb_descriptor_header *) &ecm_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &ecm_union_desc,
(struct usb_descriptor_header *) &ether_desc,
/* NOTE: status endpoint might need to be removed */
(struct usb_descriptor_header *) &hs_notify_desc,
/* data interface, altsettings 0 and 1 */
(struct usb_descriptor_header *) &ecm_data_nop_intf,
(struct usb_descriptor_header *) &ecm_data_intf,
(struct usb_descriptor_header *) &hs_in_desc,
(struct usb_descriptor_header *) &hs_out_desc,
NULL,
};
/* string descriptors: */
static struct usb_string ecm_string_defs[] = {
[0].s = "CDC Ethernet Control Model (ECM)",
[1].s = NULL /* DYNAMIC */,
[2].s = "CDC Ethernet Data",
{ } /* end of list */
};
static struct usb_gadget_strings ecm_string_table = {
.language = 0x0409, /* en-us */
.strings = ecm_string_defs,
};
static struct usb_gadget_strings *ecm_strings[] = {
&ecm_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static void ecm_do_notify(struct f_ecm *ecm)
{
struct usb_request *req = ecm->notify_req;
struct usb_cdc_notification *event;
struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
__le32 *data;
int status;
/* notification already in flight? */
if (!req)
return;
event = req->buf;
switch (ecm->notify_state) {
case ECM_NOTIFY_NONE:
return;
case ECM_NOTIFY_CONNECT:
event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
if (ecm->is_open)
event->wValue = cpu_to_le16(1);
else
event->wValue = cpu_to_le16(0);
event->wLength = 0;
req->length = sizeof *event;
DBG(cdev, "notify connect %s\n",
ecm->is_open ? "true" : "false");
ecm->notify_state = ECM_NOTIFY_SPEED;
break;
case ECM_NOTIFY_SPEED:
event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
event->wValue = cpu_to_le16(0);
event->wLength = cpu_to_le16(8);
req->length = STATUS_BYTECOUNT;
/* SPEED_CHANGE data is up/down speeds in bits/sec */
data = req->buf + sizeof *event;
data[0] = cpu_to_le32(bitrate(cdev->gadget));
data[1] = data[0];
DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget));
ecm->notify_state = ECM_NOTIFY_NONE;
break;
}
event->bmRequestType = 0xA1;
event->wIndex = cpu_to_le16(ecm->ctrl_id);
ecm->notify_req = NULL;
status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
if (status < 0) {
ecm->notify_req = req;
DBG(cdev, "notify --> %d\n", status);
}
}
static void ecm_notify(struct f_ecm *ecm)
{
/* NOTE on most versions of Linux, host side cdc-ethernet
* won't listen for notifications until its netdevice opens.
* The first notification then sits in the FIFO for a long
* time, and the second one is queued.
*/
ecm->notify_state = ECM_NOTIFY_CONNECT;
ecm_do_notify(ecm);
}
static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_ecm *ecm = req->context;
struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
struct usb_cdc_notification *event = req->buf;
switch (req->status) {
case 0:
/* no fault */
break;
case -ECONNRESET:
case -ESHUTDOWN:
ecm->notify_state = ECM_NOTIFY_NONE;
break;
default:
DBG(cdev, "event %02x --> %d\n",
event->bNotificationType, req->status);
break;
}
ecm->notify_req = req;
ecm_do_notify(ecm);
}
static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SET_ETHERNET_PACKET_FILTER:
/* see 6.2.30: no data, wIndex = interface,
* wValue = packet filter bitmap
*/
if (w_length != 0 || w_index != ecm->ctrl_id)
goto invalid;
DBG(cdev, "packet filter %02x\n", w_value);
/* REVISIT locking of cdc_filter. This assumes the UDC
* driver won't have a concurrent packet TX irq running on
* another CPU; or that if it does, this write is atomic...
*/
ecm->port.cdc_filter = w_value;
value = 0;
break;
/* and optionally:
* case USB_CDC_SEND_ENCAPSULATED_COMMAND:
* case USB_CDC_GET_ENCAPSULATED_RESPONSE:
* case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
* case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
* case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
* case USB_CDC_GET_ETHERNET_STATISTIC:
*/
default:
invalid:
DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "ecm req %02x.%02x response err %d\n",
ctrl->bRequestType, ctrl->bRequest,
value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
if (intf == ecm->ctrl_id) {
if (alt != 0)
goto fail;
if (ecm->notify->driver_data) {
VDBG(cdev, "reset ecm control %d\n", intf);
usb_ep_disable(ecm->notify);
} else {
VDBG(cdev, "init ecm ctrl %d\n", intf);
ecm->notify_desc = ep_choose(cdev->gadget,
ecm->hs.notify,
ecm->fs.notify);
}
usb_ep_enable(ecm->notify, ecm->notify_desc);
ecm->notify->driver_data = ecm;
/* Data interface has two altsettings, 0 and 1 */
} else if (intf == ecm->data_id) {
if (alt > 1)
goto fail;
if (ecm->port.in_ep->driver_data) {
DBG(cdev, "reset ecm\n");
gether_disconnect(&ecm->port);
}
if (!ecm->port.in) {
DBG(cdev, "init ecm\n");
ecm->port.in = ep_choose(cdev->gadget,
ecm->hs.in, ecm->fs.in);
ecm->port.out = ep_choose(cdev->gadget,
ecm->hs.out, ecm->fs.out);
}
/* CDC Ethernet only sends data in non-default altsettings.
* Changing altsettings resets filters, statistics, etc.
*/
if (alt == 1) {
struct net_device *net;
/* Enable zlps by default for ECM conformance;
* override for musb_hdrc (avoids txdma ovhead)
* and sa1100 (can't).
*/
ecm->port.is_zlp_ok = !(
gadget_is_sa1100(cdev->gadget)
|| gadget_is_musbhdrc(cdev->gadget)
);
ecm->port.cdc_filter = DEFAULT_FILTER;
DBG(cdev, "activate ecm\n");
net = gether_connect(&ecm->port);
if (IS_ERR(net))
return PTR_ERR(net);
}
/* NOTE this can be a minor disagreement with the ECM spec,
* which says speed notifications will "always" follow
* connection notifications. But we allow one connect to
* follow another (if the first is in flight), and instead
* just guarantee that a speed notification is always sent.
*/
ecm_notify(ecm);
} else
goto fail;
return 0;
fail:
return -EINVAL;
}
/* Because the data interface supports multiple altsettings,
* this ECM function *MUST* implement a get_alt() method.
*/
static int ecm_get_alt(struct usb_function *f, unsigned intf)
{
struct f_ecm *ecm = func_to_ecm(f);
if (intf == ecm->ctrl_id)
return 0;
return ecm->port.in_ep->driver_data ? 1 : 0;
}
static void ecm_disable(struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ecm deactivated\n");
if (ecm->port.in_ep->driver_data)
gether_disconnect(&ecm->port);
if (ecm->notify->driver_data) {
usb_ep_disable(ecm->notify);
ecm->notify->driver_data = NULL;
ecm->notify_desc = NULL;
}
}
/*-------------------------------------------------------------------------*/
/*
* Callbacks let us notify the host about connect/disconnect when the
* net device is opened or closed.
*
* For testing, note that link states on this side include both opened
* and closed variants of:
*
* - disconnected/unconfigured
* - configured but inactive (data alt 0)
* - configured and active (data alt 1)
*
* Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
* SET_INTERFACE (altsetting). Remember also that "configured" doesn't
* imply the host is actually polling the notification endpoint, and
* likewise that "active" doesn't imply it's actually using the data
* endpoints for traffic.
*/
static void ecm_open(struct gether *geth)
{
struct f_ecm *ecm = func_to_ecm(&geth->func);
DBG(ecm->port.func.config->cdev, "%s\n", __func__);
ecm->is_open = true;
ecm_notify(ecm);
}
static void ecm_close(struct gether *geth)
{
struct f_ecm *ecm = func_to_ecm(&geth->func);
DBG(ecm->port.func.config->cdev, "%s\n", __func__);
ecm->is_open = false;
ecm_notify(ecm);
}
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
static int __init
ecm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ecm *ecm = func_to_ecm(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
ecm->ctrl_id = status;
ecm_control_intf.bInterfaceNumber = status;
ecm_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
ecm->data_id = status;
ecm_data_nop_intf.bInterfaceNumber = status;
ecm_data_intf.bInterfaceNumber = status;
ecm_union_desc.bSlaveInterface0 = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
goto fail;
ecm->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
goto fail;
ecm->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is *OPTIONAL* but we
* don't treat it that way. It's simpler, and some newer CDC
* profiles (wireless handsets) no longer treat it as optional.
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
if (!ep)
goto fail;
ecm->notify = ep;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* allocate notification request and buffer */
ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!ecm->notify_req)
goto fail;
ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
if (!ecm->notify_req->buf)
goto fail;
ecm->notify_req->context = ecm;
ecm->notify_req->complete = ecm_notify_complete;
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(eth_fs_function);
if (!f->descriptors)
goto fail;
ecm->fs.in = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_in_desc);
ecm->fs.out = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_out_desc);
ecm->fs.notify = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_notify_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_in_desc.bEndpointAddress =
fs_in_desc.bEndpointAddress;
hs_out_desc.bEndpointAddress =
fs_out_desc.bEndpointAddress;
hs_notify_desc.bEndpointAddress =
fs_notify_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
if (!f->hs_descriptors)
goto fail;
ecm->hs.in = usb_find_endpoint(eth_hs_function,
f->hs_descriptors, &hs_in_desc);
ecm->hs.out = usb_find_endpoint(eth_hs_function,
f->hs_descriptors, &hs_out_desc);
ecm->hs.notify = usb_find_endpoint(eth_hs_function,
f->hs_descriptors, &hs_notify_desc);
}
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
ecm->port.open = ecm_open;
ecm->port.close = ecm_close;
DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ecm->port.in_ep->name, ecm->port.out_ep->name,
ecm->notify->name);
return 0;
fail:
if (f->descriptors)
usb_free_descriptors(f->descriptors);
if (ecm->notify_req) {
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
}
/* we might as well release our claims on endpoints */
if (ecm->notify)
ecm->notify->driver_data = NULL;
if (ecm->port.out)
ecm->port.out_ep->driver_data = NULL;
if (ecm->port.in)
ecm->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
ecm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ecm *ecm = func_to_ecm(f);
DBG(c->cdev, "ecm unbind\n");
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
kfree(ecm->notify_req->buf);
usb_ep_free_request(ecm->notify, ecm->notify_req);
ecm_string_defs[1].s = NULL;
kfree(ecm);
}
/**
* ecm_bind_config - add CDC Ethernet network link to a configuration
* @c: the configuration to support the network link
* @ethaddr: a buffer in which the ethernet address of the host side
* side of the link was recorded
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_ecm *ecm;
int status;
if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
return -EINVAL;
/* maybe allocate device-global string IDs */
if (ecm_string_defs[0].id == 0) {
/* control interface label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ecm_string_defs[0].id = status;
ecm_control_intf.iInterface = status;
/* data interface label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ecm_string_defs[2].id = status;
ecm_data_intf.iInterface = status;
/* MAC address */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
ecm_string_defs[1].id = status;
ether_desc.iMACAddress = status;
}
/* allocate and initialize one new instance */
ecm = kzalloc(sizeof *ecm, GFP_KERNEL);
if (!ecm)
return -ENOMEM;
/* export host's Ethernet address in CDC format */
snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
"%02X%02X%02X%02X%02X%02X",
ethaddr[0], ethaddr[1], ethaddr[2],
ethaddr[3], ethaddr[4], ethaddr[5]);
ecm_string_defs[1].s = ecm->ethaddr;
ecm->port.cdc_filter = DEFAULT_FILTER;
ecm->port.func.name = "cdc_ethernet";
ecm->port.func.strings = ecm_strings;
/* descriptors are per-instance copies */
ecm->port.func.bind = ecm_bind;
ecm->port.func.unbind = ecm_unbind;
ecm->port.func.set_alt = ecm_set_alt;
ecm->port.func.get_alt = ecm_get_alt;
ecm->port.func.setup = ecm_setup;
ecm->port.func.disable = ecm_disable;
status = usb_add_function(c, &ecm->port.func);
if (status) {
ecm_string_defs[1].s = NULL;
kfree(ecm);
}
return status;
}

View File

@ -0,0 +1,381 @@
/*
* f_loopback.c - USB peripheral loopback configuration driver
*
* Copyright (C) 2003-2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This program 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 program 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
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/device.h>
#include "g_zero.h"
#include "gadget_chips.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
*
* This takes messages of various sizes written OUT to a device, and loops
* them back so they can be read IN from it. It has been used by certain
* test applications. It supports limited testing of data queueing logic.
*
*
* This is currently packaged as a configuration driver, which can't be
* combined with other functions to make composite devices. However, it
* can be combined with other independent configurations.
*/
struct f_loopback {
struct usb_function function;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
};
static inline struct f_loopback *func_to_loop(struct usb_function *f)
{
return container_of(f, struct f_loopback, function);
}
static unsigned qlen = 32;
module_param(qlen, uint, 0);
MODULE_PARM_DESC(qlenn, "depth of loopback queue");
/*-------------------------------------------------------------------------*/
static struct usb_interface_descriptor loopback_intf = {
.bLength = sizeof loopback_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &fs_sink_desc,
(struct usb_descriptor_header *) &fs_source_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_loopback_descs[] = {
(struct usb_descriptor_header *) &loopback_intf,
(struct usb_descriptor_header *) &hs_source_desc,
(struct usb_descriptor_header *) &hs_sink_desc,
NULL,
};
/* function-specific strings: */
static struct usb_string strings_loopback[] = {
[0].s = "loop input to output",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_loop = {
.language = 0x0409, /* en-us */
.strings = strings_loopback,
};
static struct usb_gadget_strings *loopback_strings[] = {
&stringtab_loop,
NULL,
};
/*-------------------------------------------------------------------------*/
static int __init
loopback_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_loopback *loop = func_to_loop(f);
int id;
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
loopback_intf.bInterfaceNumber = id;
/* allocate endpoints */
loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!loop->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n",
f->name, cdev->gadget->name);
return -ENODEV;
}
loop->in_ep->driver_data = cdev; /* claim */
loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
if (!loop->out_ep)
goto autoconf_fail;
loop->out_ep->driver_data = cdev; /* claim */
/* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->hs_descriptors = hs_loopback_descs;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
f->name, loop->in_ep->name, loop->out_ep->name);
return 0;
}
static void
loopback_unbind(struct usb_configuration *c, struct usb_function *f)
{
kfree(func_to_loop(f));
}
static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_loopback *loop = ep->driver_data;
struct usb_composite_dev *cdev = loop->function.config->cdev;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == loop->out_ep) {
/* loop this OUT packet back IN to the host */
req->zero = (req->actual < req->length);
req->length = req->actual;
status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
ERROR(cdev, "can't loop %s to %s: %d\n",
ep->name, loop->in_ep->name,
status);
}
/* queue the buffer for some later OUT packet */
req->length = buflen;
status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
if (status == 0)
return;
/* "should never get here" */
/* FALLTHROUGH */
default:
ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
/* FALLTHROUGH */
/* NOTE: since this driver doesn't maintain an explicit record
* of requests it submitted (just maintains qlen count), we
* rely on the hardware driver to clean up on disconnect or
* endpoint disable.
*/
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
free_ep_req(ep, req);
return;
}
}
static void disable_loopback(struct f_loopback *loop)
{
struct usb_composite_dev *cdev;
cdev = loop->function.config->cdev;
disable_endpoints(cdev, loop->in_ep, loop->out_ep);
VDBG(cdev, "%s disabled\n", loop->function.name);
}
static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
int result = 0;
const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
struct usb_request *req;
unsigned i;
src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
/* one endpoint writes data back IN to the host */
ep = loop->in_ep;
result = usb_ep_enable(ep, src);
if (result < 0)
return result;
ep->driver_data = loop;
/* one endpoint just reads OUT packets */
ep = loop->out_ep;
result = usb_ep_enable(ep, sink);
if (result < 0) {
fail0:
ep = loop->in_ep;
usb_ep_disable(ep);
ep->driver_data = NULL;
return result;
}
ep->driver_data = loop;
/* allocate a bunch of read buffers and queue them all at once.
* we buffer at most 'qlen' transfers; fewer if any need more
* than 'buflen' bytes each.
*/
for (i = 0; i < qlen && result == 0; i++) {
req = alloc_ep_req(ep);
if (req) {
req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
if (result)
ERROR(cdev, "%s queue req --> %d\n",
ep->name, result);
} else {
usb_ep_disable(ep);
ep->driver_data = NULL;
result = -ENOMEM;
goto fail0;
}
}
DBG(cdev, "%s enabled\n", loop->function.name);
return result;
}
static int loopback_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct f_loopback *loop = func_to_loop(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt is zero */
if (loop->in_ep->driver_data)
disable_loopback(loop);
return enable_loopback(cdev, loop);
}
static void loopback_disable(struct usb_function *f)
{
struct f_loopback *loop = func_to_loop(f);
disable_loopback(loop);
}
/*-------------------------------------------------------------------------*/
static int __init loopback_bind_config(struct usb_configuration *c)
{
struct f_loopback *loop;
int status;
loop = kzalloc(sizeof *loop, GFP_KERNEL);
if (!loop)
return -ENOMEM;
loop->function.name = "loopback";
loop->function.descriptors = fs_loopback_descs;
loop->function.bind = loopback_bind;
loop->function.unbind = loopback_unbind;
loop->function.set_alt = loopback_set_alt;
loop->function.disable = loopback_disable;
status = usb_add_function(c, &loop->function);
if (status)
kfree(loop);
return status;
}
static struct usb_configuration loopback_driver = {
.label = "loopback",
.strings = loopback_strings,
.bind = loopback_bind_config,
.bConfigurationValue = 2,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1, /* 2 mA, minimal */
/* .iConfiguration = DYNAMIC */
};
/**
* loopback_add - add a loopback testing configuration to a device
* @cdev: the device to support the loopback configuration
*/
int __init loopback_add(struct usb_composite_dev *cdev)
{
int id;
/* allocate string ID(s) */
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_loopback[0].id = id;
loopback_intf.iInterface = id;
loopback_driver.iConfiguration = id;
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
loopback_driver.descriptors = otg_desc;
loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
return usb_add_config(cdev, &loopback_driver);
}

View File

@ -0,0 +1,827 @@
/*
* f_rndis.c -- RNDIS link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include <asm/atomic.h>
#include "u_ether.h"
#include "rndis.h"
/*
* This function is an RNDIS Ethernet port -- a Microsoft protocol that's
* been promoted instead of the standard CDC Ethernet. The published RNDIS
* spec is ambiguous, incomplete, and needlessly complex. Variants such as
* ActiveSync have even worse status in terms of specification.
*
* In short: it's a protocol controlled by (and for) Microsoft, not for an
* Open ecosystem or markets. Linux supports it *only* because Microsoft
* doesn't support the CDC Ethernet standard.
*
* The RNDIS data transfer model is complex, with multiple Ethernet packets
* per USB message, and out of band data. The control model is built around
* what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
* (modem, not Ethernet) veneer, with those ACM descriptors being entirely
* useless (they're ignored). RNDIS expects to be the only function in its
* configuration, so it's no real help if you need composite devices; and
* it expects to be the first configuration too.
*
* There is a single technical advantage of RNDIS over CDC Ethernet, if you
* discount the fluff that its RPC can be made to deliver: it doesn't need
* a NOP altsetting for the data interface. That lets it work on some of the
* "so smart it's stupid" hardware which takes over configuration changes
* from the software, and adds restrictions like "no altsettings".
*
* Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
* have all sorts of contrary-to-specification oddities that can prevent
* them from working sanely. Since bugfixes (or accurate specs, letting
* Linux work around those bugs) are unlikely to ever come from MSFT, you
* may want to avoid using RNDIS on purely operational grounds.
*
* Omissions from the RNDIS 1.0 specification include:
*
* - Power management ... references data that's scattered around lots
* of other documentation, which is incorrect/incomplete there too.
*
* - There are various undocumented protocol requirements, like the need
* to send garbage in some control-OUT messages.
*
* - MS-Windows drivers sometimes emit undocumented requests.
*/
struct rndis_ep_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
struct usb_endpoint_descriptor *notify;
};
struct f_rndis {
struct gether port;
u8 ctrl_id, data_id;
u8 ethaddr[ETH_ALEN];
int config;
struct usb_descriptor_header **fs_function;
struct rndis_ep_descs fs;
struct usb_descriptor_header **hs_function;
struct rndis_ep_descs hs;
struct usb_ep *notify;
struct usb_endpoint_descriptor *notify_desc;
struct usb_request *notify_req;
atomic_t notify_count;
};
static inline struct f_rndis *func_to_rndis(struct usb_function *f)
{
return container_of(f, struct f_rndis, port.func);
}
/* peak (theoretical) bulk transfer rate in bits-per-second */
static unsigned int bitrate(struct usb_gadget *g)
{
if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
return 13 * 512 * 8 * 1000 * 8;
else
return 19 * 64 * 1 * 1000 * 8;
}
/*-------------------------------------------------------------------------*/
/*
*/
#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
#define STATUS_BYTECOUNT 8 /* 8 bytes data */
/* interface descriptor: */
static struct usb_interface_descriptor rndis_control_intf __initdata = {
.bLength = sizeof rndis_control_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
/* status endpoint is optional; this could be patched later */
.bNumEndpoints = 1,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc header_desc __initdata = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = __constant_cpu_to_le16(0x0110),
};
static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
.bLength = sizeof call_mgmt_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
.bmCapabilities = 0x00,
.bDataInterface = 0x01,
};
static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
.bLength = sizeof acm_descriptor,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ACM_TYPE,
.bmCapabilities = 0x00,
};
static struct usb_cdc_union_desc rndis_union_desc __initdata = {
.bLength = sizeof(rndis_union_desc),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_UNION_TYPE,
/* .bMasterInterface0 = DYNAMIC */
/* .bSlaveInterface0 = DYNAMIC */
};
/* the data interface has two bulk endpoints */
static struct usb_interface_descriptor rndis_data_intf __initdata = {
.bLength = sizeof rndis_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bAlternateSetting = 1,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_CDC_DATA,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
};
static struct usb_endpoint_descriptor fs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *eth_fs_function[] __initdata = {
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &fs_notify_desc,
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &fs_in_desc,
(struct usb_descriptor_header *) &fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
.bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
};
static struct usb_endpoint_descriptor hs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *eth_hs_function[] __initdata = {
/* control interface matches ACM, not Ethernet */
(struct usb_descriptor_header *) &rndis_control_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &call_mgmt_descriptor,
(struct usb_descriptor_header *) &acm_descriptor,
(struct usb_descriptor_header *) &rndis_union_desc,
(struct usb_descriptor_header *) &hs_notify_desc,
/* data interface has no altsetting */
(struct usb_descriptor_header *) &rndis_data_intf,
(struct usb_descriptor_header *) &hs_in_desc,
(struct usb_descriptor_header *) &hs_out_desc,
NULL,
};
/* string descriptors: */
static struct usb_string rndis_string_defs[] = {
[0].s = "RNDIS Communications Control",
[1].s = "RNDIS Ethernet Data",
{ } /* end of list */
};
static struct usb_gadget_strings rndis_string_table = {
.language = 0x0409, /* en-us */
.strings = rndis_string_defs,
};
static struct usb_gadget_strings *rndis_strings[] = {
&rndis_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static struct sk_buff *rndis_add_header(struct sk_buff *skb)
{
skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
if (skb)
rndis_add_hdr(skb);
return skb;
}
static void rndis_response_available(void *_rndis)
{
struct f_rndis *rndis = _rndis;
struct usb_request *req = rndis->notify_req;
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
__le32 *data = req->buf;
int status;
if (atomic_inc_return(&rndis->notify_count))
return;
/* Send RNDIS RESPONSE_AVAILABLE notification; a
* USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
*
* This is the only notification defined by RNDIS.
*/
data[0] = cpu_to_le32(1);
data[1] = cpu_to_le32(0);
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
if (status) {
atomic_dec(&rndis->notify_count);
DBG(cdev, "notify/0 --> %d\n", status);
}
}
static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_rndis *rndis = req->context;
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
int status = req->status;
/* after TX:
* - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
* - RNDIS_RESPONSE_AVAILABLE (status/irq)
*/
switch (status) {
case -ECONNRESET:
case -ESHUTDOWN:
/* connection gone */
atomic_set(&rndis->notify_count, 0);
break;
default:
DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
ep->name, status,
req->actual, req->length);
/* FALLTHROUGH */
case 0:
if (ep != rndis->notify)
break;
/* handle multiple pending RNDIS_RESPONSE_AVAILABLE
* notifications by resending until we're done
*/
if (atomic_dec_and_test(&rndis->notify_count))
break;
status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
if (status) {
atomic_dec(&rndis->notify_count);
DBG(cdev, "notify/1 --> %d\n", status);
}
break;
}
}
static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_rndis *rndis = req->context;
struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
int status;
/* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
// spin_lock(&dev->lock);
status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
if (status < 0)
ERROR(cdev, "RNDIS command error %d, %d/%d\n",
status, req->actual, req->length);
// spin_unlock(&dev->lock);
}
static int
rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct usb_request *req = cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* CDC class messages; interface activation uses set_alt().
*/
switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
/* RNDIS uses the CDC command encapsulation mechanism to implement
* an RPC scheme, with much getting/setting of attributes by OID.
*/
case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_SEND_ENCAPSULATED_COMMAND:
if (w_length > req->length || w_value
|| w_index != rndis->ctrl_id)
goto invalid;
/* read the request; process it later */
value = w_length;
req->complete = rndis_command_complete;
req->context = rndis;
/* later, rndis_response_available() sends a notification */
break;
case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
| USB_CDC_GET_ENCAPSULATED_RESPONSE:
if (w_value || w_index != rndis->ctrl_id)
goto invalid;
else {
u8 *buf;
u32 n;
/* return the result */
buf = rndis_get_next_response(rndis->config, &n);
if (buf) {
memcpy(req->buf, buf, n);
req->complete = rndis_response_complete;
rndis_free_response(rndis->config, buf);
value = n;
}
/* else stalls ... spec says to avoid that */
}
break;
default:
invalid:
VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(cdev, "rndis response on err %d\n", value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0 */
if (intf == rndis->ctrl_id) {
if (rndis->notify->driver_data) {
VDBG(cdev, "reset rndis control %d\n", intf);
usb_ep_disable(rndis->notify);
} else {
VDBG(cdev, "init rndis ctrl %d\n", intf);
rndis->notify_desc = ep_choose(cdev->gadget,
rndis->hs.notify,
rndis->fs.notify);
}
usb_ep_enable(rndis->notify, rndis->notify_desc);
rndis->notify->driver_data = rndis;
} else if (intf == rndis->data_id) {
struct net_device *net;
if (rndis->port.in_ep->driver_data) {
DBG(cdev, "reset rndis\n");
gether_disconnect(&rndis->port);
} else {
DBG(cdev, "init rndis\n");
rndis->port.in = ep_choose(cdev->gadget,
rndis->hs.in, rndis->fs.in);
rndis->port.out = ep_choose(cdev->gadget,
rndis->hs.out, rndis->fs.out);
}
/* Avoid ZLPs; they can be troublesome. */
rndis->port.is_zlp_ok = false;
/* RNDIS should be in the "RNDIS uninitialized" state,
* either never activated or after rndis_uninit().
*
* We don't want data to flow here until a nonzero packet
* filter is set, at which point it enters "RNDIS data
* initialized" state ... but we do want the endpoints
* to be activated. It's a strange little state.
*
* REVISIT the RNDIS gadget code has done this wrong for a
* very long time. We need another call to the link layer
* code -- gether_updown(...bool) maybe -- to do it right.
*/
rndis->port.cdc_filter = 0;
DBG(cdev, "RNDIS RX/TX early activation ... \n");
net = gether_connect(&rndis->port);
if (IS_ERR(net))
return PTR_ERR(net);
rndis_set_param_dev(rndis->config, net,
&rndis->port.cdc_filter);
} else
goto fail;
return 0;
fail:
return -EINVAL;
}
static void rndis_disable(struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
struct usb_composite_dev *cdev = f->config->cdev;
if (!rndis->notify->driver_data)
return;
DBG(cdev, "rndis deactivated\n");
rndis_uninit(rndis->config);
gether_disconnect(&rndis->port);
usb_ep_disable(rndis->notify);
rndis->notify->driver_data = NULL;
}
/*-------------------------------------------------------------------------*/
/*
* This isn't quite the same mechanism as CDC Ethernet, since the
* notification scheme passes less data, but the same set of link
* states must be tested. A key difference is that altsettings are
* not used to tell whether the link should send packets or not.
*/
static void rndis_open(struct gether *geth)
{
struct f_rndis *rndis = func_to_rndis(&geth->func);
struct usb_composite_dev *cdev = geth->func.config->cdev;
DBG(cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3,
bitrate(cdev->gadget) / 100);
rndis_signal_connect(rndis->config);
}
static void rndis_close(struct gether *geth)
{
struct f_rndis *rndis = func_to_rndis(&geth->func);
DBG(geth->func.config->cdev, "%s\n", __func__);
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
rndis_signal_disconnect(rndis->config);
}
/*-------------------------------------------------------------------------*/
/* ethernet function driver setup/binding */
static int __init
rndis_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_rndis *rndis = func_to_rndis(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->ctrl_id = status;
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
rndis->data_id = status;
rndis_data_intf.bInterfaceNumber = status;
rndis_union_desc.bSlaveInterface0 = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
goto fail;
rndis->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
goto fail;
rndis->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* NOTE: a status/notification endpoint is, strictly speaking,
* optional. We don't treat it that way though! It's simpler,
* and some newer profiles don't treat it as optional.
*/
ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
if (!ep)
goto fail;
rndis->notify = ep;
ep->driver_data = cdev; /* claim */
status = -ENOMEM;
/* allocate notification request and buffer */
rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
if (!rndis->notify_req)
goto fail;
rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
if (!rndis->notify_req->buf)
goto fail;
rndis->notify_req->length = STATUS_BYTECOUNT;
rndis->notify_req->context = rndis;
rndis->notify_req->complete = rndis_response_complete;
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(eth_fs_function);
if (!f->descriptors)
goto fail;
rndis->fs.in = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_in_desc);
rndis->fs.out = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_out_desc);
rndis->fs.notify = usb_find_endpoint(eth_fs_function,
f->descriptors, &fs_notify_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_in_desc.bEndpointAddress =
fs_in_desc.bEndpointAddress;
hs_out_desc.bEndpointAddress =
fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
if (!f->hs_descriptors)
goto fail;
rndis->hs.in = usb_find_endpoint(eth_hs_function,
f->hs_descriptors, &hs_in_desc);
rndis->hs.out = usb_find_endpoint(eth_hs_function,
f->hs_descriptors, &hs_out_desc);
}
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
status = rndis_register(rndis_response_available, rndis);
if (status < 0)
goto fail;
rndis->config = status;
rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
#if 0
// FIXME
if (rndis_set_param_vendor(rndis->config, vendorID,
manufacturer))
goto fail0;
#endif
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
rndis->port.in_ep->name, rndis->port.out_ep->name,
rndis->notify->name);
return 0;
fail:
if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
usb_free_descriptors(f->hs_descriptors);
if (f->descriptors)
usb_free_descriptors(f->descriptors);
if (rndis->notify_req) {
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
}
/* we might as well release our claims on endpoints */
if (rndis->notify)
rndis->notify->driver_data = NULL;
if (rndis->port.out)
rndis->port.out_ep->driver_data = NULL;
if (rndis->port.in)
rndis->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
rndis_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_rndis *rndis = func_to_rndis(f);
rndis_deregister(rndis->config);
rndis_exit();
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
kfree(rndis->notify_req->buf);
usb_ep_free_request(rndis->notify, rndis->notify_req);
kfree(rndis);
}
/* Some controllers can't support RNDIS ... */
static inline bool can_support_rndis(struct usb_configuration *c)
{
/* only two endpoints on sa1100 */
if (gadget_is_sa1100(c->cdev->gadget))
return false;
/* everything else is *presumably* fine */
return true;
}
/**
* rndis_bind_config - add RNDIS network link to a configuration
* @c: the configuration to support the network link
* @ethaddr: a buffer in which the ethernet address of the host side
* side of the link was recorded
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_rndis *rndis;
int status;
if (!can_support_rndis(c) || !ethaddr)
return -EINVAL;
/* maybe allocate device-global string IDs */
if (rndis_string_defs[0].id == 0) {
/* ... and setup RNDIS itself */
status = rndis_init();
if (status < 0)
return status;
/* control interface label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
rndis_string_defs[0].id = status;
rndis_control_intf.iInterface = status;
/* data interface label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
rndis_string_defs[1].id = status;
rndis_data_intf.iInterface = status;
}
/* allocate and initialize one new instance */
status = -ENOMEM;
rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
if (!rndis)
goto fail;
memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
/* RNDIS activates when the host changes this filter */
rndis->port.cdc_filter = 0;
/* RNDIS has special (and complex) framing */
rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
rndis->port.wrap = rndis_add_header;
rndis->port.unwrap = rndis_rm_hdr;
rndis->port.func.name = "rndis";
rndis->port.func.strings = rndis_strings;
/* descriptors are per-instance copies */
rndis->port.func.bind = rndis_bind;
rndis->port.func.unbind = rndis_unbind;
rndis->port.func.set_alt = rndis_set_alt;
rndis->port.func.setup = rndis_setup;
rndis->port.func.disable = rndis_disable;
status = usb_add_function(c, &rndis->port.func);
if (status) {
kfree(rndis);
fail:
rndis_exit();
}
return status;
}

View File

@ -0,0 +1,296 @@
/*
* f_serial.c - generic USB serial function driver
*
* Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
* Copyright (C) 2008 by David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/device.h>
#include "u_serial.h"
#include "gadget_chips.h"
/*
* This function packages a simple "generic serial" port with no real
* control mechanisms, just raw data transfer over two bulk endpoints.
*
* Because it's not standardized, this isn't as interoperable as the
* CDC ACM driver. However, for many purposes it's just as functional
* if you can arrange appropriate host side drivers.
*/
struct gser_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
};
struct f_gser {
struct gserial port;
u8 data_id;
u8 port_num;
struct usb_descriptor_header **fs_function;
struct gser_descs fs;
struct usb_descriptor_header **hs_function;
struct gser_descs hs;
};
static inline struct f_gser *func_to_gser(struct usb_function *f)
{
return container_of(f, struct f_gser, port.func);
}
/*-------------------------------------------------------------------------*/
/* interface descriptor: */
static struct usb_interface_descriptor gser_interface_desc __initdata = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
.bInterfaceSubClass = 0,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *gser_fs_function[] __initdata = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_fs_in_desc,
(struct usb_descriptor_header *) &gser_fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *gser_hs_function[] __initdata = {
(struct usb_descriptor_header *) &gser_interface_desc,
(struct usb_descriptor_header *) &gser_hs_in_desc,
(struct usb_descriptor_header *) &gser_hs_out_desc,
NULL,
};
/* string descriptors: */
static struct usb_string gser_string_defs[] = {
[0].s = "Generic Serial",
{ } /* end of list */
};
static struct usb_gadget_strings gser_string_table = {
.language = 0x0409, /* en-us */
.strings = gser_string_defs,
};
static struct usb_gadget_strings *gser_strings[] = {
&gser_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt == 0, so this is an activation or a reset */
if (gser->port.in->driver_data) {
DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
gserial_disconnect(&gser->port);
} else {
DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
gser->port.in_desc = ep_choose(cdev->gadget,
gser->hs.in, gser->fs.in);
gser->port.out_desc = ep_choose(cdev->gadget,
gser->hs.out, gser->fs.out);
}
gserial_connect(&gser->port, gser->port_num);
return 0;
}
static void gser_disable(struct usb_function *f)
{
struct f_gser *gser = func_to_gser(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
gserial_disconnect(&gser->port);
}
/*-------------------------------------------------------------------------*/
/* serial function driver setup/binding */
static int __init
gser_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_gser *gser = func_to_gser(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
gser->data_id = status;
gser_interface_desc.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
if (!ep)
goto fail;
gser->port.in = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
if (!ep)
goto fail;
gser->port.out = ep;
ep->driver_data = cdev; /* claim */
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(gser_fs_function);
gser->fs.in = usb_find_endpoint(gser_fs_function,
f->descriptors, &gser_fs_in_desc);
gser->fs.out = usb_find_endpoint(gser_fs_function,
f->descriptors, &gser_fs_out_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
gser_hs_in_desc.bEndpointAddress =
gser_fs_in_desc.bEndpointAddress;
gser_hs_out_desc.bEndpointAddress =
gser_fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
gser->hs.in = usb_find_endpoint(gser_hs_function,
f->hs_descriptors, &gser_hs_in_desc);
gser->hs.out = usb_find_endpoint(gser_hs_function,
f->hs_descriptors, &gser_hs_out_desc);
}
DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
gser->port_num,
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
gser->port.in->name, gser->port.out->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (gser->port.out)
gser->port.out->driver_data = NULL;
if (gser->port.in)
gser->port.in->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
gser_unbind(struct usb_configuration *c, struct usb_function *f)
{
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
kfree(func_to_gser(f));
}
/**
* gser_bind_config - add a generic serial function to a configuration
* @c: the configuration to support the serial instance
* @port_num: /dev/ttyGS* port this interface will use
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gserial_setup() with enough ports to
* handle all the ones it binds. Caller is also responsible
* for calling @gserial_cleanup() before module unload.
*/
int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
{
struct f_gser *gser;
int status;
/* REVISIT might want instance-specific strings to help
* distinguish instances ...
*/
/* maybe allocate device-global string ID */
if (gser_string_defs[0].id == 0) {
status = usb_string_id(c->cdev);
if (status < 0)
return status;
gser_string_defs[0].id = status;
}
/* allocate and initialize one new instance */
gser = kzalloc(sizeof *gser, GFP_KERNEL);
if (!gser)
return -ENOMEM;
gser->port_num = port_num;
gser->port.func.name = "gser";
gser->port.func.strings = gser_strings;
gser->port.func.bind = gser_bind;
gser->port.func.unbind = gser_unbind;
gser->port.func.set_alt = gser_set_alt;
gser->port.func.disable = gser_disable;
status = usb_add_function(c, &gser->port.func);
if (status)
kfree(gser);
return status;
}

View File

@ -0,0 +1,587 @@
/*
* f_sourcesink.c - USB peripheral source/sink configuration driver
*
* Copyright (C) 2003-2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This program 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 program 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
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/device.h>
#include "g_zero.h"
#include "gadget_chips.h"
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
* controller drivers.
*
* This just sinks bulk packets OUT to the peripheral and sources them IN
* to the host, optionally with specific data patterns for integrity tests.
* As such it supports basic functionality and load tests.
*
* In terms of control messaging, this supports all the standard requests
* plus two that support control-OUT tests. If the optional "autoresume"
* mode is enabled, it provides good functional coverage for the "USBCV"
* test harness from USB-IF.
*
* Note that because this doesn't queue more than one request at a time,
* some other function must be used to test queueing logic. The network
* link (g_ether) is the best overall option for that, since its TX and RX
* queues are relatively independent, will receive a range of packet sizes,
* and can often be made to run out completely. Those issues are important
* when stress testing peripheral controller drivers.
*
*
* This is currently packaged as a configuration driver, which can't be
* combined with other functions to make composite devices. However, it
* can be combined with other independent configurations.
*/
struct f_sourcesink {
struct usb_function function;
struct usb_ep *in_ep;
struct usb_ep *out_ep;
struct timer_list resume;
};
static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
{
return container_of(f, struct f_sourcesink, function);
}
static unsigned autoresume;
module_param(autoresume, uint, 0);
MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
static unsigned pattern;
module_param(pattern, uint, 0);
MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
/*-------------------------------------------------------------------------*/
static struct usb_interface_descriptor source_sink_intf = {
.bLength = sizeof source_sink_intf,
.bDescriptorType = USB_DT_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_VENDOR_SPEC,
/* .iInterface = DYNAMIC */
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf,
(struct usb_descriptor_header *) &fs_sink_desc,
(struct usb_descriptor_header *) &fs_source_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_source_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_sink_desc = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_source_sink_descs[] = {
(struct usb_descriptor_header *) &source_sink_intf,
(struct usb_descriptor_header *) &hs_source_desc,
(struct usb_descriptor_header *) &hs_sink_desc,
NULL,
};
/* function-specific strings: */
static struct usb_string strings_sourcesink[] = {
[0].s = "source and sink data",
{ } /* end of list */
};
static struct usb_gadget_strings stringtab_sourcesink = {
.language = 0x0409, /* en-us */
.strings = strings_sourcesink,
};
static struct usb_gadget_strings *sourcesink_strings[] = {
&stringtab_sourcesink,
NULL,
};
/*-------------------------------------------------------------------------*/
static void sourcesink_autoresume(unsigned long _c)
{
struct usb_composite_dev *cdev = (void *)_c;
struct usb_gadget *g = cdev->gadget;
/* Normally the host would be woken up for something
* more significant than just a timer firing; likely
* because of some direct user request.
*/
if (g->speed != USB_SPEED_UNKNOWN) {
int status = usb_gadget_wakeup(g);
DBG(cdev, "%s --> %d\n", __func__, status);
}
}
static int __init
sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_sourcesink *ss = func_to_ss(f);
int id;
/* allocate interface ID(s) */
id = usb_interface_id(c, f);
if (id < 0)
return id;
source_sink_intf.bInterfaceNumber = id;
/* allocate endpoints */
ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
if (!ss->in_ep) {
autoconf_fail:
ERROR(cdev, "%s: can't autoconfigure on %s\n",
f->name, cdev->gadget->name);
return -ENODEV;
}
ss->in_ep->driver_data = cdev; /* claim */
ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
if (!ss->out_ep)
goto autoconf_fail;
ss->out_ep->driver_data = cdev; /* claim */
setup_timer(&ss->resume, sourcesink_autoresume,
(unsigned long) c->cdev);
/* support high speed hardware */
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_source_desc.bEndpointAddress =
fs_source_desc.bEndpointAddress;
hs_sink_desc.bEndpointAddress =
fs_sink_desc.bEndpointAddress;
f->hs_descriptors = hs_source_sink_descs;
}
DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
f->name, ss->in_ep->name, ss->out_ep->name);
return 0;
}
static void
sourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
{
kfree(func_to_ss(f));
}
/* optionally require specific source/sink data patterns */
static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
{
unsigned i;
u8 *buf = req->buf;
struct usb_composite_dev *cdev = ss->function.config->cdev;
for (i = 0; i < req->actual; i++, buf++) {
switch (pattern) {
/* all-zeroes has no synchronization issues */
case 0:
if (*buf == 0)
continue;
break;
/* "mod63" stays in sync with short-terminated transfers,
* OR otherwise when host and gadget agree on how large
* each usb transfer request should be. Resync is done
* with set_interface or set_config. (We *WANT* it to
* get quickly out of sync if controllers or their drivers
* stutter for any reason, including buffer duplcation...)
*/
case 1:
if (*buf == (u8)(i % 63))
continue;
break;
}
ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
usb_ep_set_halt(ss->out_ep);
return -EINVAL;
}
return 0;
}
static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
{
unsigned i;
u8 *buf = req->buf;
switch (pattern) {
case 0:
memset(req->buf, 0, req->length);
break;
case 1:
for (i = 0; i < req->length; i++)
*buf++ = (u8) (i % 63);
break;
}
}
static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
{
struct f_sourcesink *ss = ep->driver_data;
struct usb_composite_dev *cdev = ss->function.config->cdev;
int status = req->status;
switch (status) {
case 0: /* normal completion? */
if (ep == ss->out_ep) {
check_read_data(ss, req);
memset(req->buf, 0x55, req->length);
} else
reinit_write_data(ep, req);
break;
/* this endpoint is normally active while we're configured */
case -ECONNABORTED: /* hardware forced ep reset */
case -ECONNRESET: /* request dequeued */
case -ESHUTDOWN: /* disconnect from host */
VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
req->actual, req->length);
if (ep == ss->out_ep)
check_read_data(ss, req);
free_ep_req(ep, req);
return;
case -EOVERFLOW: /* buffer overrun on read means that
* we didn't provide a big enough
* buffer.
*/
default:
#if 1
DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
#endif
case -EREMOTEIO: /* short read */
break;
}
status = usb_ep_queue(ep, req, GFP_ATOMIC);
if (status) {
ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n",
ep->name, req->length, status);
usb_ep_set_halt(ep);
/* FIXME recover later ... somehow */
}
}
static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
{
struct usb_ep *ep;
struct usb_request *req;
int status;
ep = is_in ? ss->in_ep : ss->out_ep;
req = alloc_ep_req(ep);
if (!req)
return -ENOMEM;
req->complete = source_sink_complete;
if (is_in)
reinit_write_data(ep, req);
else
memset(req->buf, 0x55, req->length);
status = usb_ep_queue(ep, req, GFP_ATOMIC);
if (status) {
struct usb_composite_dev *cdev;
cdev = ss->function.config->cdev;
ERROR(cdev, "start %s %s --> %d\n",
is_in ? "IN" : "OUT",
ep->name, status);
free_ep_req(ep, req);
}
return status;
}
static void disable_source_sink(struct f_sourcesink *ss)
{
struct usb_composite_dev *cdev;
cdev = ss->function.config->cdev;
disable_endpoints(cdev, ss->in_ep, ss->out_ep);
del_timer(&ss->resume);
VDBG(cdev, "%s disabled\n", ss->function.name);
}
static int
enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
{
int result = 0;
const struct usb_endpoint_descriptor *src, *sink;
struct usb_ep *ep;
src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
/* one endpoint writes (sources) zeroes IN (to the host) */
ep = ss->in_ep;
result = usb_ep_enable(ep, src);
if (result < 0)
return result;
ep->driver_data = ss;
result = source_sink_start_ep(ss, true);
if (result < 0) {
fail:
ep = ss->in_ep;
usb_ep_disable(ep);
ep->driver_data = NULL;
return result;
}
/* one endpoint reads (sinks) anything OUT (from the host) */
ep = ss->out_ep;
result = usb_ep_enable(ep, sink);
if (result < 0)
goto fail;
ep->driver_data = ss;
result = source_sink_start_ep(ss, false);
if (result < 0) {
usb_ep_disable(ep);
ep->driver_data = NULL;
goto fail;
}
DBG(cdev, "%s enabled\n", ss->function.name);
return result;
}
static int sourcesink_set_alt(struct usb_function *f,
unsigned intf, unsigned alt)
{
struct f_sourcesink *ss = func_to_ss(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* we know alt is zero */
if (ss->in_ep->driver_data)
disable_source_sink(ss);
return enable_source_sink(cdev, ss);
}
static void sourcesink_disable(struct usb_function *f)
{
struct f_sourcesink *ss = func_to_ss(f);
disable_source_sink(ss);
}
static void sourcesink_suspend(struct usb_function *f)
{
struct f_sourcesink *ss = func_to_ss(f);
struct usb_composite_dev *cdev = f->config->cdev;
if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
return;
if (autoresume) {
mod_timer(&ss->resume, jiffies + (HZ * autoresume));
DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
} else
DBG(cdev, "%s\n", __func__);
}
static void sourcesink_resume(struct usb_function *f)
{
struct f_sourcesink *ss = func_to_ss(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "%s\n", __func__);
del_timer(&ss->resume);
}
/*-------------------------------------------------------------------------*/
static int __init sourcesink_bind_config(struct usb_configuration *c)
{
struct f_sourcesink *ss;
int status;
ss = kzalloc(sizeof *ss, GFP_KERNEL);
if (!ss)
return -ENOMEM;
ss->function.name = "source/sink";
ss->function.descriptors = fs_source_sink_descs;
ss->function.bind = sourcesink_bind;
ss->function.unbind = sourcesink_unbind;
ss->function.set_alt = sourcesink_set_alt;
ss->function.disable = sourcesink_disable;
ss->function.suspend = sourcesink_suspend;
ss->function.resume = sourcesink_resume;
status = usb_add_function(c, &ss->function);
if (status)
kfree(ss);
return status;
}
static int sourcesink_setup(struct usb_configuration *c,
const struct usb_ctrlrequest *ctrl)
{
struct usb_request *req = c->cdev->req;
int value = -EOPNOTSUPP;
u16 w_index = le16_to_cpu(ctrl->wIndex);
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
/* composite driver infrastructure handles everything except
* the two control test requests.
*/
switch (ctrl->bRequest) {
/*
* These are the same vendor-specific requests supported by
* Intel's USB 2.0 compliance test devices. We exceed that
* device spec by allowing multiple-packet requests.
*
* NOTE: the Control-OUT data stays in req->buf ... better
* would be copying it into a scratch buffer, so that other
* requests may safely intervene.
*/
case 0x5b: /* control WRITE test -- fill the buffer */
if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
goto unknown;
if (w_value || w_index)
break;
/* just read that many bytes into the buffer */
if (w_length > req->length)
break;
value = w_length;
break;
case 0x5c: /* control READ test -- return the buffer */
if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
goto unknown;
if (w_value || w_index)
break;
/* expect those bytes are still in the buffer; send back */
if (w_length > req->length)
break;
value = w_length;
break;
default:
unknown:
VDBG(c->cdev,
"unknown control req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
}
/* respond with data transfer or status phase? */
if (value >= 0) {
VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
ctrl->bRequestType, ctrl->bRequest,
w_value, w_index, w_length);
req->zero = 0;
req->length = value;
value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
if (value < 0)
ERROR(c->cdev, "source/sinkc response, err %d\n",
value);
}
/* device either stalls (value < 0) or reports success */
return value;
}
static struct usb_configuration sourcesink_driver = {
.label = "source/sink",
.strings = sourcesink_strings,
.bind = sourcesink_bind_config,
.setup = sourcesink_setup,
.bConfigurationValue = 3,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
.bMaxPower = 1, /* 2 mA, minimal */
/* .iConfiguration = DYNAMIC */
};
/**
* sourcesink_add - add a source/sink testing configuration to a device
* @cdev: the device to support the configuration
*/
int __init sourcesink_add(struct usb_composite_dev *cdev)
{
int id;
/* allocate string ID(s) */
id = usb_string_id(cdev);
if (id < 0)
return id;
strings_sourcesink[0].id = id;
source_sink_intf.iInterface = id;
sourcesink_driver.iConfiguration = id;
/* support autoresume for remote wakeup testing */
if (autoresume)
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
/* support OTG systems */
if (gadget_is_otg(cdev->gadget)) {
sourcesink_driver.descriptors = otg_desc;
sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
}
return usb_add_config(cdev, &sourcesink_driver);
}

View File

@ -0,0 +1,423 @@
/*
* f_subset.c -- "CDC Subset" Ethernet link function driver
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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 <linux/kernel.h>
#include <linux/device.h>
#include <linux/etherdevice.h>
#include "u_ether.h"
/*
* This function packages a simple "CDC Subset" Ethernet port with no real
* control mechanisms; just raw data transfer over two bulk endpoints.
* The data transfer model is exactly that of CDC Ethernet, which is
* why we call it the "CDC Subset".
*
* Because it's not standardized, this has some interoperability issues.
* They mostly relate to driver binding, since the data transfer model is
* so simple (CDC Ethernet). The original versions of this protocol used
* specific product/vendor IDs: byteswapped IDs for Digital Equipment's
* SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
* daughtercards with USB peripheral connectors. (It was used more often
* with other boards, using the Itsy identifiers.) Linux hosts recognized
* this with CONFIG_USB_ARMLINUX; these devices have only one configuration
* and one interface.
*
* At some point, MCCI defined a (nonconformant) CDC MDLM variant called
* "SAFE", which happens to have a mode which is identical to the "CDC
* Subset" in terms of data transfer and lack of control model. This was
* adopted by later Sharp Zaurus models, and by some other software which
* Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
*
* Because Microsoft's RNDIS drivers are far from robust, we added a few
* descriptors to the CDC Subset code, making this code look like a SAFE
* implementation. This lets you use MCCI's host side MS-Windows drivers
* if you get fed up with RNDIS. It also makes it easier for composite
* drivers to work, since they can use class based binding instead of
* caring about specific product and vendor IDs.
*/
struct geth_descs {
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
};
struct f_gether {
struct gether port;
char ethaddr[14];
struct usb_descriptor_header **fs_function;
struct geth_descs fs;
struct usb_descriptor_header **hs_function;
struct geth_descs hs;
};
static inline struct f_gether *func_to_geth(struct usb_function *f)
{
return container_of(f, struct f_gether, port.func);
}
/*-------------------------------------------------------------------------*/
/*
* "Simple" CDC-subset option is a simple vendor-neutral model that most
* full speed controllers can handle: one interface, two bulk endpoints.
* To assist host side drivers, we fancy it up a bit, and add descriptors so
* some host side drivers will understand it as a "SAFE" variant.
*
* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
* Data endpoints live in the control interface, there's no data interface.
* And it's not used to talk to a cell phone radio.
*/
/* interface descriptor: */
static struct usb_interface_descriptor subset_data_intf __initdata = {
.bLength = sizeof subset_data_intf,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
.bAlternateSetting = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_COMM,
.bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
.bInterfaceProtocol = 0,
/* .iInterface = DYNAMIC */
};
static struct usb_cdc_header_desc header_desc __initdata = {
.bLength = sizeof header_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_HEADER_TYPE,
.bcdCDC = __constant_cpu_to_le16(0x0110),
};
static struct usb_cdc_mdlm_desc mdlm_desc __initdata = {
.bLength = sizeof mdlm_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_MDLM_TYPE,
.bcdVersion = __constant_cpu_to_le16(0x0100),
.bGUID = {
0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
},
};
/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
* can't really use its struct. All we do here is say that we're using
* the submode of "SAFE" which directly matches the CDC Subset.
*/
static u8 mdlm_detail_desc[] __initdata = {
6,
USB_DT_CS_INTERFACE,
USB_CDC_MDLM_DETAIL_TYPE,
0, /* "SAFE" */
0, /* network control capabilities (none) */
0, /* network data capabilities ("raw" encapsulation) */
};
static struct usb_cdc_ether_desc ether_desc __initdata = {
.bLength = sizeof ether_desc,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
/* this descriptor actually adds value, surprise! */
/* .iMACAddress = DYNAMIC */
.bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
.wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
.wNumberMCFilters = __constant_cpu_to_le16(0),
.bNumberPowerFilters = 0,
};
/* full speed support: */
static struct usb_endpoint_descriptor fs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_IN,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_endpoint_descriptor fs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bEndpointAddress = USB_DIR_OUT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
};
static struct usb_descriptor_header *fs_eth_function[] __initdata = {
(struct usb_descriptor_header *) &subset_data_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &mdlm_desc,
(struct usb_descriptor_header *) &mdlm_detail_desc,
(struct usb_descriptor_header *) &ether_desc,
(struct usb_descriptor_header *) &fs_in_desc,
(struct usb_descriptor_header *) &fs_out_desc,
NULL,
};
/* high speed support: */
static struct usb_endpoint_descriptor hs_in_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_endpoint_descriptor hs_out_desc __initdata = {
.bLength = USB_DT_ENDPOINT_SIZE,
.bDescriptorType = USB_DT_ENDPOINT,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = __constant_cpu_to_le16(512),
};
static struct usb_descriptor_header *hs_eth_function[] __initdata = {
(struct usb_descriptor_header *) &subset_data_intf,
(struct usb_descriptor_header *) &header_desc,
(struct usb_descriptor_header *) &mdlm_desc,
(struct usb_descriptor_header *) &mdlm_detail_desc,
(struct usb_descriptor_header *) &ether_desc,
(struct usb_descriptor_header *) &hs_in_desc,
(struct usb_descriptor_header *) &hs_out_desc,
NULL,
};
/* string descriptors: */
static struct usb_string geth_string_defs[] = {
[0].s = "CDC Ethernet Subset/SAFE",
[1].s = NULL /* DYNAMIC */,
{ } /* end of list */
};
static struct usb_gadget_strings geth_string_table = {
.language = 0x0409, /* en-us */
.strings = geth_string_defs,
};
static struct usb_gadget_strings *geth_strings[] = {
&geth_string_table,
NULL,
};
/*-------------------------------------------------------------------------*/
static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_gether *geth = func_to_geth(f);
struct usb_composite_dev *cdev = f->config->cdev;
struct net_device *net;
/* we know alt == 0, so this is an activation or a reset */
if (geth->port.in_ep->driver_data) {
DBG(cdev, "reset cdc subset\n");
gether_disconnect(&geth->port);
}
DBG(cdev, "init + activate cdc subset\n");
geth->port.in = ep_choose(cdev->gadget,
geth->hs.in, geth->fs.in);
geth->port.out = ep_choose(cdev->gadget,
geth->hs.out, geth->fs.out);
net = gether_connect(&geth->port);
return IS_ERR(net) ? PTR_ERR(net) : 0;
}
static void geth_disable(struct usb_function *f)
{
struct f_gether *geth = func_to_geth(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "net deactivated\n");
gether_disconnect(&geth->port);
}
/*-------------------------------------------------------------------------*/
/* serial function driver setup/binding */
static int __init
geth_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_gether *geth = func_to_geth(f);
int status;
struct usb_ep *ep;
/* allocate instance-specific interface IDs */
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
subset_data_intf.bInterfaceNumber = status;
status = -ENODEV;
/* allocate instance-specific endpoints */
ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
if (!ep)
goto fail;
geth->port.in_ep = ep;
ep->driver_data = cdev; /* claim */
ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
if (!ep)
goto fail;
geth->port.out_ep = ep;
ep->driver_data = cdev; /* claim */
/* copy descriptors, and track endpoint copies */
f->descriptors = usb_copy_descriptors(fs_eth_function);
geth->fs.in = usb_find_endpoint(fs_eth_function,
f->descriptors, &fs_in_desc);
geth->fs.out = usb_find_endpoint(fs_eth_function,
f->descriptors, &fs_out_desc);
/* support all relevant hardware speeds... we expect that when
* hardware is dual speed, all bulk-capable endpoints work at
* both speeds
*/
if (gadget_is_dualspeed(c->cdev->gadget)) {
hs_in_desc.bEndpointAddress =
fs_in_desc.bEndpointAddress;
hs_out_desc.bEndpointAddress =
fs_out_desc.bEndpointAddress;
/* copy descriptors, and track endpoint copies */
f->hs_descriptors = usb_copy_descriptors(hs_eth_function);
geth->hs.in = usb_find_endpoint(hs_eth_function,
f->hs_descriptors, &hs_in_desc);
geth->hs.out = usb_find_endpoint(hs_eth_function,
f->hs_descriptors, &hs_out_desc);
}
/* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
* until we're activated via set_alt().
*/
DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
geth->port.in_ep->name, geth->port.out_ep->name);
return 0;
fail:
/* we might as well release our claims on endpoints */
if (geth->port.out)
geth->port.out_ep->driver_data = NULL;
if (geth->port.in)
geth->port.in_ep->driver_data = NULL;
ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
return status;
}
static void
geth_unbind(struct usb_configuration *c, struct usb_function *f)
{
if (gadget_is_dualspeed(c->cdev->gadget))
usb_free_descriptors(f->hs_descriptors);
usb_free_descriptors(f->descriptors);
geth_string_defs[1].s = NULL;
kfree(func_to_geth(f));
}
/**
* geth_bind_config - add CDC Subset network link to a configuration
* @c: the configuration to support the network link
* @ethaddr: a buffer in which the ethernet address of the host side
* side of the link was recorded
* Context: single threaded during gadget setup
*
* Returns zero on success, else negative errno.
*
* Caller must have called @gether_setup(). Caller is also responsible
* for calling @gether_cleanup() before module unload.
*/
int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
struct f_gether *geth;
int status;
if (!ethaddr)
return -EINVAL;
/* maybe allocate device-global string IDs */
if (geth_string_defs[0].id == 0) {
/* interface label */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
geth_string_defs[0].id = status;
subset_data_intf.iInterface = status;
/* MAC address */
status = usb_string_id(c->cdev);
if (status < 0)
return status;
geth_string_defs[1].id = status;
ether_desc.iMACAddress = status;
}
/* allocate and initialize one new instance */
geth = kzalloc(sizeof *geth, GFP_KERNEL);
if (!geth)
return -ENOMEM;
/* export host's Ethernet address in CDC format */
snprintf(geth->ethaddr, sizeof geth->ethaddr,
"%02X%02X%02X%02X%02X%02X",
ethaddr[0], ethaddr[1], ethaddr[2],
ethaddr[3], ethaddr[4], ethaddr[5]);
geth_string_defs[1].s = geth->ethaddr;
geth->port.cdc_filter = DEFAULT_FILTER;
geth->port.func.name = "cdc_subset";
geth->port.func.strings = geth_strings;
geth->port.func.bind = geth_bind;
geth->port.func.unbind = geth_unbind;
geth->port.func.set_alt = geth_set_alt;
geth->port.func.disable = geth_disable;
status = usb_add_function(c, &geth->port.func);
if (status) {
geth_string_defs[1].s = NULL;
kfree(geth);
}
return status;
}

View File

@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
dev_set_drvdata(&curlun->dev, fsg);
snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
"%s-lun%d", gadget->dev.bus_id, i);
dev_set_name(&curlun->dev,"%s-lun%d",
dev_name(&gadget->dev), i);
if ((rc = device_register(&curlun->dev)) != 0) {
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);

View File

@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
udc_controller->gadget.name = driver_name;
/* Setup gadget.dev and register with kernel */
strcpy(udc_controller->gadget.dev.bus_id, "gadget");
dev_set_name(&udc_controller->gadget.dev, "gadget");
udc_controller->gadget.dev.release = fsl_udc_release;
udc_controller->gadget.dev.parent = &pdev->dev;
ret = device_register(&udc_controller->gadget.dev);

View File

@ -0,0 +1,25 @@
/*
* This header declares the utility functions used by "Gadget Zero", plus
* interfaces to its two single-configuration function drivers.
*/
#ifndef __G_ZERO_H
#define __G_ZERO_H
#include <linux/usb/composite.h>
/* global state */
extern unsigned buflen;
extern const struct usb_descriptor_header *otg_desc[];
/* common utilities */
struct usb_request *alloc_ep_req(struct usb_ep *ep);
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out);
/* configuration-specific linkup */
int sourcesink_add(struct usb_composite_dev *cdev);
int loopback_add(struct usb_composite_dev *cdev);
#endif /* __G_ZERO_H */

View File

@ -214,3 +214,26 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x21;
return -ENOENT;
}
/**
* gadget_supports_altsettings - return true if altsettings work
* @gadget: the gadget in question
*/
static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
{
/* PXA 21x/25x/26x has no altsettings at all */
if (gadget_is_pxa(gadget))
return false;
/* PXA 27x and 3xx have *broken* altsetting support */
if (gadget_is_pxa27x(gadget))
return false;
/* SH3 hardware just doesn't do altsettings */
if (gadget_is_sh(gadget))
return false;
/* Everything else is *presumably* fine ... */
return true;
}

View File

@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.ops = &goku_ops;
/* the "gadget" abstracts/virtualizes the controller */
strcpy(dev->gadget.dev.bus_id, "gadget");
dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
dev->gadget.dev.release = gadget_release;

View File

@ -32,6 +32,7 @@
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@ -483,8 +484,7 @@ ep_release (struct inode *inode, struct file *fd)
return 0;
}
static int ep_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
{
struct ep_data *data = fd->private_data;
int status;
@ -740,7 +740,7 @@ static const struct file_operations ep_io_operations = {
.read = ep_read,
.write = ep_write,
.ioctl = ep_ioctl,
.unlocked_ioctl = ep_ioctl,
.release = ep_release,
.aio_read = ep_aio_read,
@ -1294,15 +1294,18 @@ out:
return mask;
}
static int dev_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
{
struct dev_data *dev = fd->private_data;
struct usb_gadget *gadget = dev->gadget;
long ret = -ENOTTY;
if (gadget->ops->ioctl)
return gadget->ops->ioctl (gadget, code, value);
return -ENOTTY;
if (gadget->ops->ioctl) {
lock_kernel();
ret = gadget->ops->ioctl (gadget, code, value);
unlock_kernel();
}
return ret;
}
/* used after device configuration */
@ -1314,7 +1317,7 @@ static const struct file_operations ep0_io_operations = {
.write = ep0_write,
.fasync = ep0_fasync,
.poll = ep0_poll,
.ioctl = dev_ioctl,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};
@ -1964,7 +1967,7 @@ static const struct file_operations dev_init_operations = {
.open = dev_open,
.write = dev_config,
.fasync = ep0_fasync,
.ioctl = dev_ioctl,
.unlocked_ioctl = dev_ioctl,
.release = dev_release,
};

View File

@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = {
static void nop_release(struct device *dev)
{
DEBUG("%s %s\n", __func__, dev->bus_id);
DEBUG("%s %s\n", __func__, dev_name(dev));
}
static struct lh7a40x_udc memory = {

View File

@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev)
m66592->gadget.ops = &m66592_gadget_ops;
device_initialize(&m66592->gadget.dev);
strcpy(m66592->gadget.dev.bus_id, "gadget");
dev_set_name(&m66592->gadget, "gadget");
m66592->gadget.is_dualspeed = 1;
m66592->gadget.dev.parent = &pdev->dev;
m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;

View File

@ -1,11 +1,11 @@
/*
* ndis.h
*
* ndis.h
*
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may

View File

@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
dev->gadget.is_dualspeed = 1;
/* the "gadget" abstracts/virtualizes the controller */
strcpy (dev->gadget.dev.bus_id, "gadget");
dev_set_name(&dev->gadget.dev, "gadget");
dev->gadget.dev.parent = &pdev->dev;
dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
dev->gadget.dev.release = gadget_release;

View File

@ -2686,7 +2686,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
udc->gadget.name = driver_name;
device_initialize(&udc->gadget.dev);
strcpy (udc->gadget.dev.bus_id, "gadget");
dev_set_name(&udc->gadget.dev, "gadget");
udc->gadget.dev.release = omap_udc_release;
udc->gadget.dev.parent = &odev->dev;
if (use_dma)

View File

@ -828,9 +828,8 @@ printer_poll(struct file *fd, poll_table *wait)
return status;
}
static int
printer_ioctl(struct inode *inode, struct file *fd, unsigned int code,
unsigned long arg)
static long
printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
{
struct printer_dev *dev = fd->private_data;
unsigned long flags;
@ -869,7 +868,7 @@ static struct file_operations printer_io_operations = {
.write = printer_write,
.fsync = printer_fsync,
.poll = printer_poll,
.ioctl = printer_ioctl,
.unlocked_ioctl = printer_ioctl,
.release = printer_close
};

View File

@ -1818,7 +1818,7 @@ pxa25x_udc_irq(int irq, void *_dev)
static void nop_release (struct device *dev)
{
DMSG("%s %s\n", __func__, dev->bus_id);
DMSG("%s %s\n", __func__, dev_name(dev));
}
/* this uses load-time allocation and initialization (instead of

View File

@ -1575,7 +1575,6 @@ static void udc_enable(struct pxa_udc *udc)
{
udc_writel(udc, UDCICR0, 0);
udc_writel(udc, UDCICR1, 0);
udc_writel(udc, UP2OCR, UP2OCR_HXOE);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
clk_enable(udc->clk);

View File

@ -1,8 +1,6 @@
/*
* RNDIS MSG parser
*
* Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
@ -30,6 +28,7 @@
#include <linux/init.h>
#include <linux/list.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/netdevice.h>
#include <asm/io.h>
@ -38,9 +37,7 @@
#include <asm/unaligned.h>
#undef RNDIS_PM
#undef RNDIS_WAKEUP
#undef VERBOSE
#undef VERBOSE_DEBUG
#include "rndis.h"
@ -96,9 +93,6 @@ static const u32 oid_supported_list [] =
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_PHYSICAL_MEDIUM,
#if 0
OID_GEN_RNDIS_CONFIG_PARAMETER,
#endif
/* the statistical stuff */
OID_GEN_XMIT_OK,
@ -146,7 +140,14 @@ static const u32 oid_supported_list [] =
#endif /* RNDIS_OPTIONAL_STATS */
#ifdef RNDIS_PM
/* PM and wakeup are mandatory for USB: */
/* PM and wakeup are "mandatory" for USB, but the RNDIS specs
* don't say what they mean ... and the NDIS specs are often
* confusing and/or ambiguous in this context. (That is, more
* so than their specs for the other OIDs.)
*
* FIXME someone who knows what these should do, please
* implement them!
*/
/* power management */
OID_PNP_CAPABILITIES,
@ -173,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
__le32 *outbuf;
int i, count;
rndis_query_cmplt_type *resp;
struct net_device *net;
struct net_device_stats *stats;
if (!r) return -ENOMEM;
resp = (rndis_query_cmplt_type *) r->buf;
@ -194,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
outbuf = (__le32 *) &resp[1];
resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
net = rndis_per_dev_params[configNr].dev;
if (net->get_stats)
stats = net->get_stats(net);
else
stats = NULL;
switch (OID) {
/* general oids (table 4-1) */
@ -350,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
case OID_GEN_XMIT_OK:
if (rndis_debug > 1)
DBG("%s: OID_GEN_XMIT_OK\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
rndis_per_dev_params [configNr].stats->tx_packets -
rndis_per_dev_params [configNr].stats->tx_errors -
rndis_per_dev_params [configNr].stats->tx_dropped);
if (stats) {
*outbuf = cpu_to_le32(stats->tx_packets
- stats->tx_errors - stats->tx_dropped);
retval = 0;
}
break;
@ -363,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
case OID_GEN_RCV_OK:
if (rndis_debug > 1)
DBG("%s: OID_GEN_RCV_OK\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
rndis_per_dev_params [configNr].stats->rx_packets -
rndis_per_dev_params [configNr].stats->rx_errors -
rndis_per_dev_params [configNr].stats->rx_dropped);
if (stats) {
*outbuf = cpu_to_le32(stats->rx_packets
- stats->rx_errors - stats->rx_dropped);
retval = 0;
}
break;
@ -376,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
case OID_GEN_XMIT_ERROR:
if (rndis_debug > 1)
DBG("%s: OID_GEN_XMIT_ERROR\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->tx_errors);
if (stats) {
*outbuf = cpu_to_le32(stats->tx_errors);
retval = 0;
}
break;
@ -387,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
case OID_GEN_RCV_ERROR:
if (rndis_debug > 1)
DBG("%s: OID_GEN_RCV_ERROR\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_errors);
if (stats) {
*outbuf = cpu_to_le32(stats->rx_errors);
retval = 0;
}
break;
@ -397,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
/* mandatory */
case OID_GEN_RCV_NO_BUFFER:
DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_dropped);
if (stats) {
*outbuf = cpu_to_le32(stats->rx_dropped);
retval = 0;
}
break;
#ifdef RNDIS_OPTIONAL_STATS
case OID_GEN_DIRECTED_BYTES_XMIT:
DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__);
/*
* Aunt Tilly's size of shoes
* minus antarctica count of penguins
* divided by weight of Alpha Centauri
*/
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
(rndis_per_dev_params [configNr]
.stats->tx_packets -
rndis_per_dev_params [configNr]
.stats->tx_errors -
rndis_per_dev_params [configNr]
.stats->tx_dropped)
* 123);
retval = 0;
}
break;
case OID_GEN_DIRECTED_FRAMES_XMIT:
DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__);
/* dito */
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (
(rndis_per_dev_params [configNr]
.stats->tx_packets -
rndis_per_dev_params [configNr]
.stats->tx_errors -
rndis_per_dev_params [configNr]
.stats->tx_dropped)
/ 123);
retval = 0;
}
break;
case OID_GEN_MULTICAST_BYTES_XMIT:
DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->multicast*1234);
retval = 0;
}
break;
case OID_GEN_MULTICAST_FRAMES_XMIT:
DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->multicast);
retval = 0;
}
break;
case OID_GEN_BROADCAST_BYTES_XMIT:
DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->tx_packets/42*255);
retval = 0;
}
break;
case OID_GEN_BROADCAST_FRAMES_XMIT:
DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->tx_packets/42);
retval = 0;
}
break;
case OID_GEN_DIRECTED_BYTES_RCV:
DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
case OID_GEN_DIRECTED_FRAMES_RCV:
DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
case OID_GEN_MULTICAST_BYTES_RCV:
DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->multicast * 1111);
retval = 0;
}
break;
case OID_GEN_MULTICAST_FRAMES_RCV:
DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->multicast);
retval = 0;
}
break;
case OID_GEN_BROADCAST_BYTES_RCV:
DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_packets/42*255);
retval = 0;
}
break;
case OID_GEN_BROADCAST_FRAMES_RCV:
DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_packets/42);
retval = 0;
}
break;
case OID_GEN_RCV_CRC_ERROR:
DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_crc_errors);
retval = 0;
}
break;
case OID_GEN_TRANSMIT_QUEUE_LENGTH:
DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__);
*outbuf = __constant_cpu_to_le32 (0);
retval = 0;
break;
#endif /* RNDIS_OPTIONAL_STATS */
/* ieee802.3 OIDs (table 4-3) */
/* mandatory */
@ -592,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
/* mandatory */
case OID_802_3_RCV_ERROR_ALIGNMENT:
DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
if (rndis_per_dev_params [configNr].stats) {
*outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
.stats->rx_frame_errors);
if (stats) {
*outbuf = cpu_to_le32(stats->rx_frame_errors);
retval = 0;
}
break;
@ -613,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
retval = 0;
break;
#ifdef RNDIS_OPTIONAL_STATS
case OID_802_3_XMIT_DEFERRED:
DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__);
/* TODO */
break;
case OID_802_3_XMIT_MAX_COLLISIONS:
DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__);
/* TODO */
break;
case OID_802_3_RCV_OVERRUN:
DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__);
/* TODO */
break;
case OID_802_3_XMIT_UNDERRUN:
DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__);
/* TODO */
break;
case OID_802_3_XMIT_HEARTBEAT_FAILURE:
DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__);
/* TODO */
break;
case OID_802_3_XMIT_TIMES_CRS_LOST:
DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__);
/* TODO */
break;
case OID_802_3_XMIT_LATE_COLLISIONS:
DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__);
/* TODO */
break;
#endif /* RNDIS_OPTIONAL_STATS */
#ifdef RNDIS_PM
/* power management OIDs (table 4-5) */
case OID_PNP_CAPABILITIES:
DBG("%s: OID_PNP_CAPABILITIES\n", __func__);
/* for now, no wakeup capabilities */
length = sizeof (struct NDIS_PNP_CAPABILITIES);
memset(outbuf, 0, length);
retval = 0;
break;
case OID_PNP_QUERY_POWER:
DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__,
get_unaligned_le32(buf) - 1);
/* only suspend is a real power state, and
* it can't be entered by OID_PNP_SET_POWER...
*/
length = 0;
retval = 0;
break;
#endif
default:
pr_warning("%s: query unknown OID 0x%08X\n",
__func__, OID);
@ -726,9 +532,6 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
* what makes the packet flow start and stop, like
* activating the CDC Ethernet altsetting.
*/
#ifdef RNDIS_PM
update_linkstate:
#endif
retval = 0;
if (*params->filter) {
params->state = RNDIS_DATA_INITIALIZED;
@ -747,49 +550,6 @@ update_linkstate:
DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
retval = 0;
break;
#if 0
case OID_GEN_RNDIS_CONFIG_PARAMETER:
{
struct rndis_config_parameter *param;
param = (struct rndis_config_parameter *) buf;
DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
__func__,
min(cpu_to_le32(param->ParameterNameLength),80),
buf + param->ParameterNameOffset);
retval = 0;
}
break;
#endif
#ifdef RNDIS_PM
case OID_PNP_SET_POWER:
/* The only real power state is USB suspend, and RNDIS requests
* can't enter it; this one isn't really about power. After
* resuming, Windows forces a reset, and then SET_POWER D0.
* FIXME ... then things go batty; Windows wedges itself.
*/
i = get_unaligned_le32(buf);
DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1);
switch (i) {
case NdisDeviceStateD0:
*params->filter = params->saved_filter;
goto update_linkstate;
case NdisDeviceStateD3:
case NdisDeviceStateD2:
case NdisDeviceStateD1:
params->saved_filter = *params->filter;
retval = 0;
break;
}
break;
#ifdef RNDIS_WAKEUP
// no wakeup support advertised, so wakeup OIDs always fail:
// - OID_PNP_ENABLE_WAKE_UP
// - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
#endif
#endif /* RNDIS_PM */
default:
pr_warning("%s: set unknown OID 0x%08X, size %d\n",
@ -807,8 +567,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
{
rndis_init_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
if (!params->dev)
return -ENOTSUPP;
r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
if (!r)
@ -826,7 +588,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
resp->MaxTransferSize = cpu_to_le32 (
rndis_per_dev_params [configNr].dev->mtu
params->dev->mtu
+ sizeof (struct ethhdr)
+ sizeof (struct rndis_packet_msg_type)
+ 22);
@ -834,10 +596,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
resp->AFListOffset = __constant_cpu_to_le32 (0);
resp->AFListSize = __constant_cpu_to_le32 (0);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -845,9 +604,11 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
{
rndis_query_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
// DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID));
if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
if (!params->dev)
return -ENOTSUPP;
/*
* we need more memory:
@ -878,9 +639,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
} else
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -889,6 +648,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
u32 BufLength, BufOffset;
rndis_set_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
if (!r)
@ -898,7 +658,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
BufLength = le32_to_cpu (buf->InformationBufferLength);
BufOffset = le32_to_cpu (buf->InformationBufferOffset);
#ifdef VERBOSE
#ifdef VERBOSE_DEBUG
DBG("%s: Length: %d\n", __func__, BufLength);
DBG("%s: Offset: %d\n", __func__, BufOffset);
DBG("%s: InfoBuffer: ", __func__);
@ -919,10 +679,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
else
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -930,6 +687,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
{
rndis_reset_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
if (!r)
@ -942,10 +700,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
/* resent information */
resp->AddressingReset = __constant_cpu_to_le32 (1);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -954,6 +709,7 @@ static int rndis_keepalive_response (int configNr,
{
rndis_keepalive_cmplt_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
/* host "should" check only in RNDIS_DATA_INITIALIZED state */
@ -968,10 +724,7 @@ static int rndis_keepalive_response (int configNr,
resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -983,8 +736,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
{
rndis_indicate_status_msg_type *resp;
rndis_resp_t *r;
struct rndis_params *params = rndis_per_dev_params + configNr;
if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
if (params->state == RNDIS_UNINITIALIZED)
return -ENOTSUPP;
r = rndis_add_response (configNr,
@ -1000,9 +754,7 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
resp->StatusBufferLength = __constant_cpu_to_le32 (0);
resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
if (rndis_per_dev_params [configNr].ack)
rndis_per_dev_params [configNr].ack (
rndis_per_dev_params [configNr].dev);
params->resp_avail(params->v);
return 0;
}
@ -1029,7 +781,6 @@ void rndis_uninit (int configNr)
if (configNr >= RNDIS_MAX_CONFIGS)
return;
rndis_per_dev_params [configNr].used = 0;
rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
/* drain the response queue */
@ -1142,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
return -ENOTSUPP;
}
int rndis_register (int (* rndis_control_ack) (struct net_device *))
int rndis_register(void (*resp_avail)(void *v), void *v)
{
u8 i;
if (!resp_avail)
return -EINVAL;
for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
if (!rndis_per_dev_params [i].used) {
rndis_per_dev_params [i].used = 1;
rndis_per_dev_params [i].ack = rndis_control_ack;
rndis_per_dev_params [i].resp_avail = resp_avail;
rndis_per_dev_params [i].v = v;
DBG("%s: configNr = %d\n", __func__, i);
return i;
}
}
DBG("failed\n");
return -1;
return -ENODEV;
}
void rndis_deregister (int configNr)
@ -1169,16 +924,14 @@ void rndis_deregister (int configNr)
return;
}
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
struct net_device_stats *stats,
u16 *cdc_filter)
int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
{
DBG("%s:\n", __func__ );
if (!dev || !stats) return -1;
if (!dev)
return -EINVAL;
if (configNr >= RNDIS_MAX_CONFIGS) return -1;
rndis_per_dev_params [configNr].dev = dev;
rndis_per_dev_params [configNr].stats = stats;
rndis_per_dev_params [configNr].filter = cdc_filter;
return 0;
@ -1296,14 +1049,11 @@ int rndis_rm_hdr(struct sk_buff *skb)
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
void *data)
static int rndis_proc_show(struct seq_file *m, void *v)
{
char *out = page;
int len;
rndis_params *param = (rndis_params *) data;
rndis_params *param = m->private;
out += snprintf (out, count,
seq_printf(m,
"Config Nr. %d\n"
"used : %s\n"
"state : %s\n"
@ -1326,25 +1076,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int
(param->media_state) ? 0 : param->speed*100,
(param->media_state) ? "disconnected" : "connected",
param->vendorID, param->vendorDescr);
len = out - page;
len -= off;
if (len < count) {
*eof = 1;
if (len <= 0)
return 0;
} else
len = count;
*start = page + off;
return len;
return 0;
}
static int rndis_proc_write (struct file *file, const char __user *buffer,
unsigned long count, void *data)
static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
size_t count, loff_t *ppos)
{
rndis_params *p = data;
rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
u32 speed = 0;
int i, fl_speed = 0;
@ -1386,6 +1124,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer,
return count;
}
static int rndis_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, rndis_proc_show, PDE(inode)->data);
}
static const struct file_operations rndis_proc_fops = {
.owner = THIS_MODULE,
.open = rndis_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = rndis_proc_write,
};
#define NAME_TEMPLATE "driver/rndis-%03d"
static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
@ -1403,7 +1155,9 @@ int __init rndis_init (void)
sprintf (name, NAME_TEMPLATE, i);
if (!(rndis_connect_state [i]
= create_proc_entry (name, 0660, NULL)))
= proc_create_data(name, 0660, NULL,
&rndis_proc_fops,
(void *)(rndis_per_dev_params + i))))
{
DBG("%s :remove entries", __func__);
while (i) {
@ -1413,11 +1167,6 @@ int __init rndis_init (void)
DBG("\n");
return -EIO;
}
rndis_connect_state [i]->write_proc = rndis_proc_write;
rndis_connect_state [i]->read_proc = rndis_proc_read;
rndis_connect_state [i]->data = (void *)
(rndis_per_dev_params + i);
#endif
rndis_per_dev_params [i].confignr = i;
rndis_per_dev_params [i].used = 0;

View File

@ -1,8 +1,6 @@
/*
* RNDIS Definitions for Remote NDIS
*
* Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
*
* Authors: Benedikt Spranger, Pengutronix
* Robert Schwebel, Pengutronix
*
@ -235,20 +233,19 @@ typedef struct rndis_params
const u8 *host_mac;
u16 *filter;
struct net_device *dev;
struct net_device_stats *stats;
u32 vendorID;
const char *vendorDescr;
int (*ack) (struct net_device *);
void (*resp_avail)(void *v);
void *v;
struct list_head resp_queue;
} rndis_params;
/* RNDIS Message parser and other useless functions */
int rndis_msg_parser (u8 configNr, u8 *buf);
int rndis_register (int (*rndis_control_ack) (struct net_device *));
int rndis_register(void (*resp_avail)(void *v), void *v);
void rndis_deregister (int configNr);
int rndis_set_param_dev (u8 configNr, struct net_device *dev,
struct net_device_stats *stats,
u16 *cdc_filter);
int rndis_set_param_vendor (u8 configNr, u32 vendorID,
const char *vendorDescr);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,967 @@
/*
* u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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
*/
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
#include <linux/utsname.h>
#include <linux/device.h>
#include <linux/ctype.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include "u_ether.h"
/*
* This component encapsulates the Ethernet link glue needed to provide
* one (!) network link through the USB gadget stack, normally "usb0".
*
* The control and data models are handled by the function driver which
* connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS.
* That includes all descriptor and endpoint management.
*
* Link level addressing is handled by this component using module
* parameters; if no such parameters are provided, random link level
* addresses are used. Each end of the link uses one address. The
* host end address is exported in various ways, and is often recorded
* in configuration databases.
*
* The driver which assembles each configuration using such a link is
* responsible for ensuring that each configuration includes at most one
* instance of is network link. (The network layer provides ways for
* this single "physical" link to be used by multiple virtual links.)
*/
#define DRIVER_VERSION "29-May-2008"
struct eth_dev {
/* lock is held while accessing port_usb
* or updating its backlink port_usb->ioport
*/
spinlock_t lock;
struct gether *port_usb;
struct net_device *net;
struct usb_gadget *gadget;
spinlock_t req_lock; /* guard {rx,tx}_reqs */
struct list_head tx_reqs, rx_reqs;
atomic_t tx_qlen;
unsigned header_len;
struct sk_buff *(*wrap)(struct sk_buff *skb);
int (*unwrap)(struct sk_buff *skb);
struct work_struct work;
unsigned long todo;
#define WORK_RX_MEMORY 0
bool zlp;
u8 host_mac[ETH_ALEN];
};
/*-------------------------------------------------------------------------*/
#define RX_EXTRA 20 /* bytes guarding against rx overflows */
#define DEFAULT_QLEN 2 /* double buffering by default */
#ifdef CONFIG_USB_GADGET_DUALSPEED
static unsigned qmult = 5;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qmult, "queue length multiplier at high speed");
#else /* full speed (low speed doesn't do bulk) */
#define qmult 1
#endif
/* for dual-speed hardware, use deeper queues at highspeed */
static inline int qlen(struct usb_gadget *gadget)
{
if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH)
return qmult * DEFAULT_QLEN;
else
return DEFAULT_QLEN;
}
/*-------------------------------------------------------------------------*/
/* REVISIT there must be a better way than having two sets
* of debug calls ...
*/
#undef DBG
#undef VDBG
#undef ERROR
#undef WARN
#undef INFO
#define xprintk(d, level, fmt, args...) \
printk(level "%s: " fmt , (d)->net->name , ## args)
#ifdef DEBUG
#undef DEBUG
#define DBG(dev, fmt, args...) \
xprintk(dev , KERN_DEBUG , fmt , ## args)
#else
#define DBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#ifdef VERBOSE_DEBUG
#define VDBG DBG
#else
#define VDBG(dev, fmt, args...) \
do { } while (0)
#endif /* DEBUG */
#define ERROR(dev, fmt, args...) \
xprintk(dev , KERN_ERR , fmt , ## args)
#define WARN(dev, fmt, args...) \
xprintk(dev , KERN_WARNING , fmt , ## args)
#define INFO(dev, fmt, args...) \
xprintk(dev , KERN_INFO , fmt , ## args)
/*-------------------------------------------------------------------------*/
/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
static int eth_change_mtu(struct net_device *net, int new_mtu)
{
struct eth_dev *dev = netdev_priv(net);
unsigned long flags;
int status = 0;
/* don't change MTU on "live" link (peer won't know) */
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb)
status = -EBUSY;
else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
status = -ERANGE;
else
net->mtu = new_mtu;
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
{
struct eth_dev *dev = netdev_priv(net);
strlcpy(p->driver, "g_ether", sizeof p->driver);
strlcpy(p->version, DRIVER_VERSION, sizeof p->version);
strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
}
static u32 eth_get_link(struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
return dev->gadget->speed != USB_SPEED_UNKNOWN;
}
/* REVISIT can also support:
* - WOL (by tracking suspends and issuing remote wakeup)
* - msglevel (implies updated messaging)
* - ... probably more ethtool ops
*/
static struct ethtool_ops ops = {
.get_drvinfo = eth_get_drvinfo,
.get_link = eth_get_link
};
static void defer_kevent(struct eth_dev *dev, int flag)
{
if (test_and_set_bit(flag, &dev->todo))
return;
if (!schedule_work(&dev->work))
ERROR(dev, "kevent %d may have been dropped\n", flag);
else
DBG(dev, "kevent %d scheduled\n", flag);
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req);
static int
rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
{
struct sk_buff *skb;
int retval = -ENOMEM;
size_t size = 0;
struct usb_ep *out;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb)
out = dev->port_usb->out_ep;
else
out = NULL;
spin_unlock_irqrestore(&dev->lock, flags);
if (!out)
return -ENOTCONN;
/* Padding up to RX_EXTRA handles minor disagreements with host.
* Normally we use the USB "terminate on short read" convention;
* so allow up to (N*maxpacket), since that memory is normally
* already allocated. Some hardware doesn't deal well with short
* reads (e.g. DMA must be N*maxpacket), so for now don't trim a
* byte off the end (to force hardware errors on overflow).
*
* RNDIS uses internal framing, and explicitly allows senders to
* pad to end-of-packet. That's potentially nice for speed, but
* means receivers can't recover lost synch on their own (because
* new packets don't only start after a short RX).
*/
size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
size += dev->port_usb->header_len;
size += out->maxpacket - 1;
size -= size % out->maxpacket;
skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
if (skb == NULL) {
DBG(dev, "no rx skb\n");
goto enomem;
}
/* Some platforms perform better when IP packets are aligned,
* but on at least one, checksumming fails otherwise. Note:
* RNDIS headers involve variable numbers of LE32 values.
*/
skb_reserve(skb, NET_IP_ALIGN);
req->buf = skb->data;
req->length = size;
req->complete = rx_complete;
req->context = skb;
retval = usb_ep_queue(out, req, gfp_flags);
if (retval == -ENOMEM)
enomem:
defer_kevent(dev, WORK_RX_MEMORY);
if (retval) {
DBG(dev, "rx submit --> %d\n", retval);
if (skb)
dev_kfree_skb_any(skb);
spin_lock_irqsave(&dev->req_lock, flags);
list_add(&req->list, &dev->rx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
return retval;
}
static void rx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct sk_buff *skb = req->context;
struct eth_dev *dev = ep->driver_data;
int status = req->status;
switch (status) {
/* normal completion */
case 0:
skb_put(skb, req->actual);
if (dev->unwrap)
status = dev->unwrap(skb);
if (status < 0
|| ETH_HLEN > skb->len
|| skb->len > ETH_FRAME_LEN) {
dev->net->stats.rx_errors++;
dev->net->stats.rx_length_errors++;
DBG(dev, "rx length %d\n", skb->len);
break;
}
skb->protocol = eth_type_trans(skb, dev->net);
dev->net->stats.rx_packets++;
dev->net->stats.rx_bytes += skb->len;
/* no buffer copies needed, unless hardware can't
* use skb buffers.
*/
status = netif_rx(skb);
skb = NULL;
break;
/* software-driven interface shutdown */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
VDBG(dev, "rx shutdown, code %d\n", status);
goto quiesce;
/* for hardware automagic (such as pxa) */
case -ECONNABORTED: /* endpoint reset */
DBG(dev, "rx %s reset\n", ep->name);
defer_kevent(dev, WORK_RX_MEMORY);
quiesce:
dev_kfree_skb_any(skb);
goto clean;
/* data overrun */
case -EOVERFLOW:
dev->net->stats.rx_over_errors++;
/* FALLTHROUGH */
default:
dev->net->stats.rx_errors++;
DBG(dev, "rx status %d\n", status);
break;
}
if (skb)
dev_kfree_skb_any(skb);
if (!netif_running(dev->net)) {
clean:
spin_lock(&dev->req_lock);
list_add(&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock);
req = NULL;
}
if (req)
rx_submit(dev, req, GFP_ATOMIC);
}
static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
{
unsigned i;
struct usb_request *req;
if (!n)
return -ENOMEM;
/* queue/recycle up to N requests */
i = n;
list_for_each_entry(req, list, list) {
if (i-- == 0)
goto extra;
}
while (i--) {
req = usb_ep_alloc_request(ep, GFP_ATOMIC);
if (!req)
return list_empty(list) ? -ENOMEM : 0;
list_add(&req->list, list);
}
return 0;
extra:
/* free extras */
for (;;) {
struct list_head *next;
next = req->list.next;
list_del(&req->list);
usb_ep_free_request(ep, req);
if (next == list)
break;
req = container_of(next, struct usb_request, list);
}
return 0;
}
static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
{
int status;
spin_lock(&dev->req_lock);
status = prealloc(&dev->tx_reqs, link->in_ep, n);
if (status < 0)
goto fail;
status = prealloc(&dev->rx_reqs, link->out_ep, n);
if (status < 0)
goto fail;
goto done;
fail:
DBG(dev, "can't alloc requests\n");
done:
spin_unlock(&dev->req_lock);
return status;
}
static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
{
struct usb_request *req;
unsigned long flags;
/* fill unused rxq slots with some skb */
spin_lock_irqsave(&dev->req_lock, flags);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del_init(&req->list);
spin_unlock_irqrestore(&dev->req_lock, flags);
if (rx_submit(dev, req, gfp_flags) < 0) {
defer_kevent(dev, WORK_RX_MEMORY);
return;
}
spin_lock_irqsave(&dev->req_lock, flags);
}
spin_unlock_irqrestore(&dev->req_lock, flags);
}
static void eth_work(struct work_struct *work)
{
struct eth_dev *dev = container_of(work, struct eth_dev, work);
if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
if (netif_running(dev->net))
rx_fill(dev, GFP_KERNEL);
}
if (dev->todo)
DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
}
static void tx_complete(struct usb_ep *ep, struct usb_request *req)
{
struct sk_buff *skb = req->context;
struct eth_dev *dev = ep->driver_data;
switch (req->status) {
default:
dev->net->stats.tx_errors++;
VDBG(dev, "tx err %d\n", req->status);
/* FALLTHROUGH */
case -ECONNRESET: /* unlink */
case -ESHUTDOWN: /* disconnect etc */
break;
case 0:
dev->net->stats.tx_bytes += skb->len;
}
dev->net->stats.tx_packets++;
spin_lock(&dev->req_lock);
list_add(&req->list, &dev->tx_reqs);
spin_unlock(&dev->req_lock);
dev_kfree_skb_any(skb);
atomic_dec(&dev->tx_qlen);
if (netif_carrier_ok(dev->net))
netif_wake_queue(dev->net);
}
static inline int is_promisc(u16 cdc_filter)
{
return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
}
static int eth_start_xmit(struct sk_buff *skb, struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
int length = skb->len;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
struct usb_ep *in;
u16 cdc_filter;
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
in = dev->port_usb->in_ep;
cdc_filter = dev->port_usb->cdc_filter;
} else {
in = NULL;
cdc_filter = 0;
}
spin_unlock_irqrestore(&dev->lock, flags);
if (!in) {
dev_kfree_skb_any(skb);
return 0;
}
/* apply outgoing CDC or RNDIS filters */
if (!is_promisc(cdc_filter)) {
u8 *dest = skb->data;
if (is_multicast_ether_addr(dest)) {
u16 type;
/* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
* SET_ETHERNET_MULTICAST_FILTERS requests
*/
if (is_broadcast_ether_addr(dest))
type = USB_CDC_PACKET_TYPE_BROADCAST;
else
type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
if (!(cdc_filter & type)) {
dev_kfree_skb_any(skb);
return 0;
}
}
/* ignores USB_CDC_PACKET_TYPE_DIRECTED */
}
spin_lock_irqsave(&dev->req_lock, flags);
/*
* this freelist can be empty if an interrupt triggered disconnect()
* and reconfigured the gadget (shutting down this queue) after the
* network stack decided to xmit but before we got the spinlock.
*/
if (list_empty(&dev->tx_reqs)) {
spin_unlock_irqrestore(&dev->req_lock, flags);
return 1;
}
req = container_of(dev->tx_reqs.next, struct usb_request, list);
list_del(&req->list);
/* temporarily stop TX queue when the freelist empties */
if (list_empty(&dev->tx_reqs))
netif_stop_queue(net);
spin_unlock_irqrestore(&dev->req_lock, flags);
/* no buffer copies needed, unless the network stack did it
* or the hardware can't use skb buffers.
* or there's not enough space for extra headers we need
*/
if (dev->wrap) {
struct sk_buff *skb_new;
skb_new = dev->wrap(skb);
if (!skb_new)
goto drop;
dev_kfree_skb_any(skb);
skb = skb_new;
length = skb->len;
}
req->buf = skb->data;
req->context = skb;
req->complete = tx_complete;
/* use zlp framing on tx for strict CDC-Ether conformance,
* though any robust network rx path ignores extra padding.
* and some hardware doesn't like to write zlps.
*/
req->zero = 1;
if (!dev->zlp && (length % in->maxpacket) == 0)
length++;
req->length = length;
/* throttle highspeed IRQ rate back slightly */
if (gadget_is_dualspeed(dev->gadget))
req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
: 0;
retval = usb_ep_queue(in, req, GFP_ATOMIC);
switch (retval) {
default:
DBG(dev, "tx queue err %d\n", retval);
break;
case 0:
net->trans_start = jiffies;
atomic_inc(&dev->tx_qlen);
}
if (retval) {
drop:
dev->net->stats.tx_dropped++;
dev_kfree_skb_any(skb);
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
list_add(&req->list, &dev->tx_reqs);
spin_unlock_irqrestore(&dev->req_lock, flags);
}
return 0;
}
/*-------------------------------------------------------------------------*/
static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
{
DBG(dev, "%s\n", __func__);
/* fill the rx queue */
rx_fill(dev, gfp_flags);
/* and open the tx floodgates */
atomic_set(&dev->tx_qlen, 0);
netif_wake_queue(dev->net);
}
static int eth_open(struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
struct gether *link;
DBG(dev, "%s\n", __func__);
if (netif_carrier_ok(dev->net))
eth_start(dev, GFP_KERNEL);
spin_lock_irq(&dev->lock);
link = dev->port_usb;
if (link && link->open)
link->open(link);
spin_unlock_irq(&dev->lock);
return 0;
}
static int eth_stop(struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
unsigned long flags;
VDBG(dev, "%s\n", __func__);
netif_stop_queue(net);
DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
dev->net->stats.rx_packets, dev->net->stats.tx_packets,
dev->net->stats.rx_errors, dev->net->stats.tx_errors
);
/* ensure there are no more active requests */
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
struct gether *link = dev->port_usb;
if (link->close)
link->close(link);
/* NOTE: we have no abort-queue primitive we could use
* to cancel all pending I/O. Instead, we disable then
* reenable the endpoints ... this idiom may leave toggle
* wrong, but that's a self-correcting error.
*
* REVISIT: we *COULD* just let the transfers complete at
* their own pace; the network stack can handle old packets.
* For the moment we leave this here, since it works.
*/
usb_ep_disable(link->in_ep);
usb_ep_disable(link->out_ep);
if (netif_carrier_ok(net)) {
DBG(dev, "host still using in/out endpoints\n");
usb_ep_enable(link->in_ep, link->in);
usb_ep_enable(link->out_ep, link->out);
}
}
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
}
/*-------------------------------------------------------------------------*/
/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
static char *dev_addr;
module_param(dev_addr, charp, S_IRUGO);
MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
/* this address is invisible to ifconfig */
static char *host_addr;
module_param(host_addr, charp, S_IRUGO);
MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
static u8 __init nibble(unsigned char c)
{
if (isdigit(c))
return c - '0';
c = toupper(c);
if (isxdigit(c))
return 10 + c - 'A';
return 0;
}
static int __init get_ether_addr(const char *str, u8 *dev_addr)
{
if (str) {
unsigned i;
for (i = 0; i < 6; i++) {
unsigned char num;
if ((*str == '.') || (*str == ':'))
str++;
num = nibble(*str++) << 4;
num |= (nibble(*str++));
dev_addr [i] = num;
}
if (is_valid_ether_addr(dev_addr))
return 0;
}
random_ether_addr(dev_addr);
return 1;
}
static struct eth_dev *the_dev;
/**
* gether_setup - initialize one ethernet-over-usb link
* @g: gadget to associated with these links
* @ethaddr: NULL, or a buffer in which the ethernet address of the
* host side of the link is recorded
* Context: may sleep
*
* This sets up the single network link that may be exported by a
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
* Returns negative errno, or zero on success
*/
int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
{
struct eth_dev *dev;
struct net_device *net;
int status;
if (the_dev)
return -EBUSY;
net = alloc_etherdev(sizeof *dev);
if (!net)
return -ENOMEM;
dev = netdev_priv(net);
spin_lock_init(&dev->lock);
spin_lock_init(&dev->req_lock);
INIT_WORK(&dev->work, eth_work);
INIT_LIST_HEAD(&dev->tx_reqs);
INIT_LIST_HEAD(&dev->rx_reqs);
/* network device setup */
dev->net = net;
strcpy(net->name, "usb%d");
if (get_ether_addr(dev_addr, net->dev_addr))
dev_warn(&g->dev,
"using random %s ethernet address\n", "self");
if (get_ether_addr(host_addr, dev->host_mac))
dev_warn(&g->dev,
"using random %s ethernet address\n", "host");
if (ethaddr)
memcpy(ethaddr, dev->host_mac, ETH_ALEN);
net->change_mtu = eth_change_mtu;
net->hard_start_xmit = eth_start_xmit;
net->open = eth_open;
net->stop = eth_stop;
/* watchdog_timeo, tx_timeout ... */
/* set_multicast_list */
SET_ETHTOOL_OPS(net, &ops);
/* two kinds of host-initiated state changes:
* - iff DATA transfer is active, carrier is "on"
* - tx queueing enabled if open *and* carrier is "on"
*/
netif_stop_queue(net);
netif_carrier_off(net);
dev->gadget = g;
SET_NETDEV_DEV(net, &g->dev);
status = register_netdev(net);
if (status < 0) {
dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
free_netdev(net);
} else {
DECLARE_MAC_BUF(tmp);
INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr));
INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac));
the_dev = dev;
}
return status;
}
/**
* gether_cleanup - remove Ethernet-over-USB device
* Context: may sleep
*
* This is called to free all resources allocated by @gether_setup().
*/
void gether_cleanup(void)
{
if (!the_dev)
return;
unregister_netdev(the_dev->net);
free_netdev(the_dev->net);
/* assuming we used keventd, it must quiesce too */
flush_scheduled_work();
the_dev = NULL;
}
/**
* gether_connect - notify network layer that USB link is active
* @link: the USB link, set up with endpoints, descriptors matching
* current device speed, and any framing wrapper(s) set up.
* Context: irqs blocked
*
* This is called to activate endpoints and let the network layer know
* the connection is active ("carrier detect"). It may cause the I/O
* queues to open and start letting network packets flow, but will in
* any case activate the endpoints so that they respond properly to the
* USB host.
*
* Verify net_device pointer returned using IS_ERR(). If it doesn't
* indicate some error code (negative errno), ep->driver_data values
* have been overwritten.
*/
struct net_device *gether_connect(struct gether *link)
{
struct eth_dev *dev = the_dev;
int result = 0;
if (!dev)
return ERR_PTR(-EINVAL);
link->in_ep->driver_data = dev;
result = usb_ep_enable(link->in_ep, link->in);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->in_ep->name, result);
goto fail0;
}
link->out_ep->driver_data = dev;
result = usb_ep_enable(link->out_ep, link->out);
if (result != 0) {
DBG(dev, "enable %s --> %d\n",
link->out_ep->name, result);
goto fail1;
}
if (result == 0)
result = alloc_requests(dev, link, qlen(dev->gadget));
if (result == 0) {
dev->zlp = link->is_zlp_ok;
DBG(dev, "qlen %d\n", qlen(dev->gadget));
dev->header_len = link->header_len;
dev->unwrap = link->unwrap;
dev->wrap = link->wrap;
spin_lock(&dev->lock);
dev->port_usb = link;
link->ioport = dev;
spin_unlock(&dev->lock);
netif_carrier_on(dev->net);
if (netif_running(dev->net))
eth_start(dev, GFP_ATOMIC);
/* on error, disable any endpoints */
} else {
(void) usb_ep_disable(link->out_ep);
fail1:
(void) usb_ep_disable(link->in_ep);
}
fail0:
/* caller is responsible for cleanup on error */
if (result < 0)
return ERR_PTR(result);
return dev->net;
}
/**
* gether_disconnect - notify network layer that USB link is inactive
* @link: the USB link, on which gether_connect() was called
* Context: irqs blocked
*
* This is called to deactivate endpoints and let the network layer know
* the connection went inactive ("no carrier").
*
* On return, the state is as if gether_connect() had never been called.
* The endpoints are inactive, and accordingly without active USB I/O.
* Pointers to endpoint descriptors and endpoint private data are nulled.
*/
void gether_disconnect(struct gether *link)
{
struct eth_dev *dev = link->ioport;
struct usb_request *req;
WARN_ON(!dev);
if (!dev)
return;
DBG(dev, "%s\n", __func__);
netif_stop_queue(dev->net);
netif_carrier_off(dev->net);
/* disable endpoints, forcing (synchronous) completion
* of all pending i/o. then free the request objects
* and forget about the endpoints.
*/
usb_ep_disable(link->in_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->tx_reqs)) {
req = container_of(dev->tx_reqs.next,
struct usb_request, list);
list_del(&req->list);
spin_unlock(&dev->req_lock);
usb_ep_free_request(link->in_ep, req);
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
link->in_ep->driver_data = NULL;
link->in = NULL;
usb_ep_disable(link->out_ep);
spin_lock(&dev->req_lock);
while (!list_empty(&dev->rx_reqs)) {
req = container_of(dev->rx_reqs.next,
struct usb_request, list);
list_del(&req->list);
spin_unlock(&dev->req_lock);
usb_ep_free_request(link->out_ep, req);
spin_lock(&dev->req_lock);
}
spin_unlock(&dev->req_lock);
link->out_ep->driver_data = NULL;
link->out = NULL;
/* finish forgetting about this USB link episode */
dev->header_len = 0;
dev->unwrap = NULL;
dev->wrap = NULL;
spin_lock(&dev->lock);
dev->port_usb = NULL;
link->ioport = NULL;
spin_unlock(&dev->lock);
}

View File

@ -0,0 +1,127 @@
/*
* u_ether.h -- interface to USB gadget "ethernet link" utilities
*
* Copyright (C) 2003-2005,2008 David Brownell
* Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
* Copyright (C) 2008 Nokia Corporation
*
* This program 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 program 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
*/
#ifndef __U_ETHER_H
#define __U_ETHER_H
#include <linux/err.h>
#include <linux/if_ether.h>
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
#include "gadget_chips.h"
/*
* This represents the USB side of an "ethernet" link, managed by a USB
* function which provides control and (maybe) framing. Two functions
* in different configurations could share the same ethernet link/netdev,
* using different host interaction models.
*
* There is a current limitation that only one instance of this link may
* be present in any given configuration. When that's a problem, network
* layer facilities can be used to package multiple logical links on this
* single "physical" one.
*/
struct gether {
struct usb_function func;
/* updated by gether_{connect,disconnect} */
struct eth_dev *ioport;
/* endpoints handle full and/or high speeds */
struct usb_ep *in_ep;
struct usb_ep *out_ep;
/* descriptors match device speed at gether_connect() time */
struct usb_endpoint_descriptor *in;
struct usb_endpoint_descriptor *out;
bool is_zlp_ok;
u16 cdc_filter;
/* hooks for added framing, as needed for RNDIS and EEM.
* we currently don't support multiple frames per SKB.
*/
u32 header_len;
struct sk_buff *(*wrap)(struct sk_buff *skb);
int (*unwrap)(struct sk_buff *skb);
/* called on network open/close */
void (*open)(struct gether *);
void (*close)(struct gether *);
};
#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
|USB_CDC_PACKET_TYPE_ALL_MULTICAST \
|USB_CDC_PACKET_TYPE_PROMISCUOUS \
|USB_CDC_PACKET_TYPE_DIRECTED)
/* netdev setup/teardown as directed by the gadget driver */
int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
void gether_cleanup(void);
/* connect/disconnect is handled by individual functions */
struct net_device *gether_connect(struct gether *);
void gether_disconnect(struct gether *);
/* Some controllers can't support CDC Ethernet (ECM) ... */
static inline bool can_support_ecm(struct usb_gadget *gadget)
{
if (!gadget_supports_altsettings(gadget))
return false;
/* SA1100 can do ECM, *without* status endpoint ... but we'll
* only use it in non-ECM mode for backwards compatibility
* (and since we currently require a status endpoint)
*/
if (gadget_is_sa1100(gadget))
return false;
/* Everything else is *presumably* fine ... but this is a bit
* chancy, so be **CERTAIN** there are no hardware issues with
* your controller. Add it above if it can't handle CDC.
*/
return true;
}
/* each configuration may bind one instance of an ethernet link */
int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
#ifdef CONFIG_USB_ETH_RNDIS
int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
#else
static inline int
rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
{
return 0;
}
#endif
#endif /* __U_ETHER_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,58 @@
/*
* u_serial.h - interface to USB gadget "serial port"/TTY utilities
*
* Copyright (C) 2008 David Brownell
* Copyright (C) 2008 by Nokia Corporation
*
* This software is distributed under the terms of the GNU General
* Public License ("GPL") as published by the Free Software Foundation,
* either version 2 of that License or (at your option) any later version.
*/
#ifndef __U_SERIAL_H
#define __U_SERIAL_H
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
/*
* One non-multiplexed "serial" I/O port ... there can be several of these
* on any given USB peripheral device, if it provides enough endpoints.
*
* The "u_serial" utility component exists to do one thing: manage TTY
* style I/O using the USB peripheral endpoints listed here, including
* hookups to sysfs and /dev for each logical "tty" device.
*
* REVISIT need TTY --> USB event flow too, so ACM can report open/close
* as carrier detect events. Model after ECM. There's more ACM state too.
*
* REVISIT someday, allow multiplexing several TTYs over these endpoints.
*/
struct gserial {
struct usb_function func;
/* port is managed by gserial_{connect,disconnect} */
struct gs_port *ioport;
struct usb_ep *in;
struct usb_ep *out;
struct usb_endpoint_descriptor *in_desc;
struct usb_endpoint_descriptor *out_desc;
/* REVISIT avoid this CDC-ACM support harder ... */
struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
};
/* port setup/teardown is handled by gadget driver */
int gserial_setup(struct usb_gadget *g, unsigned n_ports);
void gserial_cleanup(void);
/* connect/disconnect is handled by individual functions */
int gserial_connect(struct gserial *, u8 port_num);
void gserial_disconnect(struct gserial *);
/* functions are bound to configurations by a config or gadget driver */
int acm_bind_config(struct usb_configuration *c, u8 port_num);
int gser_bind_config(struct usb_configuration *c, u8 port_num);
#endif /* __U_SERIAL_H */

File diff suppressed because it is too large Load Diff

View File

@ -19,178 +19,66 @@
#define USB_MCFG_RDCOMB (1<<30)
#define USB_MCFG_SSDEN (1<<23)
#define USB_MCFG_PHYPLLEN (1<<19)
#define USB_MCFG_UCECLKEN (1<<18)
#define USB_MCFG_EHCCLKEN (1<<17)
#ifdef CONFIG_DMA_COHERENT
#define USB_MCFG_UCAM (1<<7)
#else
#define USB_MCFG_UCAM (0)
#endif
#define USB_MCFG_EBMEN (1<<3)
#define USB_MCFG_EMEMEN (1<<2)
#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
#define USBH_ENABLE_CE (USB_MCFG_PHYPLLEN | USB_MCFG_EHCCLKEN)
#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \
USBH_ENABLE_CE | USB_MCFG_SSDEN | \
USB_MCFG_UCAM | USB_MCFG_EBMEN | \
USB_MCFG_EMEMEN)
#ifdef CONFIG_DMA_COHERENT
#define USBH_ENABLE_INIT (USBH_ENABLE_CE \
| USB_MCFG_PFEN | USB_MCFG_RDCOMB \
| USB_MCFG_SSDEN | USB_MCFG_UCAM \
| USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
#else
#define USBH_ENABLE_INIT (USBH_ENABLE_CE \
| USB_MCFG_PFEN | USB_MCFG_RDCOMB \
| USB_MCFG_SSDEN \
| USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
#endif
#define USBH_DISABLE (USB_MCFG_EBMEN | USB_MCFG_EMEMEN)
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void au1xxx_start_ehc(struct platform_device *dev)
static void au1xxx_start_ehc(void)
{
pr_debug(__FILE__ ": starting Au1xxx EHCI USB Controller\n");
/* write HW defaults again in case Yamon cleared them */
if (au_readl(USB_HOST_CONFIG) == 0) {
au_writel(0x00d02000, USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
udelay(1000);
}
/* enable host controller */
au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG),
USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
/* enable clock to EHCI block and HS PHY PLL*/
au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
au_sync();
udelay(1000);
pr_debug(__FILE__ ": Clock to USB host has been enabled\n");
/* enable EHCI mmio */
au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
au_sync();
udelay(1000);
}
static void au1xxx_stop_ehc(struct platform_device *dev)
static void au1xxx_stop_ehc(void)
{
pr_debug(__FILE__ ": stopping Au1xxx EHCI USB Controller\n");
unsigned long c;
/* Disable mem */
au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
au_sync();
udelay(1000);
/* Disable clock */
au_writel(~USB_MCFG_EHCCLKEN & au_readl(USB_HOST_CONFIG),
USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
/* Disable EHC clock. If the HS PHY is unused disable it too. */
c = au_readl(USB_HOST_CONFIG) & ~USB_MCFG_EHCCLKEN;
if (!(c & USB_MCFG_UCECLKEN)) /* UDC disabled? */
c &= ~USB_MCFG_PHYPLLEN; /* yes: disable HS PHY PLL */
au_writel(c, USB_HOST_CONFIG);
au_sync();
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_ehci_au1xxx_probe - initialize Au1xxx-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
int usb_ehci_au1xxx_probe(const struct hc_driver *driver,
struct usb_hcd **hcd_out, struct platform_device *dev)
{
int retval;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
/* Au1200 AB USB does not support coherent memory */
if (!(read_c0_prid() & 0xff)) {
pr_info("%s: this is chip revision AB!\n", dev->name);
pr_info("%s: update your board or re-configure the kernel\n",
dev->name);
return -ENODEV;
}
#endif
au1xxx_start_ehc(dev);
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
retval = -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "Au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
retval = -ENOMEM;
goto err2;
}
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
/* ehci_hcd_init(hcd_to_ehci(hcd)); */
retval =
usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED);
if (retval == 0)
return retval;
au1xxx_stop_ehc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_ehci_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_ehci_hcd_au1xxx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
void usb_ehci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev)
{
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
au1xxx_stop_ehc(dev);
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ehci_au1xxx_hc_driver = {
.description = hcd_name,
.product_desc = "Au1xxx EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
.description = hcd_name,
.product_desc = "Au1xxx EHCI",
.hcd_priv_size = sizeof(struct ehci_hcd),
/*
* generic hardware linkage
*/
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
.irq = ehci_irq,
.flags = HCD_MEMORY | HCD_USB2,
/*
* basic lifecycle operations
@ -198,48 +86,98 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
* FIXME -- ehci_init() doesn't do enough here.
* See ehci-ppc-soc for a complete implementation.
*/
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
.shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
*/
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
.urb_enqueue = ehci_urb_enqueue,
.urb_dequeue = ehci_urb_dequeue,
.endpoint_disable = ehci_endpoint_disable,
/*
* scheduling support
*/
.get_frame_number = ehci_get_frame,
.get_frame_number = ehci_get_frame,
/*
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
.hub_status_data = ehci_hub_status_data,
.hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
.relinquish_port = ehci_relinquish_port,
.port_handed_over = ehci_port_handed_over,
};
/*-------------------------------------------------------------------------*/
static int ehci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
{
struct usb_hcd *hcd = NULL;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
int ret;
pr_debug("In ehci_hcd_au1xxx_drv_probe\n");
if (usb_disabled())
return -ENODEV;
/* FIXME we only want one one probe() not two */
ret = usb_ehci_au1xxx_probe(&ehci_au1xxx_hc_driver, &hcd, pdev);
#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
/* Au1200 AB USB does not support coherent memory */
if (!(read_c0_prid() & 0xff)) {
printk(KERN_INFO "%s: this is chip revision AB!\n", pdev->name);
printk(KERN_INFO "%s: update your board or re-configure"
" the kernel\n", pdev->name);
return -ENODEV;
}
#endif
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ");
return -ENOMEM;
}
hcd = usb_create_hcd(&ehci_au1xxx_hc_driver, &pdev->dev, "Au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed");
ret = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed");
ret = -ENOMEM;
goto err2;
}
au1xxx_start_ehc();
ehci = hcd_to_ehci(hcd);
ehci->caps = hcd->regs;
ehci->regs = hcd->regs + HC_LENGTH(readl(&ehci->caps->hc_capbase));
/* cache this readonly data; minimize chip reads */
ehci->hcs_params = readl(&ehci->caps->hcs_params);
ret = usb_add_hcd(hcd, pdev->resource[1].start,
IRQF_DISABLED | IRQF_SHARED);
if (ret == 0) {
platform_set_drvdata(pdev, hcd);
return ret;
}
au1xxx_stop_ehc();
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
}
@ -247,35 +185,138 @@ static int ehci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
/* FIXME we only want one one remove() not two */
usb_ehci_au1xxx_remove(hcd, pdev);
usb_remove_hcd(hcd);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
au1xxx_stop_ehc();
platform_set_drvdata(pdev, NULL);
return 0;
}
/*TBD*/
/*static int ehci_hcd_au1xxx_drv_suspend(struct device *dev)
#ifdef CONFIG_PM
static int ehci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
pm_message_t message)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
unsigned long flags;
int rc;
return 0;
rc = 0;
if (time_before(jiffies, ehci->next_statechange))
msleep(10);
/* Root hub was already suspended. Disable irq emission and
* mark HW unaccessible, bail out if RH has been resumed. Use
* the spinlock to properly synchronize with possible pending
* RH suspend or resume activity.
*
* This is still racy as hcd->state is manipulated outside of
* any locks =P But that will be a different fix.
*/
spin_lock_irqsave(&ehci->lock, flags);
if (hcd->state != HC_STATE_SUSPENDED) {
rc = -EINVAL;
goto bail;
}
ehci_writel(ehci, 0, &ehci->regs->intr_enable);
(void)ehci_readl(ehci, &ehci->regs->intr_enable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW) {
ehci_halt(ehci);
ehci_reset(ehci);
}
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
au1xxx_stop_ehc();
bail:
spin_unlock_irqrestore(&ehci->lock, flags);
// could save FLADJ in case of Vaux power loss
// ... we'd only use it to handle clock skew
return rc;
}
static int ehci_hcd_au1xxx_drv_resume(struct device *dev)
static int ehci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
{
struct platform_device *pdev = to_platform_device(dev);
struct usb_hcd *hcd = dev_get_drvdata(dev);
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
au1xxx_start_ehc();
// maybe restore FLADJ
if (time_before(jiffies, ehci->next_statechange))
msleep(100);
/* Mark hardware accessible again as we are out of D3 state by now */
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
/* If CF is still set, we maintained PCI Vaux power.
* Just undo the effect of ehci_pci_suspend().
*/
if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF) {
int mask = INTR_MASK;
if (!hcd->self.root_hub->do_remote_wakeup)
mask &= ~STS_PCD;
ehci_writel(ehci, mask, &ehci->regs->intr_enable);
ehci_readl(ehci, &ehci->regs->intr_enable);
return 0;
}
ehci_dbg(ehci, "lost power, restarting\n");
usb_root_hub_lost_power(hcd->self.root_hub);
/* Else reset, to cope with power loss or flush-to-storage
* style "resume" having let BIOS kick in during reboot.
*/
(void) ehci_halt(ehci);
(void) ehci_reset(ehci);
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
end_unlink_async(ehci);
ehci_work(ehci);
spin_unlock_irq(&ehci->lock);
ehci_writel(ehci, ehci->command, &ehci->regs->command);
ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */
/* here we "know" root ports should always stay powered */
ehci_port_power(ehci, 1);
hcd->state = HC_STATE_SUSPENDED;
return 0;
}
*/
MODULE_ALIAS("platform:au1xxx-ehci");
#else
#define ehci_hcd_au1xxx_drv_suspend NULL
#define ehci_hcd_au1xxx_drv_resume NULL
#endif
static struct platform_driver ehci_hcd_au1xxx_driver = {
.probe = ehci_hcd_au1xxx_drv_probe,
.remove = ehci_hcd_au1xxx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ehci_hcd_au1xxx_drv_suspend, */
/*.resume = ehci_hcd_au1xxx_drv_resume, */
.probe = ehci_hcd_au1xxx_drv_probe,
.remove = ehci_hcd_au1xxx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
.suspend = ehci_hcd_au1xxx_drv_suspend,
.resume = ehci_hcd_au1xxx_drv_resume,
.driver = {
.name = "au1xxx-ehci",
.name = "au1xxx-ehci",
.owner = THIS_MODULE,
}
};
MODULE_ALIAS("platform:au1xxx-ehci");

View File

@ -676,7 +676,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"SUSPENDED (no register access)\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
dev_name(hcd->self.controller),
hcd->product_desc);
goto done;
}
@ -688,7 +688,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"EHCI %x.%02x, hcd state %d\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
dev_name(hcd->self.controller),
hcd->product_desc,
i >> 8, i & 0x0ff, hcd->state);
size -= temp;

View File

@ -56,7 +56,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
pdata = (struct fsl_usb2_platform_data *)pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev,
"No platform data for %s.\n", pdev->dev.bus_id);
"No platform data for %s.\n", dev_name(&pdev->dev));
return -ENODEV;
}
@ -69,7 +69,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
(pdata->operating_mode == FSL_USB2_DR_OTG))) {
dev_err(&pdev->dev,
"Non Host Mode configured for %s. Wrong driver linked.\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
return -ENODEV;
}
@ -77,12 +77,12 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (!res) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
return -ENODEV;
}
irq = res->start;
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err1;
@ -92,7 +92,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
retval = -ENODEV;
goto err2;
}
@ -132,7 +132,7 @@ int usb_hcd_fsl_probe(const struct hc_driver *driver,
err2:
usb_put_hcd(hcd);
err1:
dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
return retval;
}
@ -230,8 +230,13 @@ static void mpc83xx_usb_setup(struct usb_hcd *hcd)
/* put controller in host mode. */
ehci_writel(ehci, 0x00000003, non_ehci + FSL_SOC_USB_USBMODE);
#ifdef CONFIG_PPC_85xx
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x00000008);
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000080);
#else
out_be32(non_ehci + FSL_SOC_USB_PRICTRL, 0x0000000c);
out_be32(non_ehci + FSL_SOC_USB_AGECNTTHRSH, 0x00000040);
#endif
out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
}

View File

@ -84,7 +84,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
#define EHCI_SHRINK_FRAMES 5 /* async qh unlink delay */
/* Initial IRQ latency: faster than hw default */
static int log2_irq_thresh = 0; // 0 to 6

View File

@ -77,12 +77,12 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
return -ENODEV;
}
irq = res->start;
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto fail_create_hcd;
@ -92,7 +92,7 @@ static int ixp4xx_ehci_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
retval = -ENODEV;
goto fail_request_resource;
}
@ -126,7 +126,7 @@ fail_ioremap:
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
dev_err(&pdev->dev, "init %s fail, %d\n", pdev->dev.bus_id, retval);
dev_err(&pdev->dev, "init %s fail, %d\n", dev_name(&pdev->dev), retval);
return retval;
}

View File

@ -204,7 +204,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
if (irq <= 0) {
dev_err(&pdev->dev,
"Found HC with no IRQ. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
@ -213,7 +213,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
if (!res) {
dev_err(&pdev->dev,
"Found HC with no register addr. Check %s setup!\n",
pdev->dev.bus_id);
dev_name(&pdev->dev));
err = -ENODEV;
goto err1;
}
@ -233,7 +233,7 @@ static int __init ehci_orion_drv_probe(struct platform_device *pdev)
}
hcd = usb_create_hcd(&ehci_orion_hc_driver,
&pdev->dev, pdev->dev.bus_id);
&pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
err = -ENOMEM;
goto err3;
@ -276,7 +276,7 @@ err2:
release_mem_region(res->start, res->end - res->start + 1);
err1:
dev_err(&pdev->dev, "init %s fail, %d\n",
pdev->dev.bus_id, err);
dev_name(&pdev->dev), err);
return err;
}

View File

@ -128,7 +128,7 @@ static int ps3_ehci_probe(struct ps3_system_bus_device *dev)
dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev->core.bus_id);
hcd = usb_create_hcd(&ps3_ehci_hc_driver, &dev->core, dev_name(&dev->core));
if (!hcd) {
dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,

View File

@ -1116,8 +1116,7 @@ static void scan_async (struct ehci_hcd *ehci)
struct ehci_qh *qh;
enum ehci_timer_action action = TIMER_IO_WATCHDOG;
if (!++(ehci->stamp))
ehci->stamp++;
ehci->stamp = ehci_readl(ehci, &ehci->regs->frame_index);
timer_action_done (ehci, TIMER_ASYNC_SHRINK);
rescan:
qh = ehci->async->qh_next.qh;
@ -1142,18 +1141,20 @@ rescan:
}
}
/* unlink idle entries, reducing HC PCI usage as well
/* unlink idle entries, reducing DMA usage as well
* as HCD schedule-scanning costs. delay for any qh
* we just scanned, there's a not-unusual case that it
* doesn't stay idle for long.
* (plus, avoids some kind of re-activation race.)
*/
if (list_empty (&qh->qtd_list)) {
if (qh->stamp == ehci->stamp)
if (list_empty(&qh->qtd_list)
&& qh->qh_state == QH_STATE_LINKED) {
if (!ehci->reclaim
&& ((ehci->stamp - qh->stamp) & 0x1fff)
>= (EHCI_SHRINK_FRAMES * 8))
start_unlink_async(ehci, qh);
else
action = TIMER_ASYNC_SHRINK;
else if (!ehci->reclaim
&& qh->qh_state == QH_STATE_LINKED)
start_unlink_async (ehci, qh);
}
qh = qh->qh_next.qh;

View File

@ -198,7 +198,10 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
break;
// case TIMER_ASYNC_SHRINK:
default:
t = EHCI_SHRINK_JIFFIES;
/* add a jiffie since we synch against the
* 8 KHz uframe counter.
*/
t = DIV_ROUND_UP(EHCI_SHRINK_FRAMES * HZ, 1000) + 1;
break;
}
mod_timer(&ehci->watchdog, t + jiffies);

View File

@ -94,6 +94,10 @@ static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
u16 w;
int quot = len % 4;
/* buffer is already in 'usb data order', which is LE. */
/* When reading buffer as u16, we have to take care byte order */
/* doesn't get mixed up */
if ((unsigned long)dp2 & 1) {
/* not aligned */
for (; len > 1; len -= 2) {
@ -105,8 +109,11 @@ static void write_ptddata_to_fifo(struct isp116x *isp116x, void *buf, int len)
isp116x_write_data16(isp116x, (u16) * dp);
} else {
/* aligned */
for (; len > 1; len -= 2)
isp116x_raw_write_data16(isp116x, *dp2++);
for (; len > 1; len -= 2) {
/* Keep byte order ! */
isp116x_raw_write_data16(isp116x, cpu_to_le16(*dp2++));
}
if (len)
isp116x_write_data16(isp116x, 0xff & *((u8 *) dp2));
}
@ -124,6 +131,10 @@ static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
u16 w;
int quot = len % 4;
/* buffer is already in 'usb data order', which is LE. */
/* When reading buffer as u16, we have to take care byte order */
/* doesn't get mixed up */
if ((unsigned long)dp2 & 1) {
/* not aligned */
for (; len > 1; len -= 2) {
@ -131,12 +142,16 @@ static void read_ptddata_from_fifo(struct isp116x *isp116x, void *buf, int len)
*dp++ = w & 0xff;
*dp++ = (w >> 8) & 0xff;
}
if (len)
*dp = 0xff & isp116x_read_data16(isp116x);
} else {
/* aligned */
for (; len > 1; len -= 2)
*dp2++ = isp116x_raw_read_data16(isp116x);
for (; len > 1; len -= 2) {
/* Keep byte order! */
*dp2++ = le16_to_cpu(isp116x_raw_read_data16(isp116x));
}
if (len)
*(u8 *) dp2 = 0xff & isp116x_read_data16(isp116x);
}
@ -1592,7 +1607,7 @@ static int __devinit isp116x_probe(struct platform_device *pdev)
}
/* allocate and initialize hcd */
hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(&isp116x_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
ret = -ENOMEM;
goto err5;

View File

@ -38,6 +38,7 @@ struct isp1760_hcd {
unsigned i_thresh;
unsigned long reset_done;
unsigned long next_statechange;
unsigned int devflags;
};
static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
@ -378,9 +379,31 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
int result;
u32 scratch;
u32 scratch, hwmode;
/* Setup HW Mode Control: This assumes a level active-low interrupt */
hwmode = HW_DATA_BUS_32BIT;
if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16)
hwmode &= ~HW_DATA_BUS_32BIT;
if (priv->devflags & ISP1760_FLAG_ANALOG_OC)
hwmode |= HW_ANA_DIGI_OC;
if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH)
hwmode |= HW_DACK_POL_HIGH;
if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
hwmode |= HW_DREQ_POL_HIGH;
/*
* We have to set this first in case we're in 16-bit mode.
* Write it twice to ensure correct upper bits if switching
* to 16-bit mode.
*/
isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(0xdeadbabe, hcd->regs + HC_SCRATCH_REG);
/* Change bus pattern */
scratch = isp1760_readl(hcd->regs + HC_CHIP_ID_REG);
scratch = isp1760_readl(hcd->regs + HC_SCRATCH_REG);
if (scratch != 0xdeadbabe) {
printk(KERN_ERR "ISP1760: Scratch test failed.\n");
@ -403,17 +426,29 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
/* Step 11 passed */
isp1760_info(priv, "bus width: %d, oc: %s\n",
(priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
"analog" : "digital");
/* ATL reset */
isp1760_writel(hwmode | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL);
mdelay(10);
isp1760_writel(hwmode, hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_REG);
isp1760_writel(INTERRUPT_ENABLE_MASK, hcd->regs + HC_INTERRUPT_ENABLE);
/* ATL reset */
scratch = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(scratch | ALL_ATX_RESET, hcd->regs + HC_HW_MODE_CTRL);
mdelay(10);
isp1760_writel(scratch, hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(PORT1_POWER | PORT1_INIT2, hcd->regs + HC_PORT1_CTRL);
mdelay(10);
/*
* PORT 1 Control register of the ISP1760 is the OTG control
* register on ISP1761.
*/
if (!(priv->devflags & ISP1760_FLAG_ISP1761) &&
!(priv->devflags & ISP1760_FLAG_PORT1_DIS)) {
isp1760_writel(PORT1_POWER | PORT1_INIT2,
hcd->regs + HC_PORT1_CTRL);
mdelay(10);
}
priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS);
@ -453,8 +488,7 @@ static int isp1760_run(struct usb_hcd *hcd)
hcd->state = HC_STATE_RUNNING;
isp1760_enable_interrupts(hcd);
temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
temp |= FINAL_HW_CONFIG;
isp1760_writel(temp, hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(temp | HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
command = isp1760_readl(hcd->regs + HC_USBCMD);
command &= ~(CMD_LRESET|CMD_RESET);
@ -782,8 +816,8 @@ static void enqueue_one_int_qtd(u32 int_regs, u32 payload,
qtd->status |= slot << 16;
}
void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd)
static void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
@ -816,8 +850,8 @@ void enqueue_an_ATL_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
isp1760_writel(buffstatus, hcd->regs + HC_BUFFER_STATUS_REG);
}
void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd)
static void enqueue_an_INT_packet(struct usb_hcd *hcd, struct isp1760_qh *qh,
struct isp1760_qtd *qtd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 skip_map, or_map;
@ -1592,7 +1626,7 @@ static int isp1760_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
struct inter_packet_info *ints;
u32 i;
u32 reg_base, or_reg, skip_reg;
int flags;
unsigned long flags;
struct ptd ptd;
switch (usb_pipetype(urb->pipe)) {
@ -2061,7 +2095,7 @@ static void isp1760_endpoint_disable(struct usb_hcd *usb_hcd,
struct isp1760_hcd *priv = hcd_to_priv(usb_hcd);
struct isp1760_qh *qh;
struct isp1760_qtd *qtd;
u32 flags;
unsigned long flags;
spin_lock_irqsave(&priv->lock, flags);
qh = ep->hcpriv;
@ -2112,6 +2146,7 @@ static int isp1760_get_frame(struct usb_hcd *hcd)
static void isp1760_stop(struct usb_hcd *hcd)
{
struct isp1760_hcd *priv = hcd_to_priv(hcd);
u32 temp;
isp1760_hub_control(hcd, ClearPortFeature, USB_PORT_FEAT_POWER, 1,
NULL, 0);
@ -2120,7 +2155,8 @@ static void isp1760_stop(struct usb_hcd *hcd)
spin_lock_irq(&priv->lock);
ehci_reset(priv);
/* Disable IRQ */
isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL);
temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
spin_unlock_irq(&priv->lock);
isp1760_writel(0, hcd->regs + HC_CONFIGFLAG);
@ -2128,10 +2164,11 @@ static void isp1760_stop(struct usb_hcd *hcd)
static void isp1760_shutdown(struct usb_hcd *hcd)
{
u32 command;
u32 command, temp;
isp1760_stop(hcd);
isp1760_writel(HW_DATA_BUS_32BIT, hcd->regs + HC_HW_MODE_CTRL);
temp = isp1760_readl(hcd->regs + HC_HW_MODE_CTRL);
isp1760_writel(temp &= ~HW_GLOBAL_INTR_EN, hcd->regs + HC_HW_MODE_CTRL);
command = isp1760_readl(hcd->regs + HC_USBCMD);
command &= ~CMD_RUN;
@ -2183,7 +2220,8 @@ void deinit_kmem_cache(void)
}
struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
u64 irqflags, struct device *dev, const char *busname)
u64 irqflags, struct device *dev, const char *busname,
unsigned int devflags)
{
struct usb_hcd *hcd;
struct isp1760_hcd *priv;
@ -2195,11 +2233,12 @@ struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
/* prevent usb-core allocating DMA pages */
dev->dma_mask = NULL;
hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev->bus_id);
hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
if (!hcd)
return ERR_PTR(-ENOMEM);
priv = hcd_to_priv(hcd);
priv->devflags = devflags;
init_memory(priv);
hcd->regs = ioremap(res_start, res_len);
if (!hcd->regs) {

View File

@ -3,7 +3,8 @@
/* exports for if */
struct usb_hcd *isp1760_register(u64 res_start, u64 res_len, int irq,
u64 irqflags, struct device *dev, const char *busname);
u64 irqflags, struct device *dev, const char *busname,
unsigned int devflags);
int init_kmem_once(void);
void deinit_kmem_cache(void);
@ -31,6 +32,7 @@ void deinit_kmem_cache(void);
/* Configuration Register */
#define HC_HW_MODE_CTRL 0x300
#define ALL_ATX_RESET (1 << 31)
#define HW_ANA_DIGI_OC (1 << 15)
#define HW_DATA_BUS_32BIT (1 << 8)
#define HW_DACK_POL_HIGH (1 << 6)
#define HW_DREQ_POL_HIGH (1 << 5)
@ -56,13 +58,14 @@ void deinit_kmem_cache(void);
#define PORT1_POWER (3 << 3)
#define PORT1_INIT1 (1 << 7)
#define PORT1_INIT2 (1 << 23)
#define HW_OTG_CTRL_SET 0x374
#define HW_OTG_CTRL_CLR 0x376
/* Interrupt Register */
#define HC_INTERRUPT_REG 0x310
#define HC_INTERRUPT_ENABLE 0x314
#define INTERRUPT_ENABLE_MASK (HC_INTL_INT | HC_ATL_INT | HC_EOT_INT)
#define FINAL_HW_CONFIG (HW_GLOBAL_INTR_EN | HW_DATA_BUS_32BIT)
#define HC_ISO_INT (1 << 9)
#define HC_ATL_INT (1 << 8)
@ -122,6 +125,19 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
#define isp1760_err(priv, fmt, args...) \
dev_err(priv_to_hcd(priv)->self.controller, fmt, ##args)
/*
* Device flags that can vary from board to board. All of these
* indicate the most "atypical" case, so that a devflags of 0 is
* a sane default configuration.
*/
#define ISP1760_FLAG_PORT1_DIS 0x00000001 /* Port 1 disabled */
#define ISP1760_FLAG_BUS_WIDTH_16 0x00000002 /* 16-bit data bus width */
#define ISP1760_FLAG_OTG_EN 0x00000004 /* Port 1 supports OTG */
#define ISP1760_FLAG_ANALOG_OC 0x00000008 /* Analog overcurrent */
#define ISP1760_FLAG_DACK_POL_HIGH 0x00000010 /* DACK active high */
#define ISP1760_FLAG_DREQ_POL_HIGH 0x00000020 /* DREQ active high */
#define ISP1760_FLAG_ISP1761 0x00000040 /* Chip is ISP1761 */
/* chip memory management */
struct memory_chunk {
unsigned int start;

View File

@ -35,13 +35,15 @@ static int of_isp1760_probe(struct of_device *dev,
int virq;
u64 res_len;
int ret;
const unsigned int *prop;
unsigned int devflags = 0;
ret = of_address_to_resource(dp, 0, &memory);
if (ret)
return -ENXIO;
res = request_mem_region(memory.start, memory.end - memory.start + 1,
dev->dev.bus_id);
dev_name(&dev->dev));
if (!res)
return -EBUSY;
@ -55,8 +57,32 @@ static int of_isp1760_probe(struct of_device *dev,
virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
oirq.size);
if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
devflags |= ISP1760_FLAG_ISP1761;
if (of_get_property(dp, "port1-disable", NULL) != NULL)
devflags |= ISP1760_FLAG_PORT1_DIS;
/* Some systems wire up only 16 of the 32 data lines */
prop = of_get_property(dp, "bus-width", NULL);
if (prop && *prop == 16)
devflags |= ISP1760_FLAG_BUS_WIDTH_16;
if (of_get_property(dp, "port1-otg", NULL) != NULL)
devflags |= ISP1760_FLAG_OTG_EN;
if (of_get_property(dp, "analog-oc", NULL) != NULL)
devflags |= ISP1760_FLAG_ANALOG_OC;
if (of_get_property(dp, "dack-polarity", NULL) != NULL)
devflags |= ISP1760_FLAG_DACK_POL_HIGH;
if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
hcd = isp1760_register(memory.start, res_len, virq,
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
devflags);
if (IS_ERR(hcd)) {
ret = PTR_ERR(hcd);
goto release_reg;
@ -87,6 +113,9 @@ static struct of_device_id of_isp1760_match[] = {
{
.compatible = "nxp,usb-isp1760",
},
{
.compatible = "nxp,usb-isp1761",
},
{ },
};
MODULE_DEVICE_TABLE(of, of_isp1760_match);
@ -116,6 +145,7 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
int length;
int status = 1;
struct usb_hcd *hcd;
unsigned int devflags = 0;
if (usb_disabled())
return -ENODEV;
@ -200,7 +230,8 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
dev->dev.dma_mask = NULL;
hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev->dev.bus_id);
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
devflags);
pci_set_drvdata(dev, hcd);
if (!hcd)
return 0;

View File

@ -91,7 +91,7 @@ static void at91_stop_hc(struct platform_device *pdev)
/*-------------------------------------------------------------------------*/
static int usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
@ -184,13 +184,14 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
* context, "rmmod" or something similar.
*
*/
static int usb_hcd_at91_remove(struct usb_hcd *hcd,
static void usb_hcd_at91_remove(struct usb_hcd *hcd,
struct platform_device *pdev)
{
usb_remove_hcd(hcd);
at91_stop_hc(pdev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
if (cpu_is_at91sam9261())
clk_put(hclk);
@ -199,7 +200,6 @@ static int usb_hcd_at91_remove(struct usb_hcd *hcd,
fclk = iclk = hclk = NULL;
dev_set_drvdata(&pdev->dev, NULL);
return 0;
}
/*-------------------------------------------------------------------------*/
@ -309,7 +309,8 @@ static int ohci_hcd_at91_drv_remove(struct platform_device *pdev)
}
device_init_wakeup(&pdev->dev, 0);
return usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
usb_hcd_at91_remove(platform_get_drvdata(pdev), pdev);
return 0;
}
#ifdef CONFIG_PM

View File

@ -34,7 +34,8 @@
#ifdef __LITTLE_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C)
#elif __BIG_ENDIAN
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | USBH_ENABLE_BE)
#define USBH_ENABLE_INIT (USBH_ENABLE_CE | USBH_ENABLE_E | USBH_ENABLE_C | \
USBH_ENABLE_BE)
#else
#error not byte order defined
#endif
@ -46,213 +47,87 @@
#define USB_MCFG_RDCOMB (1<<30)
#define USB_MCFG_SSDEN (1<<23)
#define USB_MCFG_OHCCLKEN (1<<16)
#ifdef CONFIG_DMA_COHERENT
#define USB_MCFG_UCAM (1<<7)
#else
#define USB_MCFG_UCAM (0)
#endif
#define USB_MCFG_OBMEN (1<<1)
#define USB_MCFG_OMEMEN (1<<0)
#define USBH_ENABLE_CE USB_MCFG_OHCCLKEN
#ifdef CONFIG_DMA_COHERENT
#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \
| USB_MCFG_PFEN | USB_MCFG_RDCOMB \
| USB_MCFG_SSDEN | USB_MCFG_UCAM \
| USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
#else
#define USBH_ENABLE_INIT (USB_MCFG_OHCCLKEN \
| USB_MCFG_PFEN | USB_MCFG_RDCOMB \
| USB_MCFG_SSDEN \
| USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
#endif
#define USBH_ENABLE_INIT (USB_MCFG_PFEN | USB_MCFG_RDCOMB | \
USBH_ENABLE_CE | USB_MCFG_SSDEN | \
USB_MCFG_UCAM | \
USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
#define USBH_DISABLE (USB_MCFG_OBMEN | USB_MCFG_OMEMEN)
#endif /* Au1200 */
extern int usb_disabled(void);
/*-------------------------------------------------------------------------*/
static void au1xxx_start_ohc(struct platform_device *dev)
static void au1xxx_start_ohc(void)
{
printk(KERN_DEBUG __FILE__
": starting Au1xxx OHCI USB Controller\n");
/* enable host controller */
#ifndef CONFIG_SOC_AU1200
au_writel(USBH_ENABLE_CE, USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT, USB_HOST_CONFIG);
au_sync();
udelay(1000);
#else /* Au1200 */
/* write HW defaults again in case Yamon cleared them */
if (au_readl(USB_HOST_CONFIG) == 0) {
au_writel(0x00d02000, USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
udelay(1000);
}
au_writel(USBH_ENABLE_CE | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
udelay(1000);
au_writel(USBH_ENABLE_INIT | au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
au_sync();
udelay(1000);
#endif /* Au1200 */
#ifndef CONFIG_SOC_AU1200
/* wait for reset complete (read register twice; see au1500 errata) */
while (au_readl(USB_HOST_CONFIG),
!(au_readl(USB_HOST_CONFIG) & USBH_ENABLE_RD))
#endif
udelay(1000);
printk(KERN_DEBUG __FILE__
": Clock to USB host has been enabled \n");
}
static void au1xxx_stop_ohc(struct platform_device *dev)
{
printk(KERN_DEBUG __FILE__
": stopping Au1xxx OHCI USB Controller\n");
#ifndef CONFIG_SOC_AU1200
/* Disable clock */
au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
#else /* Au1200 */
/* Disable mem */
au_writel(~USBH_DISABLE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_CE, USB_HOST_CONFIG);
au_sync();
udelay(1000);
/* Disable clock */
au_writel(~USBH_ENABLE_CE & au_readl(USB_HOST_CONFIG), USB_HOST_CONFIG);
au_readl(USB_HOST_CONFIG);
au_writel(au_readl(USB_HOST_CONFIG) | USBH_ENABLE_INIT, USB_HOST_CONFIG);
au_sync();
udelay(2000);
#endif /* Au1200 */
}
/*-------------------------------------------------------------------------*/
/* configure so an HC device and id are always provided */
/* always called with process context; sleeping is OK */
/**
* usb_ohci_au1xxx_probe - initialize Au1xxx-based HCDs
* Context: !in_interrupt()
*
* Allocates basic resources for this USB host controller, and
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*
*/
static int usb_ohci_au1xxx_probe(const struct hc_driver *driver,
struct platform_device *dev)
static void au1xxx_stop_ohc(void)
{
int retval;
struct usb_hcd *hcd;
#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
/* Au1200 AB USB does not support coherent memory */
if (!(read_c0_prid() & 0xff)) {
pr_info("%s: this is chip revision AB !!\n",
dev->name);
pr_info("%s: update your board or re-configure the kernel\n",
dev->name);
return -ENODEV;
}
#ifdef CONFIG_SOC_AU1200
/* Disable mem */
au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_DISABLE, USB_HOST_CONFIG);
au_sync();
udelay(1000);
#endif
if (dev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ\n");
return -ENOMEM;
}
hcd = usb_create_hcd(driver, &dev->dev, "au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = dev->resource[0].start;
hcd->rsrc_len = dev->resource[0].end - dev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed\n");
retval = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed\n");
retval = -ENOMEM;
goto err2;
}
au1xxx_start_ohc(dev);
ohci_hcd_init(hcd_to_ohci(hcd));
retval = usb_add_hcd(hcd, dev->resource[1].start, IRQF_DISABLED | IRQF_SHARED);
if (retval == 0)
return retval;
au1xxx_stop_ohc(dev);
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return retval;
/* Disable clock */
au_writel(au_readl(USB_HOST_CONFIG) & ~USBH_ENABLE_CE, USB_HOST_CONFIG);
au_sync();
}
/* may be called without controller electrically present */
/* may be called with controller, bus, and devices active */
/**
* usb_hcd_au1xxx_remove - shutdown processing for Au1xxx-based HCDs
* @dev: USB Host Controller being removed
* Context: !in_interrupt()
*
* Reverses the effect of usb_hcd_au1xxx_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
*
*/
static void usb_ohci_au1xxx_remove(struct usb_hcd *hcd, struct platform_device *dev)
static int __devinit ohci_au1xxx_start(struct usb_hcd *hcd)
{
usb_remove_hcd(hcd);
au1xxx_stop_ohc(dev);
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
}
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
int ret;
/*-------------------------------------------------------------------------*/
ohci_dbg(ohci, "ohci_au1xxx_start, ohci:%p", ohci);
static int __devinit
ohci_au1xxx_start (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
ohci_dbg (ohci, "ohci_au1xxx_start, ohci:%p", ohci);
if ((ret = ohci_init (ohci)) < 0)
if ((ret = ohci_init(ohci)) < 0)
return ret;
if ((ret = ohci_run (ohci)) < 0) {
if ((ret = ohci_run(ohci)) < 0) {
err ("can't start %s", hcd->self.bus_name);
ohci_stop (hcd);
ohci_stop(hcd);
return ret;
}
return 0;
}
/*-------------------------------------------------------------------------*/
static const struct hc_driver ohci_au1xxx_hc_driver = {
.description = hcd_name,
.product_desc = "Au1xxx OHCI",
@ -296,18 +171,66 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
.start_port_reset = ohci_start_port_reset,
};
/*-------------------------------------------------------------------------*/
static int ohci_hcd_au1xxx_drv_probe(struct platform_device *pdev)
{
int ret;
pr_debug ("In ohci_hcd_au1xxx_drv_probe");
struct usb_hcd *hcd;
if (usb_disabled())
return -ENODEV;
ret = usb_ohci_au1xxx_probe(&ohci_au1xxx_hc_driver, pdev);
#if defined(CONFIG_SOC_AU1200) && defined(CONFIG_DMA_COHERENT)
/* Au1200 AB USB does not support coherent memory */
if (!(read_c0_prid() & 0xff)) {
printk(KERN_INFO "%s: this is chip revision AB !!\n",
pdev->name);
printk(KERN_INFO "%s: update your board or re-configure "
"the kernel\n", pdev->name);
return -ENODEV;
}
#endif
if (pdev->resource[1].flags != IORESOURCE_IRQ) {
pr_debug("resource[1] is not IORESOURCE_IRQ\n");
return -ENOMEM;
}
hcd = usb_create_hcd(&ohci_au1xxx_hc_driver, &pdev->dev, "au1xxx");
if (!hcd)
return -ENOMEM;
hcd->rsrc_start = pdev->resource[0].start;
hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
pr_debug("request_mem_region failed\n");
ret = -EBUSY;
goto err1;
}
hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
pr_debug("ioremap failed\n");
ret = -ENOMEM;
goto err2;
}
au1xxx_start_ohc();
ohci_hcd_init(hcd_to_ohci(hcd));
ret = usb_add_hcd(hcd, pdev->resource[1].start,
IRQF_DISABLED | IRQF_SHARED);
if (ret == 0) {
platform_set_drvdata(pdev, hcd);
return ret;
}
au1xxx_stop_ohc();
iounmap(hcd->regs);
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
}
@ -315,30 +238,78 @@ static int ohci_hcd_au1xxx_drv_remove(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_ohci_au1xxx_remove(hcd, pdev);
return 0;
}
/*TBD*/
/*static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
usb_remove_hcd(hcd);
au1xxx_stop_ohc();
iounmap(hcd->regs);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
return 0;
}
static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev)
#ifdef CONFIG_PM
static int ohci_hcd_au1xxx_drv_suspend(struct platform_device *pdev,
pm_message_t message)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
struct usb_hcd *hcd = platform_get_drvdata(pdev);
struct ohci_hcd *ohci = hcd_to_ohci(hcd);
unsigned long flags;
int rc;
rc = 0;
/* Root hub was already suspended. Disable irq emission and
* mark HW unaccessible, bail out if RH has been resumed. Use
* the spinlock to properly synchronize with possible pending
* RH suspend or resume activity.
*
* This is still racy as hcd->state is manipulated outside of
* any locks =P But that will be a different fix.
*/
spin_lock_irqsave(&ohci->lock, flags);
if (hcd->state != HC_STATE_SUSPENDED) {
rc = -EINVAL;
goto bail;
}
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
/* make sure snapshot being resumed re-enumerates everything */
if (message.event == PM_EVENT_PRETHAW)
ohci_usb_reset(ohci);
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
au1xxx_stop_ohc();
bail:
spin_unlock_irqrestore(&ohci->lock, flags);
return rc;
}
static int ohci_hcd_au1xxx_drv_resume(struct platform_device *pdev)
{
struct usb_hcd *hcd = platform_get_drvdata(pdev);
au1xxx_start_ohc();
set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
ohci_finish_controller_resume(hcd);
return 0;
}
*/
#else
#define ohci_hcd_au1xxx_drv_suspend NULL
#define ohci_hcd_au1xxx_drv_resume NULL
#endif
static struct platform_driver ohci_hcd_au1xxx_driver = {
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
.shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_au1xxx_drv_suspend, */
/*.resume = ohci_hcd_au1xxx_drv_resume, */
.suspend = ohci_hcd_au1xxx_drv_suspend,
.resume = ohci_hcd_au1xxx_drv_resume,
.driver = {
.name = "au1xxx-ohci",
.owner = THIS_MODULE,

View File

@ -651,7 +651,7 @@ static ssize_t fill_registers_buffer(struct debug_buffer *buf)
"%s\n"
"%s version " DRIVER_VERSION "\n",
hcd->self.controller->bus->name,
hcd->self.controller->bus_id,
dev_name(hcd->self.controller),
hcd->product_desc,
hcd_name);

View File

@ -974,7 +974,7 @@ MODULE_LICENSE ("GPL");
#define PCI_DRIVER ohci_pci_driver
#endif
#ifdef CONFIG_SA1111
#if defined(CONFIG_ARCH_SA1100) && defined(CONFIG_SA1111)
#include "ohci-sa1111.c"
#define SA1111_DRIVER ohci_hcd_sa1111_driver
#endif

View File

@ -329,7 +329,7 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
}
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err0;

View File

@ -109,8 +109,6 @@ static struct clk *usb_clk;
static int isp1301_probe(struct i2c_adapter *adap);
static int isp1301_detach(struct i2c_client *client);
static int isp1301_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static const unsigned short normal_i2c[] =
{ ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
@ -123,30 +121,37 @@ static struct i2c_client_address_data addr_data = {
};
struct i2c_driver isp1301_driver = {
.class = I2C_CLASS_HWMON,
.driver = {
.name = "isp1301_pnx",
},
.attach_adapter = isp1301_probe,
.detach_client = isp1301_detach,
.command = isp1301_command
};
static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
{
struct i2c_client *c;
int err;
c = kzalloc(sizeof(*c), GFP_KERNEL);
if (!c)
return -ENOMEM;
strcpy(c->name, "isp1301");
strlcpy(c->name, "isp1301_pnx", I2C_NAME_SIZE);
c->flags = 0;
c->addr = addr;
c->adapter = adap;
c->driver = &isp1301_driver;
err = i2c_attach_client(c);
if (err) {
kfree(c);
return err;
}
isp1301_i2c_client = c;
return i2c_attach_client(c);
return 0;
}
static int isp1301_probe(struct i2c_adapter *adap)
@ -161,13 +166,6 @@ static int isp1301_detach(struct i2c_client *client)
return 0;
}
/* No commands defined */
static int isp1301_command(struct i2c_client *client, unsigned int cmd,
void *arg)
{
return 0;
}
static void i2c_write(u8 buf, u8 subaddr)
{
char tmpbuf[2];
@ -389,7 +387,7 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
USB_CLOCK_MASK) ;
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd (driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
err("Failed to allocate HC buffer");
ret = -ENOMEM;

View File

@ -14,8 +14,8 @@
*/
#include <linux/signal.h>
#include <linux/of_platform.h>
#include <asm/of_platform.h>
#include <asm/prom.h>

View File

@ -129,7 +129,7 @@ static int ps3_ohci_probe(struct ps3_system_bus_device *dev)
dev->core.dma_mask = &dummy_mask; /* FIXME: for improper usb code */
hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev->core.bus_id);
hcd = usb_create_hcd(&ps3_ohci_hc_driver, &dev->core, dev_name(&dev->core));
if (!hcd) {
dev_dbg(&dev->core, "%s:%d: usb_create_hcd failed\n", __func__,

View File

@ -159,9 +159,6 @@ static int ed_schedule (struct ohci_hcd *ohci, struct ed *ed)
{
int branch;
if (ohci_to_hcd(ohci)->state == HC_STATE_QUIESCING)
return -EAGAIN;
ed->state = ED_OPER;
ed->ed_prev = NULL;
ed->ed_next = NULL;

View File

@ -143,7 +143,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
goto err2;
}
hcd = usb_create_hcd(driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err2;

View File

@ -113,7 +113,7 @@ static int ssb_ohci_attach(struct ssb_device *dev)
ssb_device_enable(dev, flags);
hcd = usb_create_hcd(&ssb_ohci_hc_driver, dev->dev,
dev->dev->bus_id);
dev_name(dev->dev));
if (!hcd)
goto err_dev_disable;
ohcidev = hcd_to_ssb_ohci(hcd);

View File

@ -312,9 +312,9 @@ static void put_child_connect_map(struct r8a66597 *r8a66597, int address)
static void set_pipe_reg_addr(struct r8a66597_pipe *pipe, u8 dma_ch)
{
u16 pipenum = pipe->info.pipenum;
unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
const unsigned long fifoaddr[] = {D0FIFO, D1FIFO, CFIFO};
const unsigned long fifosel[] = {D0FIFOSEL, D1FIFOSEL, CFIFOSEL};
const unsigned long fifoctr[] = {D0FIFOCTR, D1FIFOCTR, CFIFOCTR};
if (dma_ch > R8A66597_PIPE_NO_DMA) /* dma fifo not use? */
dma_ch = R8A66597_PIPE_NO_DMA;
@ -863,6 +863,32 @@ static void disable_r8a66597_pipe_all(struct r8a66597 *r8a66597,
dev->dma_map = 0;
}
static u16 get_interval(struct urb *urb, __u8 interval)
{
u16 time = 1;
int i;
if (urb->dev->speed == USB_SPEED_HIGH) {
if (interval > IITV)
time = IITV;
else
time = interval ? interval - 1 : 0;
} else {
if (interval > 128) {
time = IITV;
} else {
/* calculate the nearest value for PIPEPERI */
for (i = 0; i < 7; i++) {
if ((1 << i) < interval &&
(1 << (i + 1) > interval))
time = 1 << i;
}
}
}
return time;
}
static unsigned long get_timer_interval(struct urb *urb, __u8 interval)
{
__u8 i;
@ -901,10 +927,7 @@ static void init_pipe_info(struct r8a66597 *r8a66597, struct urb *urb,
info.interval = 0;
info.timer_interval = 0;
} else {
if (ep->bInterval > IITV)
info.interval = IITV;
else
info.interval = ep->bInterval ? ep->bInterval - 1 : 0;
info.interval = get_interval(urb, ep->bInterval);
info.timer_interval = get_timer_interval(urb, ep->bInterval);
}
if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
@ -2244,6 +2267,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
struct r8a66597 *r8a66597;
int ret = 0;
int i;
unsigned long irq_trigger;
if (pdev->dev.dma_mask) {
ret = -EINVAL;
@ -2302,7 +2326,11 @@ static int __init r8a66597_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&r8a66597->child_device);
hcd->rsrc_start = res->start;
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
if (irq_sense == INTL)
irq_trigger = IRQF_TRIGGER_LOW;
else
irq_trigger = IRQF_TRIGGER_FALLING;
ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | irq_trigger);
if (ret != 0) {
err("Failed to add hcd");
goto clean_up;

View File

@ -1674,7 +1674,7 @@ sl811h_probe(struct platform_device *dev)
}
/* allocate and initialize hcd */
hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev->dev.bus_id);
hcd = usb_create_hcd(&sl811h_hc_driver, &dev->dev, dev_name(&dev->dev));
if (!hcd) {
retval = -ENOMEM;
goto err5;

View File

@ -3124,7 +3124,7 @@ static int __devinit u132_probe(struct platform_device *pdev)
if (pdev->dev.dma_mask)
return -EINVAL;
hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, dev_name(&pdev->dev));
if (!hcd) {
printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
);

View File

@ -12,7 +12,7 @@
* (C) Copyright 2004 Alan Stern, stern@rowland.harvard.edu
*/
static __u8 root_hub_hub_des[] =
static const __u8 root_hub_hub_des[] =
{
0x09, /* __u8 bLength; */
0x29, /* __u8 bDescriptorType; Hub-descriptor */

View File

@ -1421,7 +1421,8 @@ ofail: mutex_unlock(&cp->mutex);
/* IOCTL functions */
static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long auerchar_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
pauerchar_t ccp = (pauerchar_t) file->private_data;
int ret = 0;
@ -1452,7 +1453,7 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int
mutex_unlock(&ccp->mutex);
return -ENODEV;
}
lock_kernel();
switch (cmd) {
/* return != 0 if Transmitt channel ready to send */
@ -1547,9 +1548,10 @@ static int auerchar_ioctl (struct inode *inode, struct file *file, unsigned int
default:
dbg ("IOCTL_AU_UNKNOWN");
ret = -ENOIOCTLCMD;
ret = -ENOTTY;
break;
}
unlock_kernel();
/* release the mutexes */
mutex_unlock(&cp->mutex);
mutex_unlock(&ccp->mutex);
@ -1860,7 +1862,7 @@ static const struct file_operations auerswald_fops =
.llseek = no_llseek,
.read = auerchar_read,
.write = auerchar_write,
.ioctl = auerchar_ioctl,
.unlocked_ioctl = auerchar_ioctl,
.open = auerchar_open,
.release = auerchar_release,
};

View File

@ -6,8 +6,6 @@
* This program 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, version 2.
*
* $Id: emi62.c,v 1.15 2002/04/23 06:13:59 tapio Exp $
*/
#include <linux/kernel.h>
#include <linux/errno.h>

View File

@ -656,29 +656,6 @@ static int ftdi_elan_release(struct inode *inode, struct file *file)
}
#define FTDI_ELAN_IOC_MAGIC 0xA1
#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132)
static int ftdi_elan_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case FTDI_ELAN_IOCDEBUG:{
char line[132];
int size = strncpy_from_user(line,
(const char __user *)arg, sizeof(line));
if (size < 0) {
return -EINVAL;
} else {
printk(KERN_ERR "TODO: ioctl %s\n", line);
return 0;
}
}
default:
return -EFAULT;
}
}
/*
*
* blocking bulk reads are used to get data from the device
@ -1222,7 +1199,6 @@ error_1:
static const struct file_operations ftdi_elan_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.ioctl = ftdi_elan_ioctl,
.read = ftdi_elan_read,
.write = ftdi_elan_write,
.open = ftdi_elan_open,

View File

@ -474,8 +474,8 @@ exit:
/**
* iowarrior_ioctl
*/
static int iowarrior_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
static long iowarrior_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
struct iowarrior *dev = NULL;
__u8 *buffer;
@ -493,6 +493,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file,
return -ENOMEM;
/* lock this object */
lock_kernel();
mutex_lock(&dev->mutex);
/* verify that the device wasn't unplugged */
@ -584,6 +585,7 @@ static int iowarrior_ioctl(struct inode *inode, struct file *file,
error_out:
/* unlock the device */
mutex_unlock(&dev->mutex);
unlock_kernel();
kfree(buffer);
return retval;
}
@ -719,7 +721,7 @@ static const struct file_operations iowarrior_fops = {
.owner = THIS_MODULE,
.write = iowarrior_write,
.read = iowarrior_read,
.ioctl = iowarrior_ioctl,
.unlocked_ioctl = iowarrior_ioctl,
.open = iowarrior_open,
.release = iowarrior_release,
.poll = iowarrior_poll,

View File

@ -104,9 +104,7 @@ static int close_rio(struct inode *inode, struct file *file)
return 0;
}
static int
ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
static long ioctl_rio(struct file *file, unsigned int cmd, unsigned long arg)
{
struct RioCommand rio_cmd;
struct rio_usb_data *rio = &rio_instance;
@ -116,6 +114,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
int retries;
int retval=0;
lock_kernel();
mutex_lock(&(rio->lock));
/* Sanity check to make sure rio is connected, powered, etc */
if (rio->present == 0 || rio->rio_dev == NULL) {
@ -254,6 +253,7 @@ ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd,
err_out:
mutex_unlock(&(rio->lock));
unlock_kernel();
return retval;
}
@ -433,7 +433,7 @@ file_operations usb_rio_fops = {
.owner = THIS_MODULE,
.read = read_rio,
.write = write_rio,
.ioctl = ioctl_rio,
.unlocked_ioctl = ioctl_rio,
.open = open_rio,
.release = close_rio,
};

View File

@ -2982,9 +2982,8 @@ sisusb_handle_command(struct sisusb_usb_data *sisusb, struct sisusb_command *y,
return retval;
}
static int
sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
static long
sisusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct sisusb_usb_data *sisusb;
struct sisusb_info x;
@ -2995,6 +2994,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
return -ENODEV;
lock_kernel();
mutex_lock(&sisusb->lock);
/* Sanity check */
@ -3053,6 +3053,7 @@ sisusb_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
err_out:
mutex_unlock(&sisusb->lock);
unlock_kernel();
return retval;
}
@ -3066,9 +3067,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
case SISUSB_GET_CONFIG_SIZE:
case SISUSB_GET_CONFIG:
case SISUSB_COMMAND:
lock_kernel();
retval = sisusb_ioctl(f->f_path.dentry->d_inode, f, cmd, arg);
unlock_kernel();
retval = sisusb_ioctl(f, cmd, arg);
return retval;
default:
@ -3087,7 +3086,7 @@ static const struct file_operations usb_sisusb_fops = {
#ifdef SISUSB_NEW_CONFIG_COMPAT
.compat_ioctl = sisusb_compat_ioctl,
#endif
.ioctl = sisusb_ioctl
.unlocked_ioctl = sisusb_ioctl
};
static struct usb_class_driver usb_sisusb_class = {

View File

@ -146,7 +146,7 @@ static ssize_t lcd_read(struct file *file, char __user * buffer, size_t count, l
return retval;
}
static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
static long lcd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct usb_lcd *dev;
u16 bcdDevice;
@ -158,12 +158,14 @@ static int lcd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
switch (cmd) {
case IOCTL_GET_HARD_VERSION:
lock_kernel();
bcdDevice = le16_to_cpu((dev->udev)->descriptor.bcdDevice);
sprintf(buf,"%1d%1d.%1d%1d",
(bcdDevice & 0xF000)>>12,
(bcdDevice & 0xF00)>>8,
(bcdDevice & 0xF0)>>4,
(bcdDevice & 0xF));
unlock_kernel();
if (copy_to_user((void __user *)arg,buf,strlen(buf))!=0)
return -EFAULT;
break;
@ -272,7 +274,7 @@ static const struct file_operations lcd_fops = {
.read = lcd_read,
.write = lcd_write,
.open = lcd_open,
.ioctl = lcd_ioctl,
.unlocked_ioctl = lcd_ioctl,
.release = lcd_release,
};

View File

@ -9,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/usb.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include "usb_mon.h"
@ -42,19 +43,8 @@ static ssize_t mon_stat_read(struct file *file, char __user *buf,
size_t nbytes, loff_t *ppos)
{
struct snap *sp = file->private_data;
loff_t pos = *ppos;
int cnt;
if (pos < 0 || pos >= sp->slen)
return 0;
if (nbytes == 0)
return 0;
if ((cnt = sp->slen - pos) > nbytes)
cnt = nbytes;
if (copy_to_user(buf, sp->str + pos, cnt))
return -EFAULT;
*ppos = pos + cnt;
return cnt;
return simple_read_from_buffer(buf, nbytes, ppos, sp->str, sp->slen);
}
static int mon_stat_release(struct inode *inode, struct file *file)

View File

@ -64,14 +64,6 @@ config USB_SERIAL_AIRCABLE
To compile this driver as a module, choose M here: the module
will be called aircable.
config USB_SERIAL_AIRPRIME
tristate "USB AirPrime CDMA Wireless Driver"
help
Say Y here if you want to use a AirPrime CDMA Wireless PC card.
To compile this driver as a module, choose M here: the
module will be called airprime.
config USB_SERIAL_ARK3116
tristate "USB ARK Micro 3116 USB Serial Driver"
help

View File

@ -12,7 +12,6 @@ usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o
usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y)
obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o
obj-$(CONFIG_USB_SERIAL_CH341) += ch341.o

View File

@ -1,353 +0,0 @@
/*
* AirPrime CDMA Wireless Serial USB driver
*
* Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
#define URB_TRANSFER_BUFFER_SIZE 4096
#define NUM_READ_URBS 4
#define NUM_WRITE_URBS 4
#define NUM_BULK_EPS 3
#define MAX_BULK_EPS 6
/* if overridden by the user, then use their value for the size of the
* read and write urbs, and the number of endpoints */
static int buffer_size = URB_TRANSFER_BUFFER_SIZE;
static int endpoints = NUM_BULK_EPS;
static int debug;
struct airprime_private {
spinlock_t lock;
int outstanding_urbs;
int throttled;
struct urb *read_urbp[NUM_READ_URBS];
/* Settings for the port */
int rts_state; /* Handshaking pins (outputs) */
int dtr_state;
int cts_state; /* Handshaking pins (inputs) */
int dsr_state;
int dcd_state;
int ri_state;
};
static int airprime_send_setup(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct airprime_private *priv;
dbg("%s", __func__);
if (port->number != 0)
return 0;
priv = usb_get_serial_port_data(port);
if (port->tty) {
int val = 0;
if (priv->dtr_state)
val |= 0x01;
if (priv->rts_state)
val |= 0x02;
return usb_control_msg(serial->dev,
usb_rcvctrlpipe(serial->dev, 0),
0x22, 0x21, val, 0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
}
return 0;
}
static void airprime_read_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
unsigned char *data = urb->transfer_buffer;
struct tty_struct *tty;
int result;
int status = urb->status;
dbg("%s - port %d", __func__, port->number);
if (status) {
dbg("%s - nonzero read bulk status received: %d",
__func__, status);
return;
}
usb_serial_debug_data(debug, &port->dev, __func__,
urb->actual_length, data);
tty = port->tty;
if (tty && urb->actual_length) {
tty_insert_flip_string(tty, data, urb->actual_length);
tty_flip_buffer_push(tty);
}
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__func__, result);
return;
}
static void airprime_write_bulk_callback(struct urb *urb)
{
struct usb_serial_port *port = urb->context;
struct airprime_private *priv = usb_get_serial_port_data(port);
int status = urb->status;
unsigned long flags;
dbg("%s - port %d", __func__, port->number);
/* free up the transfer buffer, as usb_free_urb() does not do this */
kfree(urb->transfer_buffer);
if (status)
dbg("%s - nonzero write bulk status received: %d",
__func__, status);
spin_lock_irqsave(&priv->lock, flags);
--priv->outstanding_urbs;
spin_unlock_irqrestore(&priv->lock, flags);
usb_serial_port_softint(port);
}
static int airprime_open(struct usb_serial_port *port, struct file *filp)
{
struct airprime_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
struct urb *urb;
char *buffer = NULL;
int i;
int result = 0;
dbg("%s - port %d", __func__, port->number);
/* initialize our private data structure if it isn't already created */
if (!priv) {
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
result = -ENOMEM;
goto out;
}
spin_lock_init(&priv->lock);
usb_set_serial_port_data(port, priv);
}
/* Set some sane defaults */
priv->rts_state = 1;
priv->dtr_state = 1;
for (i = 0; i < NUM_READ_URBS; ++i) {
buffer = kmalloc(buffer_size, GFP_KERNEL);
if (!buffer) {
dev_err(&port->dev, "%s - out of memory.\n",
__func__);
result = -ENOMEM;
goto errout;
}
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
kfree(buffer);
dev_err(&port->dev, "%s - no more urbs?\n",
__func__);
result = -ENOMEM;
goto errout;
}
usb_fill_bulk_urb(urb, serial->dev,
usb_rcvbulkpipe(serial->dev,
port->bulk_out_endpointAddress),
buffer, buffer_size,
airprime_read_bulk_callback, port);
result = usb_submit_urb(urb, GFP_KERNEL);
if (result) {
usb_free_urb(urb);
kfree(buffer);
dev_err(&port->dev,
"%s - failed submitting read urb %d for port %d, error %d\n",
__func__, i, port->number, result);
goto errout;
}
/* remember this urb so we can kill it when the
port is closed */
priv->read_urbp[i] = urb;
}
airprime_send_setup(port);
goto out;
errout:
/* some error happened, cancel any submitted urbs and clean up
anything that got allocated successfully */
while (i-- != 0) {
urb = priv->read_urbp[i];
buffer = urb->transfer_buffer;
usb_kill_urb(urb);
usb_free_urb(urb);
kfree(buffer);
}
out:
return result;
}
static void airprime_close(struct usb_serial_port *port, struct file *filp)
{
struct airprime_private *priv = usb_get_serial_port_data(port);
int i;
dbg("%s - port %d", __func__, port->number);
priv->rts_state = 0;
priv->dtr_state = 0;
mutex_lock(&port->serial->disc_mutex);
if (!port->serial->disconnected)
airprime_send_setup(port);
mutex_unlock(&port->serial->disc_mutex);
for (i = 0; i < NUM_READ_URBS; ++i) {
usb_kill_urb(priv->read_urbp[i]);
kfree(priv->read_urbp[i]->transfer_buffer);
usb_free_urb(priv->read_urbp[i]);
}
/* free up private structure */
kfree(priv);
usb_set_serial_port_data(port, NULL);
}
static int airprime_write(struct usb_serial_port *port,
const unsigned char *buf, int count)
{
struct airprime_private *priv = usb_get_serial_port_data(port);
struct usb_serial *serial = port->serial;
struct urb *urb;
unsigned char *buffer;
unsigned long flags;
int status;
dbg("%s - port %d", __func__, port->number);
spin_lock_irqsave(&priv->lock, flags);
if (priv->outstanding_urbs > NUM_WRITE_URBS) {
spin_unlock_irqrestore(&priv->lock, flags);
dbg("%s - write limit hit\n", __func__);
return 0;
}
spin_unlock_irqrestore(&priv->lock, flags);
buffer = kmalloc(count, GFP_ATOMIC);
if (!buffer) {
dev_err(&port->dev, "out of memory\n");
return -ENOMEM;
}
urb = usb_alloc_urb(0, GFP_ATOMIC);
if (!urb) {
dev_err(&port->dev, "no more free urbs\n");
kfree(buffer);
return -ENOMEM;
}
memcpy(buffer, buf, count);
usb_serial_debug_data(debug, &port->dev, __func__, count, buffer);
usb_fill_bulk_urb(urb, serial->dev,
usb_sndbulkpipe(serial->dev,
port->bulk_out_endpointAddress),
buffer, count,
airprime_write_bulk_callback, port);
/* send it down the pipe */
status = usb_submit_urb(urb, GFP_ATOMIC);
if (status) {
dev_err(&port->dev,
"%s - usb_submit_urb(write bulk) failed with status = %d\n",
__func__, status);
count = status;
kfree(buffer);
} else {
spin_lock_irqsave(&priv->lock, flags);
++priv->outstanding_urbs;
spin_unlock_irqrestore(&priv->lock, flags);
}
/* we are done with this urb, so let the host driver
* really free it when it is finished with it */
usb_free_urb(urb);
return count;
}
static struct usb_driver airprime_driver = {
.name = "airprime",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
.no_dynamic_id = 1,
};
static struct usb_serial_driver airprime_device = {
.driver = {
.owner = THIS_MODULE,
.name = "airprime",
},
.usb_driver = &airprime_driver,
.id_table = id_table,
.open = airprime_open,
.close = airprime_close,
.write = airprime_write,
};
static int __init airprime_init(void)
{
int retval;
airprime_device.num_ports = endpoints;
if (endpoints < 0 || endpoints >= MAX_BULK_EPS)
airprime_device.num_ports = NUM_BULK_EPS;
retval = usb_serial_register(&airprime_device);
if (retval)
return retval;
retval = usb_register(&airprime_driver);
if (retval)
usb_serial_deregister(&airprime_device);
return retval;
}
static void __exit airprime_exit(void)
{
dbg("%s", __func__);
usb_deregister(&airprime_driver);
usb_serial_deregister(&airprime_device);
}
module_init(airprime_init);
module_exit(airprime_exit);
MODULE_LICENSE("GPL");
module_param(debug, bool, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug enabled");
module_param(buffer_size, int, 0);
MODULE_PARM_DESC(buffer_size,
"Size of the transfer buffers in bytes (default 4096)");
module_param(endpoints, int, 0);
MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)");

Some files were not shown because too many files have changed in this diff Show More