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: (123 commits)
  wimax/i2400m: add CREDITS and MAINTAINERS entries
  wimax: export linux/wimax.h and linux/wimax/i2400m.h with headers_install
  i2400m: Makefile and Kconfig
  i2400m/SDIO: TX and RX path backends
  i2400m/SDIO: firmware upload backend
  i2400m/SDIO: probe/disconnect, dev init/shutdown and reset backends
  i2400m/SDIO: header for the SDIO subdriver
  i2400m/USB: TX and RX path backends
  i2400m/USB: firmware upload backend
  i2400m/USB: probe/disconnect, dev init/shutdown and reset backends
  i2400m/USB: header for the USB bus driver
  i2400m: debugfs controls
  i2400m: various functions for device management
  i2400m: RX and TX data/control paths
  i2400m: firmware loading and bootrom initialization
  i2400m: linkage to the networking stack
  i2400m: Generic probe/disconnect, reset and message passing
  i2400m: host/device procotol and core driver definitions
  i2400m: documentation and instructions for usage
  wimax: Makefile, Kconfig and docbook linkage for the stack
  ...
This commit is contained in:
Linus Torvalds 2009-01-07 15:37:24 -08:00
commit 7c7758f99d
186 changed files with 28290 additions and 1398 deletions

17
CREDITS
View file

@ -464,6 +464,11 @@ S: 1200 Goldenrod Dr.
S: Nampa, Idaho 83686
S: USA
N: Dirk J. Brandewie
E: dirk.j.brandewie@intel.com
E: linux-wimax@intel.com
D: Intel Wireless WiMAX Connection 2400 SDIO driver
N: Derrick J. Brashear
E: shadow@dementia.org
W: http://www.dementia.org/~shadow
@ -2119,6 +2124,11 @@ N: H.J. Lu
E: hjl@gnu.ai.mit.edu
D: GCC + libraries hacker
N: Yanir Lubetkin
E: yanirx.lubatkin@intel.com
E: linux-wimax@intel.com
D: Intel Wireless WiMAX Connection 2400 driver
N: Michal Ludvig
E: michal@logix.cz
E: michal.ludvig@asterisk.co.nz
@ -2693,6 +2703,13 @@ S: RR #5, 497 Pole Line Road
S: Thunder Bay, Ontario
S: CANADA P7C 5M9
N: Inaky Perez-Gonzalez
E: inaky.perez-gonzalez@intel.com
E: linux-wimax@intel.com
E: inakypg@yahoo.com
D: WiMAX stack
D: Intel Wireless WiMAX Connection 2400 driver
N: Yuri Per
E: yuri@pts.mipt.ru
D: Some smbfs fixes

View file

@ -74,6 +74,14 @@
!Enet/sunrpc/rpcb_clnt.c
!Enet/sunrpc/clnt.c
</sect1>
<sect1><title>WiMAX</title>
!Enet/wimax/op-msg.c
!Enet/wimax/op-reset.c
!Enet/wimax/op-rfkill.c
!Enet/wimax/stack.c
!Iinclude/net/wimax.h
!Iinclude/linux/wimax.h
</sect1>
</chapter>
<chapter id="netdev">

View file

@ -91,6 +91,7 @@ parameter is applicable:
SUSPEND System suspend states are enabled.
FTRACE Function tracing enabled.
TS Appropriate touchscreen support is enabled.
UMS USB Mass Storage support is enabled.
USB USB support is enabled.
USBHID USB Human Interface Device support is enabled.
V4L Video For Linux support is enabled.
@ -2383,6 +2384,41 @@ and is between 256 and 4096 characters. It is defined in the file
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
usb-storage.delay_use=
[UMS] The delay in seconds before a new device is
scanned for Logical Units (default 5).
usb-storage.quirks=
[UMS] A list of quirks entries to supplement or
override the built-in unusual_devs list. List
entries are separated by commas. Each entry has
the form VID:PID:Flags where VID and PID are Vendor
and Product ID values (4-digit hex numbers) and
Flags is a set of characters, each corresponding
to a common usb-storage quirk flag as follows:
a = SANE_SENSE (collect more than 18 bytes
of sense data);
c = FIX_CAPACITY (decrease the reported
device capacity by one sector);
h = CAPACITY_HEURISTICS (decrease the
reported device capacity by one
sector if the number is odd);
i = IGNORE_DEVICE (don't bind to this
device);
l = NOT_LOCKABLE (don't try to lock and
unlock ejectable media);
m = MAX_SECTORS_64 (don't transfer more
than 64 sectors = 32 KB at a time);
o = CAPACITY_OK (accept the capacity
reported by the device);
r = IGNORE_RESIDUE (the device reports
bogus residue values);
s = SINGLE_LUN (the device has only one
Logical Unit);
w = NO_WP_DETECT (don't test whether the
medium is write-protected).
Example: quirks=0419:aaf5:rl,0421:0433:rc
add_efi_memmap [EFI; x86-32,X86-64] Include EFI memory map in
kernel's map of available physical RAM.

View file

@ -313,11 +313,13 @@ three of the methods listed above. In addition, a driver indicates
that it supports autosuspend by setting the .supports_autosuspend flag
in its usb_driver structure. It is then responsible for informing the
USB core whenever one of its interfaces becomes busy or idle. The
driver does so by calling these three functions:
driver does so by calling these five functions:
int usb_autopm_get_interface(struct usb_interface *intf);
void usb_autopm_put_interface(struct usb_interface *intf);
int usb_autopm_set_interface(struct usb_interface *intf);
int usb_autopm_get_interface_async(struct usb_interface *intf);
void usb_autopm_put_interface_async(struct usb_interface *intf);
The functions work by maintaining a counter in the usb_interface
structure. When intf->pm_usage_count is > 0 then the interface is
@ -330,10 +332,12 @@ associated with the device itself rather than any of its interfaces.
This field is used only by the USB core.)
The driver owns intf->pm_usage_count; it can modify the value however
and whenever it likes. A nice aspect of the usb_autopm_* routines is
that the changes they make are protected by the usb_device structure's
PM mutex (udev->pm_mutex); however drivers may change pm_usage_count
without holding the mutex.
and whenever it likes. A nice aspect of the non-async usb_autopm_*
routines is that the changes they make are protected by the usb_device
structure's PM mutex (udev->pm_mutex); however drivers may change
pm_usage_count without holding the mutex. Drivers using the async
routines are responsible for their own synchronization and mutual
exclusion.
usb_autopm_get_interface() increments pm_usage_count and
attempts an autoresume if the new value is > 0 and the
@ -348,6 +352,14 @@ without holding the mutex.
is suspended, and it attempts an autosuspend if the value is
<= 0 and the device isn't suspended.
usb_autopm_get_interface_async() and
usb_autopm_put_interface_async() do almost the same things as
their non-async counterparts. The differences are: they do
not acquire the PM mutex, and they use a workqueue to do their
jobs. As a result they can be called in an atomic context,
such as an URB's completion handler, but when they return the
device will not generally not yet be in the desired state.
There also are a couple of utility routines drivers can use:
usb_autopm_enable() sets pm_usage_cnt to 0 and then calls

View file

@ -0,0 +1,260 @@
Driver for the Intel Wireless Wimax Connection 2400m
(C) 2008 Intel Corporation < linux-wimax@intel.com >
This provides a driver for the Intel Wireless WiMAX Connection 2400m
and a basic Linux kernel WiMAX stack.
1. Requirements
* Linux installation with Linux kernel 2.6.22 or newer (if building
from a separate tree)
* Intel i2400m Echo Peak or Baxter Peak; this includes the Intel
Wireless WiMAX/WiFi Link 5x50 series.
* build tools:
+ Linux kernel development package for the target kernel; to
build against your currently running kernel, you need to have
the kernel development package corresponding to the running
image installed (usually if your kernel is named
linux-VERSION, the development package is called
linux-dev-VERSION or linux-headers-VERSION).
+ GNU C Compiler, make
2. Compilation and installation
2.1. Compilation of the drivers included in the kernel
Configure the kernel; to enable the WiMAX drivers select Drivers >
Networking Drivers > WiMAX device support. Enable all of them as
modules (easier).
If USB or SDIO are not enabled in the kernel configuration, the options
to build the i2400m USB or SDIO drivers will not show. Enable said
subsystems and go back to the WiMAX menu to enable the drivers.
Compile and install your kernel as usual.
2.2. Compilation of the drivers distributed as an standalone module
To compile
$ cd source/directory
$ make
Once built you can load and unload using the provided load.sh script;
load.sh will load the modules, load.sh u will unload them.
To install in the default kernel directories (and enable auto loading
when the device is plugged):
$ make install
$ depmod -a
If your kernel development files are located in a non standard
directory or if you want to build for a kernel that is not the
currently running one, set KDIR to the right location:
$ make KDIR=/path/to/kernel/dev/tree
For more information, please contact linux-wimax@intel.com.
3. Installing the firmware
The firmware can be obtained from http://linuxwimax.org or might have
been supplied with your hardware.
It has to be installed in the target system:
*
$ cp FIRMWAREFILE.sbcf /lib/firmware/i2400m-fw-BUSTYPE-1.3.sbcf
* NOTE: if your firmware came in an .rpm or .deb file, just install
it as normal, with the rpm (rpm -i FIRMWARE.rpm) or dpkg
(dpkg -i FIRMWARE.deb) commands. No further action is needed.
* BUSTYPE will be usb or sdio, depending on the hardware you have.
Each hardware type comes with its own firmware and will not work
with other types.
4. Design
This package contains two major parts: a WiMAX kernel stack and a
driver for the Intel i2400m.
The WiMAX stack is designed to provide for common WiMAX control
services to current and future WiMAX devices from any vendor; please
see README.wimax for details.
The i2400m kernel driver is broken up in two main parts: the bus
generic driver and the bus-specific drivers. The bus generic driver
forms the drivercore and contain no knowledge of the actual method we
use to connect to the device. The bus specific drivers are just the
glue to connect the bus-generic driver and the device. Currently only
USB and SDIO are supported. See drivers/net/wimax/i2400m/i2400m.h for
more information.
The bus generic driver is logically broken up in two parts: OS-glue and
hardware-glue. The OS-glue interfaces with Linux. The hardware-glue
interfaces with the device on using an interface provided by the
bus-specific driver. The reason for this breakup is to be able to
easily reuse the hardware-glue to write drivers for other OSes; note
the hardware glue part is written as a native Linux driver; no
abstraction layers are used, so to port to another OS, the Linux kernel
API calls should be replaced with the target OS's.
5. Usage
To load the driver, follow the instructions in the install section;
once the driver is loaded, plug in the device (unless it is permanently
plugged in). The driver will enumerate the device, upload the firmware
and output messages in the kernel log (dmesg, /var/log/messages or
/var/log/kern.log) such as:
...
i2400m_usb 5-4:1.0: firmware interface version 8.0.0
i2400m_usb 5-4:1.0: WiMAX interface wmx0 (00:1d:e1:01:94:2c) ready
At this point the device is ready to work.
Current versions require the Intel WiMAX Network Service in userspace
to make things work. See the network service's README for instructions
on how to scan, connect and disconnect.
5.1. Module parameters
Module parameters can be set at kernel or module load time or by
echoing values:
$ echo VALUE > /sys/module/MODULENAME/parameters/PARAMETERNAME
To make changes permanent, for example, for the i2400m module, you can
also create a file named /etc/modprobe.d/i2400m containing:
options i2400m idle_mode_disabled=1
To find which parameters are supported by a module, run:
$ modinfo path/to/module.ko
During kernel bootup (if the driver is linked in the kernel), specify
the following to the kernel command line:
i2400m.PARAMETER=VALUE
5.1.1. i2400m: idle_mode_disabled
The i2400m module supports a parameter to disable idle mode. This
parameter, once set, will take effect only when the device is
reinitialized by the driver (eg: following a reset or a reconnect).
5.2. Debug operations: debugfs entries
The driver will register debugfs entries that allow the user to tweak
debug settings. There are three main container directories where
entries are placed, which correspond to the three blocks a i2400m WiMAX
driver has:
* /sys/kernel/debug/wimax:DEVNAME/ for the generic WiMAX stack
controls
* /sys/kernel/debug/wimax:DEVNAME/i2400m for the i2400m generic
driver controls
* /sys/kernel/debug/wimax:DEVNAME/i2400m-usb (or -sdio) for the
bus-specific i2400m-usb or i2400m-sdio controls).
Of course, if debugfs is mounted in a directory other than
/sys/kernel/debug, those paths will change.
5.2.1. Increasing debug output
The files named *dl_* indicate knobs for controlling the debug output
of different submodules:
*
# find /sys/kernel/debug/wimax\:wmx0 -name \*dl_\*
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_tx
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_rx
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_notif
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_fw
/sys/kernel/debug/wimax:wmx0/i2400m-usb/dl_usb
/sys/kernel/debug/wimax:wmx0/i2400m/dl_tx
/sys/kernel/debug/wimax:wmx0/i2400m/dl_rx
/sys/kernel/debug/wimax:wmx0/i2400m/dl_rfkill
/sys/kernel/debug/wimax:wmx0/i2400m/dl_netdev
/sys/kernel/debug/wimax:wmx0/i2400m/dl_fw
/sys/kernel/debug/wimax:wmx0/i2400m/dl_debugfs
/sys/kernel/debug/wimax:wmx0/i2400m/dl_driver
/sys/kernel/debug/wimax:wmx0/i2400m/dl_control
/sys/kernel/debug/wimax:wmx0/wimax_dl_stack
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_rfkill
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_reset
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_msg
/sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
/sys/kernel/debug/wimax:wmx0/wimax_dl_debugfs
By reading the file you can obtain the current value of said debug
level; by writing to it, you can set it.
To increase the debug level of, for example, the i2400m's generic TX
engine, just write:
$ echo 3 > /sys/kernel/debug/wimax:wmx0/i2400m/dl_tx
Increasing numbers yield increasing debug information; for details of
what is printed and the available levels, check the source. The code
uses 0 for disabled and increasing values until 8.
5.2.2. RX and TX statistics
The i2400m/rx_stats and i2400m/tx_stats provide statistics about the
data reception/delivery from the device:
$ cat /sys/kernel/debug/wimax:wmx0/i2400m/rx_stats
45 1 3 34 3104 48 480
The numbers reported are
* packets/RX-buffer: total, min, max
* RX-buffers: total RX buffers received, accumulated RX buffer size
in bytes, min size received, max size received
Thus, to find the average buffer size received, divide accumulated
RX-buffer / total RX-buffers.
To clear the statistics back to 0, write anything to the rx_stats file:
$ echo 1 > /sys/kernel/debug/wimax:wmx0/i2400m_rx_stats
Likewise for TX.
Note the packets this debug file refers to are not network packet, but
packets in the sense of the device-specific protocol for communication
to the host. See drivers/net/wimax/i2400m/tx.c.
5.2.3. Tracing messages received from user space
To echo messages received from user space into the trace pipe that the
i2400m driver creates, set the debug file i2400m/trace_msg_from_user to
1:
*
$ echo 1 > /sys/kernel/debug/wimax:wmx0/i2400m/trace_msg_from_user
5.2.4. Performing a device reset
By writing a 0, a 1 or a 2 to the file
/sys/kernel/debug/wimax:wmx0/reset, the driver performs a warm (without
disconnecting from the bus), cold (disconnecting from the bus) or bus
(bus specific) reset on the device.
5.2.5. Asking the device to enter power saving mode
By writing any value to the /sys/kernel/debug/wimax:wmx0 file, the
device will attempt to enter power saving mode.
6. Troubleshooting
6.1. Driver complains about 'i2400m-fw-usb-1.2.sbcf: request failed'
If upon connecting the device, the following is output in the kernel
log:
i2400m_usb 5-4:1.0: fw i2400m-fw-usb-1.3.sbcf: request failed: -2
This means that the driver cannot locate the firmware file named
/lib/firmware/i2400m-fw-usb-1.2.sbcf. Check that the file is present in
the right location.

View file

@ -0,0 +1,81 @@
Linux kernel WiMAX stack
(C) 2008 Intel Corporation < linux-wimax@intel.com >
This provides a basic Linux kernel WiMAX stack to provide a common
control API for WiMAX devices, usable from kernel and user space.
1. Design
The WiMAX stack is designed to provide for common WiMAX control
services to current and future WiMAX devices from any vendor.
Because currently there is only one and we don't know what would be the
common services, the APIs it currently provides are very minimal.
However, it is done in such a way that it is easily extensible to
accommodate future requirements.
The stack works by embedding a struct wimax_dev in your device's
control structures. This provides a set of callbacks that the WiMAX
stack will call in order to implement control operations requested by
the user. As well, the stack provides API functions that the driver
calls to notify about changes of state in the device.
The stack exports the API calls needed to control the device to user
space using generic netlink as a marshalling mechanism. You can access
them using your own code or use the wrappers provided for your
convenience in libwimax (in the wimax-tools package).
For detailed information on the stack, please see
include/linux/wimax.h.
2. Usage
For usage in a driver (registration, API, etc) please refer to the
instructions in the header file include/linux/wimax.h.
When a device is registered with the WiMAX stack, a set of debugfs
files will appear in /sys/kernel/debug/wimax:wmxX can tweak for
control.
2.1. Obtaining debug information: debugfs entries
The WiMAX stack is compiled, by default, with debug messages that can
be used to diagnose issues. By default, said messages are disabled.
The drivers will register debugfs entries that allow the user to tweak
debug settings.
Each driver, when registering with the stack, will cause a debugfs
directory named wimax:DEVICENAME to be created; optionally, it might
create more subentries below it.
2.1.1. Increasing debug output
The files named *dl_* indicate knobs for controlling the debug output
of different submodules of the WiMAX stack:
*
# find /sys/kernel/debug/wimax\:wmx0 -name \*dl_\*
/sys/kernel/debug/wimax:wmx0/wimax_dl_stack
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_rfkill
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_reset
/sys/kernel/debug/wimax:wmx0/wimax_dl_op_msg
/sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
/sys/kernel/debug/wimax:wmx0/wimax_dl_debugfs
/sys/kernel/debug/wimax:wmx0/.... # other driver specific files
NOTE: Of course, if debugfs is mounted in a directory other than
/sys/kernel/debug, those paths will change.
By reading the file you can obtain the current value of said debug
level; by writing to it, you can set it.
To increase the debug level of, for example, the id-table submodule,
just write:
$ echo 3 > /sys/kernel/debug/wimax:wmx0/wimax_dl_id_table
Increasing numbers yield increasing debug information; for details of
what is printed and the available levels, check the source. The code
uses 0 for disabled and increasing values until 8.

View file

@ -2305,6 +2305,14 @@ W: http://lists.sourceforge.net/mailman/listinfo/ipw2100-devel
W: http://ipw2200.sourceforge.net
S: Supported
INTEL WIRELESS WIMAX CONNECTION 2400
P: Inaky Perez-Gonzalez
M: inaky.perez-gonzalez@intel.com
M: linux-wimax@intel.com
L: wimax@linuxwimax.org
S: Supported
W: http://linuxwimax.org
INTEL WIRELESS WIFI LINK (iwlwifi)
P: Zhu Yi
M: yi.zhu@intel.com
@ -2982,6 +2990,7 @@ MUSB MULTIPOINT HIGH SPEED DUAL-ROLE CONTROLLER
P: Felipe Balbi
M: felipe.balbi@nokia.com
L: linux-usb@vger.kernel.org
T: git gitorious.org:/musb/mainline.git
S: Maintained
MYRICOM MYRI-10G 10GbE DRIVER (MYRI10GE)
@ -4733,6 +4742,14 @@ M: zaga@fly.cc.fer.hr
L: linux-scsi@vger.kernel.org
S: Maintained
WIMAX STACK
P: Inaky Perez-Gonzalez
M: inaky.perez-gonzalez@intel.com
M: linux-wimax@intel.com
L: wimax@linuxwimax.org
S: Supported
W: http://linuxwimax.org
WIMEDIA LLC PROTOCOL (WLP) SUBSYSTEM
P: David Vrabel
M: david.vrabel@csr.com

View file

@ -0,0 +1,23 @@
/*
* Copyright (C) 2008 Darius Augulis <augulis.darius@gmail.com>
*
* 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.
*/
#ifndef __ASM_ARCH_MXC_USB
#define __ASM_ARCH_MXC_USB
struct imxusb_platform_data {
int (*init)(struct device *);
int (*exit)(struct device *);
};
#endif /* __ASM_ARCH_MXC_USB */

View file

@ -77,38 +77,6 @@
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_ARCH_OMAP_OTG) || defined(CONFIG_USB_MUSB_OTG)
static struct otg_transceiver *xceiv;
/**
* otg_get_transceiver - find the (single) OTG transceiver driver
*
* Returns the transceiver driver, after getting a refcount to it; or
* null if there is no such transceiver. The caller is responsible for
* releasing that count.
*/
struct otg_transceiver *otg_get_transceiver(void)
{
if (xceiv)
get_device(xceiv->dev);
return xceiv;
}
EXPORT_SYMBOL(otg_get_transceiver);
int otg_set_transceiver(struct otg_transceiver *x)
{
if (xceiv && x)
return -EBUSY;
xceiv = x;
return 0;
}
EXPORT_SYMBOL(otg_set_transceiver);
#endif
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_ARCH_OMAP_OTG) || defined(CONFIG_ARCH_OMAP15XX)
static void omap2_usb_devconf_clear(u8 port, u32 mask)

View file

@ -134,7 +134,7 @@
};
USB1: usb@e0000400 {
compatible = "ohci-be";
compatible = "ibm,usb-ohci-440epx", "ohci-be";
reg = <0x00000000 0xe0000400 0x00000060>;
interrupt-parent = <&UIC0>;
interrupts = <0x15 0x8>;

View file

@ -57,6 +57,7 @@ obj-$(CONFIG_ATA_OVER_ETH) += block/aoe/
obj-$(CONFIG_PARIDE) += block/paride/
obj-$(CONFIG_TC) += tc/
obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/
obj-$(CONFIG_PCI) += usb/

View file

@ -1579,7 +1579,7 @@ static void ub_reset_task(struct work_struct *work)
struct ub_dev *sc = container_of(work, struct ub_dev, reset_work);
unsigned long flags;
struct ub_lun *lun;
int lkr, rc;
int rc;
if (!sc->reset) {
printk(KERN_WARNING "%s: Running reset unrequested\n",
@ -1597,10 +1597,11 @@ static void ub_reset_task(struct work_struct *work)
} else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
;
} else {
if ((lkr = usb_lock_device_for_reset(sc->dev, sc->intf)) < 0) {
rc = usb_lock_device_for_reset(sc->dev, sc->intf);
if (rc < 0) {
printk(KERN_NOTICE
"%s: usb_lock_device_for_reset failed (%d)\n",
sc->name, lkr);
sc->name, rc);
} else {
rc = usb_reset_device(sc->dev);
if (rc < 0) {
@ -1608,9 +1609,7 @@ static void ub_reset_task(struct work_struct *work)
"usb_lock_device_for_reset failed (%d)\n",
sc->name, rc);
}
if (lkr)
usb_unlock_device(sc->dev);
usb_unlock_device(sc->dev);
}
}

View file

@ -102,7 +102,7 @@ static void hid_reset(struct work_struct *work)
struct usbhid_device *usbhid =
container_of(work, struct usbhid_device, reset_work);
struct hid_device *hid = usbhid->hid;
int rc_lock, rc = 0;
int rc = 0;
if (test_bit(HID_CLEAR_HALT, &usbhid->iofl)) {
dev_dbg(&usbhid->intf->dev, "clear halt\n");
@ -113,11 +113,10 @@ static void hid_reset(struct work_struct *work)
else if (test_bit(HID_RESET_PENDING, &usbhid->iofl)) {
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_lock_device_for_reset(hid_to_usb_dev(hid), usbhid->intf);
if (rc == 0) {
rc = usb_reset_device(hid_to_usb_dev(hid));
if (rc_lock)
usb_unlock_device(hid_to_usb_dev(hid));
usb_unlock_device(hid_to_usb_dev(hid));
}
clear_bit(HID_RESET_PENDING, &usbhid->iofl);
}

View file

@ -114,18 +114,6 @@ config SENSORS_PCF8591
These devices are hard to detect and rarely found on mainstream
hardware. If unsure, say N.
config ISP1301_OMAP
tristate "Philips ISP1301 with OMAP OTG"
depends on ARCH_OMAP_OTG
help
If you say yes here you get support for the Philips ISP1301
USB-On-The-Go transceiver working with the OMAP OTG controller.
The ISP1301 is used in products including H2 and H3 development
boards for Texas Instruments OMAP processors.
This driver can also be built as a module. If so, the module
will be called isp1301_omap.
config SENSORS_MAX6875
tristate "Maxim MAX6875 Power supply supervisor"
depends on EXPERIMENTAL

View file

@ -18,7 +18,6 @@ obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o
obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_PCF8575) += pcf8575.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o
obj-$(CONFIG_MCU_MPC8349EMITX) += mcu_mpc8349emitx.o

View file

@ -3655,7 +3655,7 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
int ret;
pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
ret = usb_lock_device_for_reset(hdw->usb_dev,NULL);
if (ret == 1) {
if (ret == 0) {
ret = usb_reset_device(hdw->usb_dev);
usb_unlock_device(hdw->usb_dev);
} else {

View file

@ -2614,6 +2614,8 @@ source "drivers/net/tokenring/Kconfig"
source "drivers/net/wireless/Kconfig"
source "drivers/net/wimax/Kconfig"
source "drivers/net/usb/Kconfig"
source "drivers/net/pcmcia/Kconfig"

View file

@ -263,3 +263,4 @@ obj-$(CONFIG_NIU) += niu.o
obj-$(CONFIG_VIRTIO_NET) += virtio_net.o
obj-$(CONFIG_SFC) += sfc/
obj-$(CONFIG_WIMAX) += wimax/

View file

@ -283,9 +283,9 @@ static int kaweth_control(struct kaweth_device *kaweth,
dr->bRequestType= requesttype;
dr->bRequest = request;
dr->wValue = cpu_to_le16p(&value);
dr->wIndex = cpu_to_le16p(&index);
dr->wLength = cpu_to_le16p(&size);
dr->wValue = cpu_to_le16(value);
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);
return kaweth_internal_control_msg(kaweth->dev,
pipe,

View file

@ -150,8 +150,8 @@ static int get_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
pegasus->dr.bRequestType = PEGASUS_REQT_READ;
pegasus->dr.bRequest = PEGASUS_REQ_GET_REGS;
pegasus->dr.wValue = cpu_to_le16(0);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16p(&size);
pegasus->dr.wIndex = cpu_to_le16(indx);
pegasus->dr.wLength = cpu_to_le16(size);
pegasus->ctrl_urb->transfer_buffer_length = size;
usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
@ -208,8 +208,8 @@ static int set_registers(pegasus_t * pegasus, __u16 indx, __u16 size,
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS;
pegasus->dr.wValue = cpu_to_le16(0);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wLength = cpu_to_le16p(&size);
pegasus->dr.wIndex = cpu_to_le16(indx);
pegasus->dr.wLength = cpu_to_le16(size);
pegasus->ctrl_urb->transfer_buffer_length = size;
usb_fill_control_urb(pegasus->ctrl_urb, pegasus->usb,
@ -261,7 +261,7 @@ static int set_register(pegasus_t * pegasus, __u16 indx, __u8 data)
pegasus->dr.bRequestType = PEGASUS_REQT_WRITE;
pegasus->dr.bRequest = PEGASUS_REQ_SET_REG;
pegasus->dr.wValue = cpu_to_le16(data);
pegasus->dr.wIndex = cpu_to_le16p(&indx);
pegasus->dr.wIndex = cpu_to_le16(indx);
pegasus->dr.wLength = cpu_to_le16(1);
pegasus->ctrl_urb->transfer_buffer_length = 1;
@ -476,7 +476,7 @@ static inline void get_node_id(pegasus_t * pegasus, __u8 * id)
for (i = 0; i < 3; i++) {
read_eprom_word(pegasus, i, &w16);
((__le16 *) id)[i] = cpu_to_le16p(&w16);
((__le16 *) id)[i] = cpu_to_le16(w16);
}
}

17
drivers/net/wimax/Kconfig Normal file
View file

@ -0,0 +1,17 @@
#
# WiMAX LAN device drivers configuration
#
comment "Enable WiMAX (Networking options) to see the WiMAX drivers"
depends on WIMAX = n
if WIMAX
menu "WiMAX Wireless Broadband devices"
source "drivers/net/wimax/i2400m/Kconfig"
endmenu
endif

View file

@ -0,0 +1,5 @@
obj-$(CONFIG_WIMAX_I2400M) += i2400m/
# (from Sam Ravnborg) force kbuild to create built-in.o
obj- := dummy.o

View file

@ -0,0 +1,49 @@
config WIMAX_I2400M
tristate
depends on WIMAX
select FW_LOADER
comment "Enable USB support to see WiMAX USB drivers"
depends on USB = n
comment "Enable MMC support to see WiMAX SDIO drivers"
depends on MMC = n
config WIMAX_I2400M_USB
tristate "Intel Wireless WiMAX Connection 2400 over USB (including 5x50)"
depends on WIMAX && USB
select WIMAX_I2400M
help
Select if you have a device based on the Intel WiMAX
Connection 2400 over USB (like any of the Intel Wireless
WiMAX/WiFi Link 5x50 series).
If unsure, it is safe to select M (module).
config WIMAX_I2400M_SDIO
tristate "Intel Wireless WiMAX Connection 2400 over SDIO"
depends on WIMAX && MMC
select WIMAX_I2400M
help
Select if you have a device based on the Intel WiMAX
Connection 2400 over SDIO.
If unsure, it is safe to select M (module).
config WIMAX_I2400M_DEBUG_LEVEL
int "WiMAX i2400m debug level"
depends on WIMAX_I2400M
default 8
help
Select the maximum debug verbosity level to be compiled into
the WiMAX i2400m driver code.
By default, this is disabled at runtime and can be
selectively enabled at runtime for different parts of the
code using the sysfs debug-levels file.
If set at zero, this will compile out all the debug code.
It is recommended that it is left at 8.

View file

@ -0,0 +1,29 @@
obj-$(CONFIG_WIMAX_I2400M) += i2400m.o
obj-$(CONFIG_WIMAX_I2400M_USB) += i2400m-usb.o
obj-$(CONFIG_WIMAX_I2400M_SDIO) += i2400m-sdio.o
i2400m-y := \
control.o \
driver.o \
fw.o \
op-rfkill.o \
netdev.o \
tx.o \
rx.o
i2400m-$(CONFIG_DEBUG_FS) += debugfs.o
i2400m-usb-y := \
usb-fw.o \
usb-notif.o \
usb-tx.o \
usb-rx.o \
usb.o
i2400m-sdio-y := \
sdio.o \
sdio-tx.o \
sdio-fw.o \
sdio-rx.o

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,45 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Debug levels control file for the i2400m module
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __debug_levels__h__
#define __debug_levels__h__
/* Maximum compile and run time debug level for all submodules */
#define D_MODULENAME i2400m
#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL
#include <linux/wimax/debug.h>
/* List of all the enabled modules */
enum d_module {
D_SUBMODULE_DECLARE(control),
D_SUBMODULE_DECLARE(driver),
D_SUBMODULE_DECLARE(debugfs),
D_SUBMODULE_DECLARE(fw),
D_SUBMODULE_DECLARE(netdev),
D_SUBMODULE_DECLARE(rfkill),
D_SUBMODULE_DECLARE(rx),
D_SUBMODULE_DECLARE(tx),
};
#endif /* #ifndef __debug_levels__h__ */

View file

@ -0,0 +1,392 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Debugfs interfaces to manipulate driver and device information
*
*
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#include <linux/debugfs.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include "i2400m.h"
#define D_SUBMODULE debugfs
#include "debug-levels.h"
static
int debugfs_netdev_queue_stopped_get(void *data, u64 *val)
{
struct i2400m *i2400m = data;
*val = netif_queue_stopped(i2400m->wimax_dev.net_dev);
return 0;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_netdev_queue_stopped,
debugfs_netdev_queue_stopped_get,
NULL, "%llu\n");
static
struct dentry *debugfs_create_netdev_queue_stopped(
const char *name, struct dentry *parent, struct i2400m *i2400m)
{
return debugfs_create_file(name, 0400, parent, i2400m,
&fops_netdev_queue_stopped);
}
/*
* inode->i_private has the @data argument to debugfs_create_file()
*/
static
int i2400m_stats_open(struct inode *inode, struct file *filp)
{
filp->private_data = inode->i_private;
return 0;
}
/*
* We don't allow partial reads of this file, as then the reader would
* get weirdly confused data as it is updated.
*
* So or you read it all or nothing; if you try to read with an offset
* != 0, we consider you are done reading.
*/
static
ssize_t i2400m_rx_stats_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
char buf[128];
unsigned long flags;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
spin_lock_irqsave(&i2400m->rx_lock, flags);
snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
i2400m->rx_pl_num, i2400m->rx_pl_min,
i2400m->rx_pl_max, i2400m->rx_num,
i2400m->rx_size_acc,
i2400m->rx_size_min, i2400m->rx_size_max);
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
}
/* Any write clears the stats */
static
ssize_t i2400m_rx_stats_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
unsigned long flags;
spin_lock_irqsave(&i2400m->rx_lock, flags);
i2400m->rx_pl_num = 0;
i2400m->rx_pl_max = 0;
i2400m->rx_pl_min = UINT_MAX;
i2400m->rx_num = 0;
i2400m->rx_size_acc = 0;
i2400m->rx_size_min = UINT_MAX;
i2400m->rx_size_max = 0;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
return count;
}
static
const struct file_operations i2400m_rx_stats_fops = {
.owner = THIS_MODULE,
.open = i2400m_stats_open,
.read = i2400m_rx_stats_read,
.write = i2400m_rx_stats_write,
};
/* See i2400m_rx_stats_read() */
static
ssize_t i2400m_tx_stats_read(struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
char buf[128];
unsigned long flags;
if (*ppos != 0)
return 0;
if (count < sizeof(buf))
return -ENOSPC;
spin_lock_irqsave(&i2400m->tx_lock, flags);
snprintf(buf, sizeof(buf), "%u %u %u %u %u %u %u\n",
i2400m->tx_pl_num, i2400m->tx_pl_min,
i2400m->tx_pl_max, i2400m->tx_num,
i2400m->tx_size_acc,
i2400m->tx_size_min, i2400m->tx_size_max);
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
return simple_read_from_buffer(buffer, count, ppos, buf, strlen(buf));
}
/* Any write clears the stats */
static
ssize_t i2400m_tx_stats_write(struct file *filp, const char __user *buffer,
size_t count, loff_t *ppos)
{
struct i2400m *i2400m = filp->private_data;
unsigned long flags;
spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400m->tx_pl_num = 0;
i2400m->tx_pl_max = 0;
i2400m->tx_pl_min = UINT_MAX;
i2400m->tx_num = 0;
i2400m->tx_size_acc = 0;
i2400m->tx_size_min = UINT_MAX;
i2400m->tx_size_max = 0;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
return count;
}
static
const struct file_operations i2400m_tx_stats_fops = {
.owner = THIS_MODULE,
.open = i2400m_stats_open,
.read = i2400m_tx_stats_read,
.write = i2400m_tx_stats_write,
};
/* Write 1 to ask the device to go into suspend */
static
int debugfs_i2400m_suspend_set(void *data, u64 val)
{
int result;
struct i2400m *i2400m = data;
result = i2400m_cmd_enter_powersave(i2400m);
if (result >= 0)
result = 0;
return result;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_suspend,
NULL, debugfs_i2400m_suspend_set,
"%llu\n");
static
struct dentry *debugfs_create_i2400m_suspend(
const char *name, struct dentry *parent, struct i2400m *i2400m)
{
return debugfs_create_file(name, 0200, parent, i2400m,
&fops_i2400m_suspend);
}
/*
* Reset the device
*
* Write 0 to ask the device to soft reset, 1 to cold reset, 2 to bus
* reset (as defined by enum i2400m_reset_type).
*/
static
int debugfs_i2400m_reset_set(void *data, u64 val)
{
int result;
struct i2400m *i2400m = data;
enum i2400m_reset_type rt = val;
switch(rt) {
case I2400M_RT_WARM:
case I2400M_RT_COLD:
case I2400M_RT_BUS:
result = i2400m->bus_reset(i2400m, rt);
if (result >= 0)
result = 0;
default:
result = -EINVAL;
}
return result;
}
DEFINE_SIMPLE_ATTRIBUTE(fops_i2400m_reset,
NULL, debugfs_i2400m_reset_set,
"%llu\n");
static
struct dentry *debugfs_create_i2400m_reset(
const char *name, struct dentry *parent, struct i2400m *i2400m)
{
return debugfs_create_file(name, 0200, parent, i2400m,
&fops_i2400m_reset);
}
/*
* Debug levels control; see debug.h
*/
struct d_level D_LEVEL[] = {
D_SUBMODULE_DEFINE(control),
D_SUBMODULE_DEFINE(driver),
D_SUBMODULE_DEFINE(debugfs),
D_SUBMODULE_DEFINE(fw),
D_SUBMODULE_DEFINE(netdev),
D_SUBMODULE_DEFINE(rfkill),
D_SUBMODULE_DEFINE(rx),
D_SUBMODULE_DEFINE(tx),
};
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
#define __debugfs_register(prefix, name, parent) \
do { \
result = d_level_register_debugfs(prefix, name, parent); \
if (result < 0) \
goto error; \
} while (0)
int i2400m_debugfs_add(struct i2400m *i2400m)
{
int result;
struct device *dev = i2400m_dev(i2400m);
struct dentry *dentry = i2400m->wimax_dev.debugfs_dentry;
struct dentry *fd;
dentry = debugfs_create_dir("i2400m", dentry);
result = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
if (result == -ENODEV)
result = 0; /* No debugfs support */
goto error;
}
i2400m->debugfs_dentry = dentry;
__debugfs_register("dl_", control, dentry);
__debugfs_register("dl_", driver, dentry);
__debugfs_register("dl_", debugfs, dentry);
__debugfs_register("dl_", fw, dentry);
__debugfs_register("dl_", netdev, dentry);
__debugfs_register("dl_", rfkill, dentry);
__debugfs_register("dl_", rx, dentry);
__debugfs_register("dl_", tx, dentry);
fd = debugfs_create_size_t("tx_in", 0400, dentry,
&i2400m->tx_in);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"tx_in: %d\n", result);
goto error;
}
fd = debugfs_create_size_t("tx_out", 0400, dentry,
&i2400m->tx_out);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"tx_out: %d\n", result);
goto error;
}
fd = debugfs_create_u32("state", 0600, dentry,
&i2400m->state);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"state: %d\n", result);
goto error;
}
/*
* Trace received messages from user space
*
* In order to tap the bidirectional message stream in the
* 'msg' pipe, user space can read from the 'msg' pipe;
* however, due to limitations in libnl, we can't know what
* the different applications are sending down to the kernel.
*
* So we have this hack where the driver will echo any message
* received on the msg pipe from user space [through a call to
* wimax_dev->op_msg_from_user() into
* i2400m_op_msg_from_user()] into the 'trace' pipe that this
* driver creates.
*
* So then, reading from both the 'trace' and 'msg' pipes in
* user space will provide a full dump of the traffic.
*
* Write 1 to activate, 0 to clear.
*
* It is not really very atomic, but it is also not too
* critical.
*/
fd = debugfs_create_u8("trace_msg_from_user", 0600, dentry,
&i2400m->trace_msg_from_user);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"trace_msg_from_user: %d\n", result);
goto error;
}
fd = debugfs_create_netdev_queue_stopped("netdev_queue_stopped",
dentry, i2400m);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"netdev_queue_stopped: %d\n", result);
goto error;
}
fd = debugfs_create_file("rx_stats", 0600, dentry, i2400m,
&i2400m_rx_stats_fops);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"rx_stats: %d\n", result);
goto error;
}
fd = debugfs_create_file("tx_stats", 0600, dentry, i2400m,
&i2400m_tx_stats_fops);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"tx_stats: %d\n", result);
goto error;
}
fd = debugfs_create_i2400m_suspend("suspend", dentry, i2400m);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry suspend: %d\n",
result);
goto error;
}
fd = debugfs_create_i2400m_reset("reset", dentry, i2400m);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry reset: %d\n", result);
goto error;
}
result = 0;
error:
return result;
}
void i2400m_debugfs_rm(struct i2400m *i2400m)
{
debugfs_remove_recursive(i2400m->debugfs_dentry);
}

View file

@ -0,0 +1,728 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Generic probe/disconnect, reset and message passing
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* See i2400m.h for driver documentation. This contains helpers for
* the driver model glue [_setup()/_release()], handling device resets
* [_dev_reset_handle()], and the backends for the WiMAX stack ops
* reset [_op_reset()] and message from user [_op_msg_from_user()].
*
* ROADMAP:
*
* i2400m_op_msg_from_user()
* i2400m_msg_to_dev()
* wimax_msg_to_user_send()
*
* i2400m_op_reset()
* i240m->bus_reset()
*
* i2400m_dev_reset_handle()
* __i2400m_dev_reset_handle()
* __i2400m_dev_stop()
* __i2400m_dev_start()
*
* i2400m_setup()
* i2400m_bootrom_init()
* register_netdev()
* i2400m_dev_start()
* __i2400m_dev_start()
* i2400m_dev_bootstrap()
* i2400m_tx_setup()
* i2400m->bus_dev_start()
* i2400m_check_mac_addr()
* wimax_dev_add()
*
* i2400m_release()
* wimax_dev_rm()
* i2400m_dev_stop()
* __i2400m_dev_stop()
* i2400m_dev_shutdown()
* i2400m->bus_dev_stop()
* i2400m_tx_release()
* unregister_netdev()
*/
#include "i2400m.h"
#include <linux/wimax/i2400m.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#define D_SUBMODULE driver
#include "debug-levels.h"
int i2400m_idle_mode_disabled; /* 0 (idle mode enabled) by default */
module_param_named(idle_mode_disabled, i2400m_idle_mode_disabled, int, 0644);
MODULE_PARM_DESC(idle_mode_disabled,
"If true, the device will not enable idle mode negotiation "
"with the base station (when connected) to save power.");
/**
* i2400m_queue_work - schedule work on a i2400m's queue
*
* @i2400m: device descriptor
*
* @fn: function to run to execute work. It gets passed a 'struct
* work_struct' that is wrapped in a 'struct i2400m_work'. Once
* done, you have to (1) i2400m_put(i2400m_work->i2400m) and then
* (2) kfree(i2400m_work).
*
* @gfp_flags: GFP flags for memory allocation.
*
* @pl: pointer to a payload buffer that you want to pass to the _work
* function. Use this to pack (for example) a struct with extra
* arguments.
*
* @pl_size: size of the payload buffer.
*
* We do this quite often, so this just saves typing; allocate a
* wrapper for a i2400m, get a ref to it, pack arguments and launch
* the work.
*
* A usual workflow is:
*
* struct my_work_args {
* void *something;
* int whatever;
* };
* ...
*
* struct my_work_args my_args = {
* .something = FOO,
* .whaetever = BLAH
* };
* i2400m_queue_work(i2400m, 1, my_work_function, GFP_KERNEL,
* &args, sizeof(args))
*
* And now the work function can unpack the arguments and call the
* real function (or do the job itself):
*
* static
* void my_work_fn((struct work_struct *ws)
* {
* struct i2400m_work *iw =
* container_of(ws, struct i2400m_work, ws);
* struct my_work_args *my_args = (void *) iw->pl;
*
* my_work(iw->i2400m, my_args->something, my_args->whatevert);
* }
*/
int i2400m_queue_work(struct i2400m *i2400m,
void (*fn)(struct work_struct *), gfp_t gfp_flags,
const void *pl, size_t pl_size)
{
int result;
struct i2400m_work *iw;
BUG_ON(i2400m->work_queue == NULL);
result = -ENOMEM;
iw = kzalloc(sizeof(*iw) + pl_size, gfp_flags);
if (iw == NULL)
goto error_kzalloc;
iw->i2400m = i2400m_get(i2400m);
memcpy(iw->pl, pl, pl_size);
INIT_WORK(&iw->ws, fn);
result = queue_work(i2400m->work_queue, &iw->ws);
error_kzalloc:
return result;
}
EXPORT_SYMBOL_GPL(i2400m_queue_work);
/*
* Schedule i2400m's specific work on the system's queue.
*
* Used for a few cases where we really need it; otherwise, identical
* to i2400m_queue_work().
*
* Returns < 0 errno code on error, 1 if ok.
*
* If it returns zero, something really bad happened, as it means the
* works struct was already queued, but we have just allocated it, so
* it should not happen.
*/
int i2400m_schedule_work(struct i2400m *i2400m,
void (*fn)(struct work_struct *), gfp_t gfp_flags)
{
int result;
struct i2400m_work *iw;
BUG_ON(i2400m->work_queue == NULL);
result = -ENOMEM;
iw = kzalloc(sizeof(*iw), gfp_flags);
if (iw == NULL)
goto error_kzalloc;
iw->i2400m = i2400m_get(i2400m);
INIT_WORK(&iw->ws, fn);
result = schedule_work(&iw->ws);
if (result == 0)
result = -ENXIO;
error_kzalloc:
return result;
}
/*
* WiMAX stack operation: relay a message from user space
*
* @wimax_dev: device descriptor
* @pipe_name: named pipe the message is for
* @msg_buf: pointer to the message bytes
* @msg_len: length of the buffer
* @genl_info: passed by the generic netlink layer
*
* The WiMAX stack will call this function when a message was received
* from user space.
*
* For the i2400m, this is an L3L4 message, as specified in
* include/linux/wimax/i2400m.h, and thus prefixed with a 'struct
* i2400m_l3l4_hdr'. Driver (and device) expect the messages to be
* coded in Little Endian.
*
* This function just verifies that the header declaration and the
* payload are consistent and then deals with it, either forwarding it
* to the device or procesing it locally.
*
* In the i2400m, messages are basically commands that will carry an
* ack, so we use i2400m_msg_to_dev() and then deliver the ack back to
* user space. The rx.c code might intercept the response and use it
* to update the driver's state, but then it will pass it on so it can
* be relayed back to user space.
*
* Note that asynchronous events from the device are processed and
* sent to user space in rx.c.
*/
static
int i2400m_op_msg_from_user(struct wimax_dev *wimax_dev,
const char *pipe_name,
const void *msg_buf, size_t msg_len,
const struct genl_info *genl_info)
{
int result;
struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev);
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *ack_skb;
d_fnstart(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p "
"msg_len %zu genl_info %p)\n", wimax_dev, i2400m,
msg_buf, msg_len, genl_info);
ack_skb = i2400m_msg_to_dev(i2400m, msg_buf, msg_len);
result = PTR_ERR(ack_skb);
if (IS_ERR(ack_skb))
goto error_msg_to_dev;
if (unlikely(i2400m->trace_msg_from_user))
wimax_msg(&i2400m->wimax_dev, "trace",
msg_buf, msg_len, GFP_KERNEL);
result = wimax_msg_send(&i2400m->wimax_dev, ack_skb);
error_msg_to_dev:
d_fnend(4, dev, "(wimax_dev %p [i2400m %p] msg_buf %p msg_len %zu "
"genl_info %p) = %d\n", wimax_dev, i2400m, msg_buf, msg_len,
genl_info, result);
return result;
}
/*
* Context to wait for a reset to finalize
*/
struct i2400m_reset_ctx {
struct completion completion;
int result;
};
/*
* WiMAX stack operation: reset a device
*
* @wimax_dev: device descriptor
*
* See the documentation for wimax_reset() and wimax_dev->op_reset for
* the requirements of this function. The WiMAX stack guarantees
* serialization on calls to this function.
*
* Do a warm reset on the device; if it fails, resort to a cold reset
* and return -ENODEV. On successful warm reset, we need to block
* until it is complete.
*
* The bus-driver implementation of reset takes care of falling back
* to cold reset if warm fails.
*/
static
int i2400m_op_reset(struct wimax_dev *wimax_dev)
{
int result;
struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev);
struct device *dev = i2400m_dev(i2400m);
struct i2400m_reset_ctx ctx = {
.completion = COMPLETION_INITIALIZER_ONSTACK(ctx.completion),
.result = 0,
};
d_fnstart(4, dev, "(wimax_dev %p)\n", wimax_dev);
mutex_lock(&i2400m->init_mutex);
i2400m->reset_ctx = &ctx;
mutex_unlock(&i2400m->init_mutex);
result = i2400m->bus_reset(i2400m, I2400M_RT_WARM);
if (result < 0)
goto out;
result = wait_for_completion_timeout(&ctx.completion, 4*HZ);
if (result == 0)
result = -ETIMEDOUT;
else if (result > 0)
result = ctx.result;
/* if result < 0, pass it on */
mutex_lock(&i2400m->init_mutex);
i2400m->reset_ctx = NULL;
mutex_unlock(&i2400m->init_mutex);
out:
d_fnend(4, dev, "(wimax_dev %p) = %d\n", wimax_dev, result);
return result;
}
/*
* Check the MAC address we got from boot mode is ok
*
* @i2400m: device descriptor
*
* Returns: 0 if ok, < 0 errno code on error.
*/
static
int i2400m_check_mac_addr(struct i2400m *i2400m)
{
int result;
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb;
const struct i2400m_tlv_detailed_device_info *ddi;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
const unsigned char zeromac[ETH_ALEN] = { 0 };
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
skb = i2400m_get_device_info(i2400m);
if (IS_ERR(skb)) {
result = PTR_ERR(skb);
dev_err(dev, "Cannot verify MAC address, error reading: %d\n",
result);
goto error;
}
/* Extract MAC addresss */
ddi = (void *) skb->data;
BUILD_BUG_ON(ETH_ALEN != sizeof(ddi->mac_address));
d_printf(2, dev, "GET DEVICE INFO: mac addr "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
ddi->mac_address[0], ddi->mac_address[1],
ddi->mac_address[2], ddi->mac_address[3],
ddi->mac_address[4], ddi->mac_address[5]);
if (!memcmp(net_dev->perm_addr, ddi->mac_address,
sizeof(ddi->mac_address)))
goto ok;
dev_warn(dev, "warning: device reports a different MAC address "
"to that of boot mode's\n");
dev_warn(dev, "device reports %02x:%02x:%02x:%02x:%02x:%02x\n",
ddi->mac_address[0], ddi->mac_address[1],
ddi->mac_address[2], ddi->mac_address[3],
ddi->mac_address[4], ddi->mac_address[5]);
dev_warn(dev, "boot mode reported %02x:%02x:%02x:%02x:%02x:%02x\n",
net_dev->perm_addr[0], net_dev->perm_addr[1],
net_dev->perm_addr[2], net_dev->perm_addr[3],
net_dev->perm_addr[4], net_dev->perm_addr[5]);
if (!memcmp(zeromac, ddi->mac_address, sizeof(zeromac)))
dev_err(dev, "device reports an invalid MAC address, "
"not updating\n");
else {
dev_warn(dev, "updating MAC address\n");
net_dev->addr_len = ETH_ALEN;
memcpy(net_dev->perm_addr, ddi->mac_address, ETH_ALEN);
memcpy(net_dev->dev_addr, ddi->mac_address, ETH_ALEN);
}
ok:
result = 0;
kfree_skb(skb);
error:
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
}
/**
* __i2400m_dev_start - Bring up driver communication with the device
*
* @i2400m: device descriptor
* @flags: boot mode flags
*
* Returns: 0 if ok, < 0 errno code on error.
*
* Uploads firmware and brings up all the resources needed to be able
* to communicate with the device.
*
* TX needs to be setup before the bus-specific code (otherwise on
* shutdown, the bus-tx code could try to access it).
*/
static
int __i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri flags)
{
int result;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct net_device *net_dev = wimax_dev->net_dev;
struct device *dev = i2400m_dev(i2400m);
int times = 3;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
retry:
result = i2400m_dev_bootstrap(i2400m, flags);
if (result < 0) {
dev_err(dev, "cannot bootstrap device: %d\n", result);
goto error_bootstrap;
}
result = i2400m_tx_setup(i2400m);
if (result < 0)
goto error_tx_setup;
result = i2400m->bus_dev_start(i2400m);
if (result < 0)
goto error_bus_dev_start;
i2400m->work_queue = create_singlethread_workqueue(wimax_dev->name);
if (i2400m->work_queue == NULL) {
result = -ENOMEM;
dev_err(dev, "cannot create workqueue\n");
goto error_create_workqueue;
}
/* At this point is ok to send commands to the device */
result = i2400m_check_mac_addr(i2400m);
if (result < 0)
goto error_check_mac_addr;
i2400m->ready = 1;
wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
result = i2400m_dev_initialize(i2400m);
if (result < 0)
goto error_dev_initialize;
/* At this point, reports will come for the device and set it
* to the right state if it is different than UNINITIALIZED */
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
net_dev, i2400m, result);
return result;
error_dev_initialize:
error_check_mac_addr:
destroy_workqueue(i2400m->work_queue);
error_create_workqueue:
i2400m->bus_dev_stop(i2400m);
error_bus_dev_start:
i2400m_tx_release(i2400m);
error_tx_setup:
error_bootstrap:
if (result == -ERESTARTSYS && times-- > 0) {
flags = I2400M_BRI_SOFT;
goto retry;
}
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
net_dev, i2400m, result);
return result;
}
static
int i2400m_dev_start(struct i2400m *i2400m, enum i2400m_bri bm_flags)
{
int result;
mutex_lock(&i2400m->init_mutex); /* Well, start the device */
result = __i2400m_dev_start(i2400m, bm_flags);
if (result >= 0)
i2400m->updown = 1;
mutex_unlock(&i2400m->init_mutex);
return result;
}
/**
* i2400m_dev_stop - Tear down driver communication with the device
*
* @i2400m: device descriptor
*
* Returns: 0 if ok, < 0 errno code on error.
*
* Releases all the resources allocated to communicate with the device.
*/
static
void __i2400m_dev_stop(struct i2400m *i2400m)
{
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
wimax_state_change(wimax_dev, __WIMAX_ST_QUIESCING);
i2400m_dev_shutdown(i2400m);
i2400m->ready = 0;
destroy_workqueue(i2400m->work_queue);
i2400m->bus_dev_stop(i2400m);
i2400m_tx_release(i2400m);
wimax_state_change(wimax_dev, WIMAX_ST_DOWN);
d_fnend(3, dev, "(i2400m %p) = 0\n", i2400m);
}
/*
* Watch out -- we only need to stop if there is a need for it. The
* device could have reset itself and failed to come up again (see
* _i2400m_dev_reset_handle()).
*/
static
void i2400m_dev_stop(struct i2400m *i2400m)
{
mutex_lock(&i2400m->init_mutex);
if (i2400m->updown) {
__i2400m_dev_stop(i2400m);
i2400m->updown = 0;
}
mutex_unlock(&i2400m->init_mutex);
}
/*
* The device has rebooted; fix up the device and the driver
*
* Tear down the driver communication with the device, reload the
* firmware and reinitialize the communication with the device.
*
* If someone calls a reset when the device's firmware is down, in
* theory we won't see it because we are not listening. However, just
* in case, leave the code to handle it.
*
* If there is a reset context, use it; this means someone is waiting
* for us to tell him when the reset operation is complete and the
* device is ready to rock again.
*
* NOTE: if we are in the process of bringing up or down the
* communication with the device [running i2400m_dev_start() or
* _stop()], don't do anything, let it fail and handle it.
*
* This function is ran always in a thread context
*/
static
void __i2400m_dev_reset_handle(struct work_struct *ws)
{
int result;
struct i2400m_work *iw = container_of(ws, struct i2400m_work, ws);
struct i2400m *i2400m = iw->i2400m;
struct device *dev = i2400m_dev(i2400m);
enum wimax_st wimax_state;
struct i2400m_reset_ctx *ctx = i2400m->reset_ctx;
d_fnstart(3, dev, "(ws %p i2400m %p)\n", ws, i2400m);
result = 0;
if (mutex_trylock(&i2400m->init_mutex) == 0) {
/* We are still in i2400m_dev_start() [let it fail] or
* i2400m_dev_stop() [we are shutting down anyway, so
* ignore it] or we are resetting somewhere else. */
dev_err(dev, "device rebooted\n");
i2400m_msg_to_dev_cancel_wait(i2400m, -ERESTARTSYS);
complete(&i2400m->msg_completion);
goto out;
}
wimax_state = wimax_state_get(&i2400m->wimax_dev);
if (wimax_state < WIMAX_ST_UNINITIALIZED) {
dev_info(dev, "device rebooted: it is down, ignoring\n");
goto out_unlock; /* ifconfig up/down wasn't called */
}
dev_err(dev, "device rebooted: reinitializing driver\n");
__i2400m_dev_stop(i2400m);
i2400m->updown = 0;
result = __i2400m_dev_start(i2400m,
I2400M_BRI_SOFT | I2400M_BRI_MAC_REINIT);
if (result < 0) {
dev_err(dev, "device reboot: cannot start the device: %d\n",
result);
result = i2400m->bus_reset(i2400m, I2400M_RT_BUS);
if (result >= 0)
result = -ENODEV;
} else
i2400m->updown = 1;
out_unlock:
if (i2400m->reset_ctx) {
ctx->result = result;
complete(&ctx->completion);
}
mutex_unlock(&i2400m->init_mutex);
out:
i2400m_put(i2400m);
kfree(iw);
d_fnend(3, dev, "(ws %p i2400m %p) = void\n", ws, i2400m);
return;
}
/**
* i2400m_dev_reset_handle - Handle a device's reset in a thread context
*
* Schedule a device reset handling out on a thread context, so it
* is safe to call from atomic context. We can't use the i2400m's
* queue as we are going to destroy it and reinitialize it as part of
* the driver bringup/bringup process.
*
* See __i2400m_dev_reset_handle() for details; that takes care of
* reinitializing the driver to handle the reset, calling into the
* bus-specific functions ops as needed.
*/
int i2400m_dev_reset_handle(struct i2400m *i2400m)
{
return i2400m_schedule_work(i2400m, __i2400m_dev_reset_handle,
GFP_ATOMIC);
}
EXPORT_SYMBOL_GPL(i2400m_dev_reset_handle);
/**
* i2400m_setup - bus-generic setup function for the i2400m device
*
* @i2400m: device descriptor (bus-specific parts have been initialized)
*
* Returns: 0 if ok, < 0 errno code on error.
*
* Initializes the bus-generic parts of the i2400m driver; the
* bus-specific parts have been initialized, function pointers filled
* out by the bus-specific probe function.
*
* As well, this registers the WiMAX and net device nodes. Once this
* function returns, the device is operative and has to be ready to
* receive and send network traffic and WiMAX control operations.
*/
int i2400m_setup(struct i2400m *i2400m, enum i2400m_bri bm_flags)
{
int result = -ENODEV;
struct device *dev = i2400m_dev(i2400m);
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
snprintf(wimax_dev->name, sizeof(wimax_dev->name),
"i2400m-%s:%s", dev->bus->name, dev->bus_id);
i2400m->bm_cmd_buf = kzalloc(I2400M_BM_CMD_BUF_SIZE, GFP_KERNEL);
if (i2400m->bm_cmd_buf == NULL) {
dev_err(dev, "cannot allocate USB command buffer\n");
goto error_bm_cmd_kzalloc;
}
i2400m->bm_ack_buf = kzalloc(I2400M_BM_ACK_BUF_SIZE, GFP_KERNEL);
if (i2400m->bm_ack_buf == NULL) {
dev_err(dev, "cannot allocate USB ack buffer\n");
goto error_bm_ack_buf_kzalloc;
}
result = i2400m_bootrom_init(i2400m, bm_flags);
if (result < 0) {
dev_err(dev, "read mac addr: bootrom init "
"failed: %d\n", result);
goto error_bootrom_init;
}
result = i2400m_read_mac_addr(i2400m);
if (result < 0)
goto error_read_mac_addr;
result = register_netdev(net_dev); /* Okey dokey, bring it up */
if (result < 0) {
dev_err(dev, "cannot register i2400m network device: %d\n",
result);
goto error_register_netdev;
}
netif_carrier_off(net_dev);
result = i2400m_dev_start(i2400m, bm_flags);
if (result < 0)
goto error_dev_start;
i2400m->wimax_dev.op_msg_from_user = i2400m_op_msg_from_user;
i2400m->wimax_dev.op_rfkill_sw_toggle = i2400m_op_rfkill_sw_toggle;
i2400m->wimax_dev.op_reset = i2400m_op_reset;
result = wimax_dev_add(&i2400m->wimax_dev, net_dev);
if (result < 0)
goto error_wimax_dev_add;
/* User space needs to do some init stuff */
wimax_state_change(wimax_dev, WIMAX_ST_UNINITIALIZED);
/* Now setup all that requires a registered net and wimax device. */
result = i2400m_debugfs_add(i2400m);
if (result < 0) {
dev_err(dev, "cannot setup i2400m's debugfs: %d\n", result);
goto error_debugfs_setup;
}
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
error_debugfs_setup:
wimax_dev_rm(&i2400m->wimax_dev);
error_wimax_dev_add:
i2400m_dev_stop(i2400m);
error_dev_start:
unregister_netdev(net_dev);
error_register_netdev:
error_read_mac_addr:
error_bootrom_init:
kfree(i2400m->bm_ack_buf);
error_bm_ack_buf_kzalloc:
kfree(i2400m->bm_cmd_buf);
error_bm_cmd_kzalloc:
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
}
EXPORT_SYMBOL_GPL(i2400m_setup);
/**
* i2400m_release - release the bus-generic driver resources
*
* Sends a disconnect message and undoes any setup done by i2400m_setup()
*/
void i2400m_release(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
netif_stop_queue(i2400m->wimax_dev.net_dev);
i2400m_debugfs_rm(i2400m);
wimax_dev_rm(&i2400m->wimax_dev);
i2400m_dev_stop(i2400m);
unregister_netdev(i2400m->wimax_dev.net_dev);
kfree(i2400m->bm_ack_buf);
kfree(i2400m->bm_cmd_buf);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
EXPORT_SYMBOL_GPL(i2400m_release);
static
int __init i2400m_driver_init(void)
{
return 0;
}
module_init(i2400m_driver_init);
static
void __exit i2400m_driver_exit(void)
{
/* for scheds i2400m_dev_reset_handle() */
flush_scheduled_work();
return;
}
module_exit(i2400m_driver_exit);
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
MODULE_DESCRIPTION("Intel 2400M WiMAX networking bus-generic driver");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,132 @@
/*
* Intel Wireless WiMAX Connection 2400m
* SDIO-specific i2400m driver definitions
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Brian Bian <brian.bian@intel.com>
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
*
* This driver implements the bus-specific part of the i2400m for
* SDIO. Check i2400m.h for a generic driver description.
*
* ARCHITECTURE
*
* This driver sits under the bus-generic i2400m driver, providing the
* connection to the device.
*
* When probed, all the function pointers are setup and then the
* bus-generic code called. The generic driver will then use the
* provided pointers for uploading firmware (i2400ms_bus_bm*() in
* sdio-fw.c) and then setting up the device (i2400ms_dev_*() in
* sdio.c).
*
* Once firmware is uploaded, TX functions (sdio-tx.c) are called when
* data is ready for transmission in the TX fifo; then the SDIO IRQ is
* fired and data is available (sdio-rx.c), it is sent to the generic
* driver for processing with i2400m_rx.
*/
#ifndef __I2400M_SDIO_H__
#define __I2400M_SDIO_H__
#include "i2400m.h"
/* Host-Device interface for SDIO */
enum {
I2400MS_BLK_SIZE = 256,
I2400MS_PL_SIZE_MAX = 0x3E00,
I2400MS_DATA_ADDR = 0x0,
I2400MS_INTR_STATUS_ADDR = 0x13,
I2400MS_INTR_CLEAR_ADDR = 0x13,
I2400MS_INTR_ENABLE_ADDR = 0x14,
I2400MS_INTR_GET_SIZE_ADDR = 0x2C,
/* The number of ticks to wait for the device to signal that
* it is ready */
I2400MS_INIT_SLEEP_INTERVAL = 10,
};
/**
* struct i2400ms - descriptor for a SDIO connected i2400m
*
* @i2400m: bus-generic i2400m implementation; has to be first (see
* it's documentation in i2400m.h).
*
* @func: pointer to our SDIO function
*
* @tx_worker: workqueue struct used to TX data when the bus-generic
* code signals packets are pending for transmission to the device.
*
* @tx_workqueue: workqeueue used for data TX; we don't use the
* system's workqueue as that might cause deadlocks with code in
* the bus-generic driver.
*/
struct i2400ms {
struct i2400m i2400m; /* FIRST! See doc */
struct sdio_func *func;
struct work_struct tx_worker;
struct workqueue_struct *tx_workqueue;
char tx_wq_name[32];
struct dentry *debugfs_dentry;
};
static inline
void i2400ms_init(struct i2400ms *i2400ms)
{
i2400m_init(&i2400ms->i2400m);
}
extern int i2400ms_rx_setup(struct i2400ms *);
extern void i2400ms_rx_release(struct i2400ms *);
extern ssize_t __i2400ms_rx_get_size(struct i2400ms *);
extern int i2400ms_tx_setup(struct i2400ms *);
extern void i2400ms_tx_release(struct i2400ms *);
extern void i2400ms_bus_tx_kick(struct i2400m *);
extern ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *,
const struct i2400m_bootrom_header *,
size_t, int);
extern ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *,
struct i2400m_bootrom_header *,
size_t);
#endif /* #ifndef __I2400M_SDIO_H__ */

View file

@ -0,0 +1,264 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB-specific i2400m driver definitions
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
*
* This driver implements the bus-specific part of the i2400m for
* USB. Check i2400m.h for a generic driver description.
*
* ARCHITECTURE
*
* This driver listens to notifications sent from the notification
* endpoint (in usb-notif.c); when data is ready to read, the code in
* there schedules a read from the device (usb-rx.c) and then passes
* the data to the generic RX code (rx.c).
*
* When the generic driver needs to send data (network or control), it
* queues up in the TX FIFO (tx.c) and that will notify the driver
* through the i2400m->bus_tx_kick() callback
* (usb-tx.c:i2400mu_bus_tx_kick) which will send the items in the
* FIFO queue.
*
* This driver, as well, implements the USB-specific ops for the generic
* driver to be able to setup/teardown communication with the device
* [i2400m_bus_dev_start() and i2400m_bus_dev_stop()], reseting the
* device [i2400m_bus_reset()] and performing firmware upload
* [i2400m_bus_bm_cmd() and i2400_bus_bm_wait_for_ack()].
*/
#ifndef __I2400M_USB_H__
#define __I2400M_USB_H__
#include "i2400m.h"
#include <linux/kthread.h>
/*
* Error Density Count: cheapo error density (over time) counter
*
* Originally by Reinette Chatre <reinette.chatre@intel.com>
*
* Embed an 'struct edc' somewhere. Each time there is a soft or
* retryable error, call edc_inc() and check if the error top
* watermark has been reached.
*/
enum {
EDC_MAX_ERRORS = 10,
EDC_ERROR_TIMEFRAME = HZ,
};
/* error density counter */
struct edc {
unsigned long timestart;
u16 errorcount;
};
static inline void edc_init(struct edc *edc)
{
edc->timestart = jiffies;
}
/**
* edc_inc - report a soft error and check if we are over the watermark
*
* @edc: pointer to error density counter.
* @max_err: maximum number of errors we can accept over the timeframe
* @timeframe: lenght of the timeframe (in jiffies).
*
* Returns: !0 1 if maximum acceptable errors per timeframe has been
* exceeded. 0 otherwise.
*
* This is way to determine if the number of acceptable errors per time
* period has been exceeded. It is not accurate as there are cases in which
* this scheme will not work, for example if there are periodic occurences
* of errors that straddle updates to the start time. This scheme is
* sufficient for our usage.
*
* To use, embed a 'struct edc' somewhere, initialize it with
* edc_init() and when an error hits:
*
* if (do_something_fails_with_a_soft_error) {
* if (edc_inc(&my->edc, MAX_ERRORS, MAX_TIMEFRAME))
* Ops, hard error, do something about it
* else
* Retry or ignore, depending on whatever
* }
*/
static inline int edc_inc(struct edc *edc, u16 max_err, u16 timeframe)
{
unsigned long now;
now = jiffies;
if (now - edc->timestart > timeframe) {
edc->errorcount = 1;
edc->timestart = now;
} else if (++edc->errorcount > max_err) {
edc->errorcount = 0;
edc->timestart = now;
return 1;
}
return 0;
}
/* Host-Device interface for USB */
enum {
I2400MU_MAX_NOTIFICATION_LEN = 256,
I2400MU_BLK_SIZE = 16,
I2400MU_PL_SIZE_MAX = 0x3EFF,
/* Endpoints */
I2400MU_EP_BULK_OUT = 0,
I2400MU_EP_NOTIFICATION,
I2400MU_EP_RESET_COLD,
I2400MU_EP_BULK_IN,
};
/**
* struct i2400mu - descriptor for a USB connected i2400m
*
* @i2400m: bus-generic i2400m implementation; has to be first (see
* it's documentation in i2400m.h).
*
* @usb_dev: pointer to our USB device
*
* @usb_iface: pointer to our USB interface
*
* @urb_edc: error density counter; used to keep a density-on-time tab
* on how many soft (retryable or ignorable) errors we get. If we
* go over the threshold, we consider the bus transport is failing
* too much and reset.
*
* @notif_urb: URB for receiving notifications from the device.
*
* @tx_kthread: thread we use for data TX. We use a thread because in
* order to do deep power saving and put the device to sleep, we
* need to call usb_autopm_*() [blocking functions].
*
* @tx_wq: waitqueue for the TX kthread to sleep when there is no data
* to be sent; when more data is available, it is woken up by
* i2400mu_bus_tx_kick().
*
* @rx_kthread: thread we use for data RX. We use a thread because in
* order to do deep power saving and put the device to sleep, we
* need to call usb_autopm_*() [blocking functions].
*
* @rx_wq: waitqueue for the RX kthread to sleep when there is no data
* to receive. When data is available, it is woken up by
* usb-notif.c:i2400mu_notification_grok().
*
* @rx_pending_count: number of rx-data-ready notifications that were
* still not handled by the RX kthread.
*
* @rx_size: current RX buffer size that is being used.
*
* @rx_size_acc: accumulator of the sizes of the previous read
* transactions.
*
* @rx_size_cnt: number of read transactions accumulated in
* @rx_size_acc.
*
* @do_autopm: disable(0)/enable(>0) calling the
* usb_autopm_get/put_interface() barriers when executing
* commands. See doc in i2400mu_suspend() for more information.
*
* @rx_size_auto_shrink: if true, the rx_size is shrinked
* automatically based on the average size of the received
* transactions. This allows the receive code to allocate smaller
* chunks of memory and thus reduce pressure on the memory
* allocator by not wasting so much space. By default it is
* enabled.
*
* @debugfs_dentry: hookup for debugfs files.
* These have to be in a separate directory, a child of
* (wimax_dev->debugfs_dentry) so they can be removed when the
* module unloads, as we don't keep each dentry.
*/
struct i2400mu {
struct i2400m i2400m; /* FIRST! See doc */
struct usb_device *usb_dev;
struct usb_interface *usb_iface;
struct edc urb_edc; /* Error density counter */
struct urb *notif_urb;
struct task_struct *tx_kthread;
wait_queue_head_t tx_wq;
struct task_struct *rx_kthread;
wait_queue_head_t rx_wq;
atomic_t rx_pending_count;
size_t rx_size, rx_size_acc, rx_size_cnt;
atomic_t do_autopm;
u8 rx_size_auto_shrink;
struct dentry *debugfs_dentry;
};
static inline
void i2400mu_init(struct i2400mu *i2400mu)
{
i2400m_init(&i2400mu->i2400m);
edc_init(&i2400mu->urb_edc);
init_waitqueue_head(&i2400mu->tx_wq);
atomic_set(&i2400mu->rx_pending_count, 0);
init_waitqueue_head(&i2400mu->rx_wq);
i2400mu->rx_size = PAGE_SIZE - sizeof(struct skb_shared_info);
atomic_set(&i2400mu->do_autopm, 1);
i2400mu->rx_size_auto_shrink = 1;
}
extern int i2400mu_notification_setup(struct i2400mu *);
extern void i2400mu_notification_release(struct i2400mu *);
extern int i2400mu_rx_setup(struct i2400mu *);
extern void i2400mu_rx_release(struct i2400mu *);
extern void i2400mu_rx_kick(struct i2400mu *);
extern int i2400mu_tx_setup(struct i2400mu *);
extern void i2400mu_tx_release(struct i2400mu *);
extern void i2400mu_bus_tx_kick(struct i2400m *);
extern ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *,
const struct i2400m_bootrom_header *,
size_t, int);
extern ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *,
struct i2400m_bootrom_header *,
size_t);
#endif /* #ifndef __I2400M_USB_H__ */

View file

@ -0,0 +1,755 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Declarations for bus-generic internal APIs
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
*
* GENERAL DRIVER ARCHITECTURE
*
* The i2400m driver is split in the following two major parts:
*
* - bus specific driver
* - bus generic driver (this part)
*
* The bus specific driver sets up stuff specific to the bus the
* device is connected to (USB, SDIO, PCI, tam-tam...non-authoritative
* nor binding list) which is basically the device-model management
* (probe/disconnect, etc), moving data from device to kernel and
* back, doing the power saving details and reseting the device.
*
* For details on each bus-specific driver, see it's include file,
* i2400m-BUSNAME.h
*
* The bus-generic functionality break up is:
*
* - Firmware upload: fw.c - takes care of uploading firmware to the
* device. bus-specific driver just needs to provides a way to
* execute boot-mode commands and to reset the device.
*
* - RX handling: rx.c - receives data from the bus-specific code and
* feeds it to the network or WiMAX stack or uses it to modify
* the driver state. bus-specific driver only has to receive
* frames and pass them to this module.
*
* - TX handling: tx.c - manages the TX FIFO queue and provides means
* for the bus-specific TX code to pull data from the FIFO
* queue. bus-specific code just pulls frames from this module
* to sends them to the device.
*
* - netdev glue: netdev.c - interface with Linux networking
* stack. Pass around data frames, and configure when the
* device is up and running or shutdown (through ifconfig up /
* down). Bus-generic only.
*
* - control ops: control.c - implements various commmands for
* controlling the device. bus-generic only.
*
* - device model glue: driver.c - implements helpers for the
* device-model glue done by the bus-specific layer
* (setup/release the driver resources), turning the device on
* and off, handling the device reboots/resets and a few simple
* WiMAX stack ops.
*
* Code is also broken up in linux-glue / device-glue.
*
* Linux glue contains functions that deal mostly with gluing with the
* rest of the Linux kernel.
*
* Device-glue are functions that deal mostly with the way the device
* does things and talk the device's language.
*
* device-glue code is licensed BSD so other open source OSes can take
* it to implement their drivers.
*
*
* APIs AND HEADER FILES
*
* This bus generic code exports three APIs:
*
* - HDI (host-device interface) definitions common to all busses
* (include/linux/wimax/i2400m.h); these can be also used by user
* space code.
* - internal API for the bus-generic code
* - external API for the bus-specific drivers
*
*
* LIFE CYCLE:
*
* When the bus-specific driver probes, it allocates a network device
* with enough space for it's data structue, that must contain a
* &struct i2400m at the top.
*
* On probe, it needs to fill the i2400m members marked as [fill], as
* well as i2400m->wimax_dev.net_dev and call i2400m_setup(). The
* i2400m driver will only register with the WiMAX and network stacks;
* the only access done to the device is to read the MAC address so we
* can register a network device. This calls i2400m_dev_start() to
* load firmware, setup communication with the device and configure it
* for operation.
*
* At this point, control and data communications are possible.
*
* On disconnect/driver unload, the bus-specific disconnect function
* calls i2400m_release() to undo i2400m_setup(). i2400m_dev_stop()
* shuts the firmware down and releases resources uses to communicate
* with the device.
*
* While the device is up, it might reset. The bus-specific driver has
* to catch that situation and call i2400m_dev_reset_handle() to deal
* with it (reset the internal driver structures and go back to square
* one).
*/
#ifndef __I2400M_H__
#define __I2400M_H__
#include <linux/usb.h>
#include <linux/netdevice.h>
#include <linux/completion.h>
#include <linux/rwsem.h>
#include <asm/atomic.h>
#include <net/wimax.h>
#include <linux/wimax/i2400m.h>
#include <asm/byteorder.h>
/* Misc constants */
enum {
/* Firmware uploading */
I2400M_BOOT_RETRIES = 3,
/* Size of the Boot Mode Command buffer */
I2400M_BM_CMD_BUF_SIZE = 16 * 1024,
I2400M_BM_ACK_BUF_SIZE = 256,
};
/* Firmware version we request when pulling the fw image file */
#define I2400M_FW_VERSION "1.3"
/**
* i2400m_reset_type - methods to reset a device
*
* @I2400M_RT_WARM: Reset without device disconnection, device handles
* are kept valid but state is back to power on, with firmware
* re-uploaded.
* @I2400M_RT_COLD: Tell the device to disconnect itself from the bus
* and reconnect. Renders all device handles invalid.
* @I2400M_RT_BUS: Tells the bus to reset the device; last measure
* used when both types above don't work.
*/
enum i2400m_reset_type {
I2400M_RT_WARM, /* first measure */
I2400M_RT_COLD, /* second measure */
I2400M_RT_BUS, /* call in artillery */
};
struct i2400m_reset_ctx;
/**
* struct i2400m - descriptor for an Intel 2400m
*
* Members marked with [fill] must be filled out/initialized before
* calling i2400m_setup().
*
* @bus_tx_block_size: [fill] SDIO imposes a 256 block size, USB 16,
* so we have a tx_blk_size variable that the bus layer sets to
* tell the engine how much of that we need.
*
* @bus_pl_size_max: [fill] Maximum payload size.
*
* @bus_dev_start: [fill] Function called by the bus-generic code
* [i2400m_dev_start()] to setup the bus-specific communications
* to the the device. See LIFE CYCLE above.
*
* NOTE: Doesn't need to upload the firmware, as that is taken
* care of by the bus-generic code.
*
* @bus_dev_stop: [fill] Function called by the bus-generic code
* [i2400m_dev_stop()] to shutdown the bus-specific communications
* to the the device. See LIFE CYCLE above.
*
* This function does not need to reset the device, just tear down
* all the host resources created to handle communication with
* the device.
*
* @bus_tx_kick: [fill] Function called by the bus-generic code to let
* the bus-specific code know that there is data available in the
* TX FIFO for transmission to the device.
*
* This function cannot sleep.
*
* @bus_reset: [fill] Function called by the bus-generic code to reset
* the device in in various ways. Doesn't need to wait for the
* reset to finish.
*
* If warm or cold reset fail, this function is expected to do a
* bus-specific reset (eg: USB reset) to get the device to a
* working state (even if it implies device disconecction).
*
* Note the warm reset is used by the firmware uploader to
* reinitialize the device.
*
* IMPORTANT: this is called very early in the device setup
* process, so it cannot rely on common infrastructure being laid
* out.
*
* @bus_bm_cmd_send: [fill] Function called to send a boot-mode
* command. Flags are defined in 'enum i2400m_bm_cmd_flags'. This
* is synchronous and has to return 0 if ok or < 0 errno code in
* any error condition.
*
* @bus_bm_wait_for_ack: [fill] Function called to wait for a
* boot-mode notification (that can be a response to a previously
* issued command or an asynchronous one). Will read until all the
* indicated size is read or timeout. Reading more or less data
* than asked for is an error condition. Return 0 if ok, < 0 errno
* code on error.
*
* The caller to this function will check if the response is a
* barker that indicates the device going into reset mode.
*
* @bus_fw_name: [fill] name of the firmware image (in most cases,
* they are all the same for a single release, except that they
* have the type of the bus embedded in the name (eg:
* i2400m-fw-X-VERSION.sbcf, where X is the bus name).
*
* @bus_bm_mac_addr_impaired: [fill] Set to true if the device's MAC
* address provided in boot mode is kind of broken and needs to
* be re-read later on.
*
*
* @wimax_dev: WiMAX generic device for linkage into the kernel WiMAX
* stack. Due to the way a net_device is allocated, we need to
* force this to be the first field so that we can get from
* netdev_priv() the right pointer.
*
* @state: device's state (as reported by it)
*
* @state_wq: waitqueue that is woken up whenever the state changes
*
* @tx_lock: spinlock to protect TX members
*
* @tx_buf: FIFO buffer for TX; we queue data here
*
* @tx_in: FIFO index for incoming data. Note this doesn't wrap around
* and it is always greater than @tx_out.
*
* @tx_out: FIFO index for outgoing data
*
* @tx_msg: current TX message that is active in the FIFO for
* appending payloads.
*
* @tx_sequence: current sequence number for TX messages from the
* device to the host.
*
* @tx_msg_size: size of the current message being transmitted by the
* bus-specific code.
*
* @tx_pl_num: total number of payloads sent
*
* @tx_pl_max: maximum number of payloads sent in a TX message
*
* @tx_pl_min: minimum number of payloads sent in a TX message
*
* @tx_num: number of TX messages sent
*
* @tx_size_acc: number of bytes in all TX messages sent
* (this is different to net_dev's statistics as it also counts
* control messages).
*
* @tx_size_min: smallest TX message sent.
*
* @tx_size_max: biggest TX message sent.
*
* @rx_lock: spinlock to protect RX members
*
* @rx_pl_num: total number of payloads received
*
* @rx_pl_max: maximum number of payloads received in a RX message
*
* @rx_pl_min: minimum number of payloads received in a RX message
*
* @rx_num: number of RX messages received
*
* @rx_size_acc: number of bytes in all RX messages received
* (this is different to net_dev's statistics as it also counts
* control messages).
*
* @rx_size_min: smallest RX message received.
*
* @rx_size_max: buggest RX message received.
*
* @init_mutex: Mutex used for serializing the device bringup
* sequence; this way if the device reboots in the middle, we
* don't try to do a bringup again while we are tearing down the
* one that failed.
*
* Can't reuse @msg_mutex because from within the bringup sequence
* we need to send messages to the device and thus use @msg_mutex.
*
* @msg_mutex: mutex used to send control commands to the device (we
* only allow one at a time, per host-device interface design).
*
* @msg_completion: used to wait for an ack to a control command sent
* to the device.
*
* @ack_skb: used to store the actual ack to a control command if the
* reception of the command was successful. Otherwise, a ERR_PTR()
* errno code that indicates what failed with the ack reception.
*
* Only valid after @msg_completion is woken up. Only updateable
* if @msg_completion is armed. Only touched by
* i2400m_msg_to_dev().
*
* Protected by @rx_lock. In theory the command execution flow is
* sequential, but in case the device sends an out-of-phase or
* very delayed response, we need to avoid it trampling current
* execution.
*
* @bm_cmd_buf: boot mode command buffer for composing firmware upload
* commands.
*
* USB can't r/w to stack, vmalloc, etc...as well, we end up
* having to alloc/free a lot to compose commands, so we use these
* for stagging and not having to realloc all the time.
*
* This assumes the code always runs serialized. Only one thread
* can call i2400m_bm_cmd() at the same time.
*
* @bm_ack_buf: boot mode acknoledge buffer for staging reception of
* responses to commands.
*
* See @bm_cmd_buf.
*
* @work_queue: work queue for processing device reports. This
* workqueue cannot be used for processing TX or RX to the device,
* as from it we'll process device reports, which might require
* further communication with the device.
*
* @debugfs_dentry: hookup for debugfs files.
* These have to be in a separate directory, a child of
* (wimax_dev->debugfs_dentry) so they can be removed when the
* module unloads, as we don't keep each dentry.
*/
struct i2400m {
struct wimax_dev wimax_dev; /* FIRST! See doc */
unsigned updown:1; /* Network device is up or down */
unsigned boot_mode:1; /* is the device in boot mode? */
unsigned sboot:1; /* signed or unsigned fw boot */
unsigned ready:1; /* all probing steps done */
u8 trace_msg_from_user; /* echo rx msgs to 'trace' pipe */
/* typed u8 so debugfs/u8 can tweak */
enum i2400m_system_state state;
wait_queue_head_t state_wq; /* Woken up when on state updates */
size_t bus_tx_block_size;
size_t bus_pl_size_max;
int (*bus_dev_start)(struct i2400m *);
void (*bus_dev_stop)(struct i2400m *);
void (*bus_tx_kick)(struct i2400m *);
int (*bus_reset)(struct i2400m *, enum i2400m_reset_type);
ssize_t (*bus_bm_cmd_send)(struct i2400m *,
const struct i2400m_bootrom_header *,
size_t, int flags);
ssize_t (*bus_bm_wait_for_ack)(struct i2400m *,
struct i2400m_bootrom_header *, size_t);
const char *bus_fw_name;
unsigned bus_bm_mac_addr_impaired:1;
spinlock_t tx_lock; /* protect TX state */
void *tx_buf;
size_t tx_in, tx_out;
struct i2400m_msg_hdr *tx_msg;
size_t tx_sequence, tx_msg_size;
/* TX stats */
unsigned tx_pl_num, tx_pl_max, tx_pl_min,
tx_num, tx_size_acc, tx_size_min, tx_size_max;
/* RX stats */
spinlock_t rx_lock; /* protect RX state */
unsigned rx_pl_num, rx_pl_max, rx_pl_min,
rx_num, rx_size_acc, rx_size_min, rx_size_max;
struct mutex msg_mutex; /* serialize command execution */
struct completion msg_completion;
struct sk_buff *ack_skb; /* protected by rx_lock */
void *bm_ack_buf; /* for receiving acks over USB */
void *bm_cmd_buf; /* for issuing commands over USB */
struct workqueue_struct *work_queue;
struct mutex init_mutex; /* protect bringup seq */
struct i2400m_reset_ctx *reset_ctx; /* protected by init_mutex */
struct work_struct wake_tx_ws;
struct sk_buff *wake_tx_skb;
struct dentry *debugfs_dentry;
};
/*
* Initialize a 'struct i2400m' from all zeroes
*
* This is a bus-generic API call.
*/
static inline
void i2400m_init(struct i2400m *i2400m)
{
wimax_dev_init(&i2400m->wimax_dev);
i2400m->boot_mode = 1;
init_waitqueue_head(&i2400m->state_wq);
spin_lock_init(&i2400m->tx_lock);
i2400m->tx_pl_min = UINT_MAX;
i2400m->tx_size_min = UINT_MAX;
spin_lock_init(&i2400m->rx_lock);
i2400m->rx_pl_min = UINT_MAX;
i2400m->rx_size_min = UINT_MAX;
mutex_init(&i2400m->msg_mutex);
init_completion(&i2400m->msg_completion);
mutex_init(&i2400m->init_mutex);
/* wake_tx_ws is initialized in i2400m_tx_setup() */
}
/*
* Bus-generic internal APIs
* -------------------------
*/
static inline
struct i2400m *wimax_dev_to_i2400m(struct wimax_dev *wimax_dev)
{
return container_of(wimax_dev, struct i2400m, wimax_dev);
}
static inline
struct i2400m *net_dev_to_i2400m(struct net_device *net_dev)
{
return wimax_dev_to_i2400m(netdev_priv(net_dev));
}
/*
* Boot mode support
*/
/**
* i2400m_bm_cmd_flags - flags to i2400m_bm_cmd()
*
* @I2400M_BM_CMD_RAW: send the command block as-is, without doing any
* extra processing for adding CRC.
*/
enum i2400m_bm_cmd_flags {
I2400M_BM_CMD_RAW = 1 << 2,
};
/**
* i2400m_bri - Boot-ROM indicators
*
* Flags for i2400m_bootrom_init() and i2400m_dev_bootstrap() [which
* are passed from things like i2400m_setup()]. Can be combined with
* |.
*
* @I2400M_BRI_SOFT: The device rebooted already and a reboot
* barker received, proceed directly to ack the boot sequence.
* @I2400M_BRI_NO_REBOOT: Do not reboot the device and proceed
* directly to wait for a reboot barker from the device.
* @I2400M_BRI_MAC_REINIT: We need to reinitialize the boot
* rom after reading the MAC adress. This is quite a dirty hack,
* if you ask me -- the device requires the bootrom to be
* intialized after reading the MAC address.
*/
enum i2400m_bri {
I2400M_BRI_SOFT = 1 << 1,
I2400M_BRI_NO_REBOOT = 1 << 2,
I2400M_BRI_MAC_REINIT = 1 << 3,
};
extern void i2400m_bm_cmd_prepare(struct i2400m_bootrom_header *);
extern int i2400m_dev_bootstrap(struct i2400m *, enum i2400m_bri);
extern int i2400m_read_mac_addr(struct i2400m *);
extern int i2400m_bootrom_init(struct i2400m *, enum i2400m_bri);
/* Make/grok boot-rom header commands */
static inline
__le32 i2400m_brh_command(enum i2400m_brh_opcode opcode, unsigned use_checksum,
unsigned direct_access)
{
return cpu_to_le32(
I2400M_BRH_SIGNATURE
| (direct_access ? I2400M_BRH_DIRECT_ACCESS : 0)
| I2400M_BRH_RESPONSE_REQUIRED /* response always required */
| (use_checksum ? I2400M_BRH_USE_CHECKSUM : 0)
| (opcode & I2400M_BRH_OPCODE_MASK));
}
static inline
void i2400m_brh_set_opcode(struct i2400m_bootrom_header *hdr,
enum i2400m_brh_opcode opcode)
{
hdr->command = cpu_to_le32(
(le32_to_cpu(hdr->command) & ~I2400M_BRH_OPCODE_MASK)
| (opcode & I2400M_BRH_OPCODE_MASK));
}
static inline
unsigned i2400m_brh_get_opcode(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_OPCODE_MASK;
}
static inline
unsigned i2400m_brh_get_response(const struct i2400m_bootrom_header *hdr)
{
return (le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_MASK)
>> I2400M_BRH_RESPONSE_SHIFT;
}
static inline
unsigned i2400m_brh_get_use_checksum(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_USE_CHECKSUM;
}
static inline
unsigned i2400m_brh_get_response_required(
const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_RESPONSE_REQUIRED;
}
static inline
unsigned i2400m_brh_get_direct_access(const struct i2400m_bootrom_header *hdr)
{
return le32_to_cpu(hdr->command) & I2400M_BRH_DIRECT_ACCESS;
}
static inline
unsigned i2400m_brh_get_signature(const struct i2400m_bootrom_header *hdr)
{
return (le32_to_cpu(hdr->command) & I2400M_BRH_SIGNATURE_MASK)
>> I2400M_BRH_SIGNATURE_SHIFT;
}
/*
* Driver / device setup and internal functions
*/
extern void i2400m_netdev_setup(struct net_device *net_dev);
extern int i2400m_tx_setup(struct i2400m *);
extern void i2400m_wake_tx_work(struct work_struct *);
extern void i2400m_tx_release(struct i2400m *);
extern void i2400m_net_rx(struct i2400m *, struct sk_buff *, unsigned,
const void *, int);
enum i2400m_pt;
extern int i2400m_tx(struct i2400m *, const void *, size_t, enum i2400m_pt);
#ifdef CONFIG_DEBUG_FS
extern int i2400m_debugfs_add(struct i2400m *);
extern void i2400m_debugfs_rm(struct i2400m *);
#else
static inline int i2400m_debugfs_add(struct i2400m *i2400m)
{
return 0;
}
static inline void i2400m_debugfs_rm(struct i2400m *i2400m) {}
#endif
/* Called by _dev_start()/_dev_stop() to initialize the device itself */
extern int i2400m_dev_initialize(struct i2400m *);
extern void i2400m_dev_shutdown(struct i2400m *);
extern struct attribute_group i2400m_dev_attr_group;
extern int i2400m_schedule_work(struct i2400m *,
void (*)(struct work_struct *), gfp_t);
/* HDI message's payload description handling */
static inline
size_t i2400m_pld_size(const struct i2400m_pld *pld)
{
return I2400M_PLD_SIZE_MASK & le32_to_cpu(pld->val);
}
static inline
enum i2400m_pt i2400m_pld_type(const struct i2400m_pld *pld)
{
return (I2400M_PLD_TYPE_MASK & le32_to_cpu(pld->val))
>> I2400M_PLD_TYPE_SHIFT;
}
static inline
void i2400m_pld_set(struct i2400m_pld *pld, size_t size,
enum i2400m_pt type)
{
pld->val = cpu_to_le32(
((type << I2400M_PLD_TYPE_SHIFT) & I2400M_PLD_TYPE_MASK)
| (size & I2400M_PLD_SIZE_MASK));
}
/*
* API for the bus-specific drivers
* --------------------------------
*/
static inline
struct i2400m *i2400m_get(struct i2400m *i2400m)
{
dev_hold(i2400m->wimax_dev.net_dev);
return i2400m;
}
static inline
void i2400m_put(struct i2400m *i2400m)
{
dev_put(i2400m->wimax_dev.net_dev);
}
extern int i2400m_dev_reset_handle(struct i2400m *);
/*
* _setup()/_release() are called by the probe/disconnect functions of
* the bus-specific drivers.
*/
extern int i2400m_setup(struct i2400m *, enum i2400m_bri bm_flags);
extern void i2400m_release(struct i2400m *);
extern int i2400m_rx(struct i2400m *, struct sk_buff *);
extern struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *, size_t *);
extern void i2400m_tx_msg_sent(struct i2400m *);
static const __le32 i2400m_NBOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_NBOOT_BARKER),
__constant_cpu_to_le32(I2400M_NBOOT_BARKER),
__constant_cpu_to_le32(I2400M_NBOOT_BARKER),
__constant_cpu_to_le32(I2400M_NBOOT_BARKER)
};
static const __le32 i2400m_SBOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_SBOOT_BARKER),
__constant_cpu_to_le32(I2400M_SBOOT_BARKER),
__constant_cpu_to_le32(I2400M_SBOOT_BARKER),
__constant_cpu_to_le32(I2400M_SBOOT_BARKER)
};
/*
* Utility functions
*/
static inline
struct device *i2400m_dev(struct i2400m *i2400m)
{
return i2400m->wimax_dev.net_dev->dev.parent;
}
/*
* Helper for scheduling simple work functions
*
* This struct can get any kind of payload attached (normally in the
* form of a struct where you pack the stuff you want to pass to the
* _work function).
*/
struct i2400m_work {
struct work_struct ws;
struct i2400m *i2400m;
u8 pl[0];
};
extern int i2400m_queue_work(struct i2400m *,
void (*)(struct work_struct *), gfp_t,
const void *, size_t);
extern int i2400m_msg_check_status(const struct i2400m_l3l4_hdr *,
char *, size_t);
extern int i2400m_msg_size_check(struct i2400m *,
const struct i2400m_l3l4_hdr *, size_t);
extern struct sk_buff *i2400m_msg_to_dev(struct i2400m *, const void *, size_t);
extern void i2400m_msg_to_dev_cancel_wait(struct i2400m *, int);
extern void i2400m_msg_ack_hook(struct i2400m *,
const struct i2400m_l3l4_hdr *, size_t);
extern void i2400m_report_hook(struct i2400m *,
const struct i2400m_l3l4_hdr *, size_t);
extern int i2400m_cmd_enter_powersave(struct i2400m *);
extern int i2400m_cmd_get_state(struct i2400m *);
extern int i2400m_cmd_exit_idle(struct i2400m *);
extern struct sk_buff *i2400m_get_device_info(struct i2400m *);
extern int i2400m_firmware_check(struct i2400m *);
extern int i2400m_set_init_config(struct i2400m *,
const struct i2400m_tlv_hdr **, size_t);
static inline
struct usb_endpoint_descriptor *usb_get_epd(struct usb_interface *iface, int ep)
{
return &iface->cur_altsetting->endpoint[ep].desc;
}
extern int i2400m_op_rfkill_sw_toggle(struct wimax_dev *,
enum wimax_rf_state);
extern void i2400m_report_tlv_rf_switches_status(
struct i2400m *, const struct i2400m_tlv_rf_switches_status *);
/*
* Do a millisecond-sleep for allowing wireshark to dump all the data
* packets. Used only for debugging.
*/
static inline
void __i2400m_msleep(unsigned ms)
{
#if 1
#else
msleep(ms);
#endif
}
/* Module parameters */
extern int i2400m_idle_mode_disabled;
#endif /* #ifndef __I2400M_H__ */

View file

@ -0,0 +1,524 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Glue with the networking stack
*
*
* Copyright (C) 2007 Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* This implements an ethernet device for the i2400m.
*
* We fake being an ethernet device to simplify the support from user
* space and from the other side. The world is (sadly) configured to
* take in only Ethernet devices...
*
* Because of this, currently there is an copy-each-rxed-packet
* overhead on the RX path. Each IP packet has to be reallocated to
* add an ethernet header (as there is no space in what we get from
* the device). This is a known drawback and coming versions of the
* device's firmware are being changed to add header space that can be
* used to insert the ethernet header without having to reallocate and
* copy.
*
* TX error handling is tricky; because we have to FIFO/queue the
* buffers for transmission (as the hardware likes it aggregated), we
* just give the skb to the TX subsystem and by the time it is
* transmitted, we have long forgotten about it. So we just don't care
* too much about it.
*
* Note that when the device is in idle mode with the basestation, we
* need to negotiate coming back up online. That involves negotiation
* and possible user space interaction. Thus, we defer to a workqueue
* to do all that. By default, we only queue a single packet and drop
* the rest, as potentially the time to go back from idle to normal is
* long.
*
* ROADMAP
*
* i2400m_open Called on ifconfig up
* i2400m_stop Called on ifconfig down
*
* i2400m_hard_start_xmit Called by the network stack to send a packet
* i2400m_net_wake_tx Wake up device from basestation-IDLE & TX
* i2400m_wake_tx_work
* i2400m_cmd_exit_idle
* i2400m_tx
* i2400m_net_tx TX a data frame
* i2400m_tx
*
* i2400m_change_mtu Called on ifconfig mtu XXX
*
* i2400m_tx_timeout Called when the device times out
*
* i2400m_net_rx Called by the RX code when a data frame is
* available.
* i2400m_netdev_setup Called to setup all the netdev stuff from
* alloc_netdev.
*/
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include "i2400m.h"
#define D_SUBMODULE netdev
#include "debug-levels.h"
enum {
/* netdev interface */
/*
* Out of NWG spec (R1_v1.2.2), 3.3.3 ASN Bearer Plane MTU Size
*
* The MTU is 1400 or less
*/
I2400M_MAX_MTU = 1400,
I2400M_TX_TIMEOUT = HZ,
I2400M_TX_QLEN = 5,
};
static
int i2400m_open(struct net_device *net_dev)
{
int result;
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
if (i2400m->ready == 0) {
dev_err(dev, "Device is still initializing\n");
result = -EBUSY;
} else
result = 0;
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = %d\n",
net_dev, i2400m, result);
return result;
}
/*
*
* On kernel versions where cancel_work_sync() didn't return anything,
* we rely on wake_tx_skb() being non-NULL.
*/
static
int i2400m_stop(struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(net_dev %p [i2400m %p])\n", net_dev, i2400m);
/* See i2400m_hard_start_xmit(), references are taken there
* and here we release them if the work was still
* pending. Note we can't differentiate work not pending vs
* never scheduled, so the NULL check does that. */
if (cancel_work_sync(&i2400m->wake_tx_ws) == 0
&& i2400m->wake_tx_skb != NULL) {
unsigned long flags;
struct sk_buff *wake_tx_skb;
spin_lock_irqsave(&i2400m->tx_lock, flags);
wake_tx_skb = i2400m->wake_tx_skb; /* compat help */
i2400m->wake_tx_skb = NULL; /* compat help */
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
i2400m_put(i2400m);
kfree_skb(wake_tx_skb);
}
d_fnend(3, dev, "(net_dev %p [i2400m %p]) = 0\n", net_dev, i2400m);
return 0;
}
/*
* Wake up the device and transmit a held SKB, then restart the net queue
*
* When the device goes into basestation-idle mode, we need to tell it
* to exit that mode; it will negotiate with the base station, user
* space may have to intervene to rehandshake crypto and then tell us
* when it is ready to transmit the packet we have "queued". Still we
* need to give it sometime after it reports being ok.
*
* On error, there is not much we can do. If the error was on TX, we
* still wake the queue up to see if the next packet will be luckier.
*
* If _cmd_exit_idle() fails...well, it could be many things; most
* commonly it is that something else took the device out of IDLE mode
* (for example, the base station). In that case we get an -EILSEQ and
* we are just going to ignore that one. If the device is back to
* connected, then fine -- if it is someother state, the packet will
* be dropped anyway.
*/
void i2400m_wake_tx_work(struct work_struct *ws)
{
int result;
struct i2400m *i2400m = container_of(ws, struct i2400m, wake_tx_ws);
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb = i2400m->wake_tx_skb;
unsigned long flags;
spin_lock_irqsave(&i2400m->tx_lock, flags);
skb = i2400m->wake_tx_skb;
i2400m->wake_tx_skb = NULL;
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnstart(3, dev, "(ws %p i2400m %p skb %p)\n", ws, i2400m, skb);
result = -EINVAL;
if (skb == NULL) {
dev_err(dev, "WAKE&TX: skb dissapeared!\n");
goto out_put;
}
result = i2400m_cmd_exit_idle(i2400m);
if (result == -EILSEQ)
result = 0;
if (result < 0) {
dev_err(dev, "WAKE&TX: device didn't get out of idle: "
"%d\n", result);
goto error;
}
result = wait_event_timeout(i2400m->state_wq,
i2400m->state != I2400M_SS_IDLE, 5 * HZ);
if (result == 0)
result = -ETIMEDOUT;
if (result < 0) {
dev_err(dev, "WAKE&TX: error waiting for device to exit IDLE: "
"%d\n", result);
goto error;
}
msleep(20); /* device still needs some time or it drops it */
result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
netif_wake_queue(i2400m->wimax_dev.net_dev);
error:
kfree_skb(skb); /* refcount transferred by _hard_start_xmit() */
out_put:
i2400m_put(i2400m);
d_fnend(3, dev, "(ws %p i2400m %p skb %p) = void [%d]\n",
ws, i2400m, skb, result);
}
/*
* Prepare the data payload TX header
*
* The i2400m expects a 4 byte header in front of a data packet.
*
* Because we pretend to be an ethernet device, this packet comes with
* an ethernet header. Pull it and push our header.
*/
static
void i2400m_tx_prep_header(struct sk_buff *skb)
{
struct i2400m_pl_data_hdr *pl_hdr;
skb_pull(skb, ETH_HLEN);
pl_hdr = (struct i2400m_pl_data_hdr *) skb_push(skb, sizeof(*pl_hdr));
pl_hdr->reserved = 0;
}
/*
* TX an skb to an idle device
*
* When the device is in basestation-idle mode, we need to wake it up
* and then TX. So we queue a work_struct for doing so.
*
* We need to get an extra ref for the skb (so it is not dropped), as
* well as be careful not to queue more than one request (won't help
* at all). If more than one request comes or there are errors, we
* just drop the packets (see i2400m_hard_start_xmit()).
*/
static
int i2400m_net_wake_tx(struct i2400m *i2400m, struct net_device *net_dev,
struct sk_buff *skb)
{
int result;
struct device *dev = i2400m_dev(i2400m);
unsigned long flags;
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
if (net_ratelimit()) {
d_printf(3, dev, "WAKE&NETTX: "
"skb %p sending %d bytes to radio\n",
skb, skb->len);
d_dump(4, dev, skb->data, skb->len);
}
/* We hold a ref count for i2400m and skb, so when
* stopping() the device, we need to cancel that work
* and if pending, release those resources. */
result = 0;
spin_lock_irqsave(&i2400m->tx_lock, flags);
if (!work_pending(&i2400m->wake_tx_ws)) {
netif_stop_queue(net_dev);
i2400m_get(i2400m);
i2400m->wake_tx_skb = skb_get(skb); /* transfer ref count */
i2400m_tx_prep_header(skb);
result = schedule_work(&i2400m->wake_tx_ws);
WARN_ON(result == 0);
}
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
if (result == 0) {
/* Yes, this happens even if we stopped the
* queue -- blame the queue disciplines that
* queue without looking -- I guess there is a reason
* for that. */
if (net_ratelimit())
d_printf(1, dev, "NETTX: device exiting idle, "
"dropping skb %p, queue running %d\n",
skb, netif_queue_stopped(net_dev));
result = -EBUSY;
}
d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result);
return result;
}
/*
* Transmit a packet to the base station on behalf of the network stack.
*
* Returns: 0 if ok, < 0 errno code on error.
*
* We need to pull the ethernet header and add the hardware header,
* which is currently set to all zeroes and reserved.
*/
static
int i2400m_net_tx(struct i2400m *i2400m, struct net_device *net_dev,
struct sk_buff *skb)
{
int result;
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p net_dev %p skb %p)\n",
i2400m, net_dev, skb);
/* FIXME: check eth hdr, only IPv4 is routed by the device as of now */
net_dev->trans_start = jiffies;
i2400m_tx_prep_header(skb);
d_printf(3, dev, "NETTX: skb %p sending %d bytes to radio\n",
skb, skb->len);
d_dump(4, dev, skb->data, skb->len);
result = i2400m_tx(i2400m, skb->data, skb->len, I2400M_PT_DATA);
d_fnend(3, dev, "(i2400m %p net_dev %p skb %p) = %d\n",
i2400m, net_dev, skb, result);
return result;
}
/*
* Transmit a packet to the base station on behalf of the network stack
*
*
* Returns: NETDEV_TX_OK (always, even in case of error)
*
* In case of error, we just drop it. Reasons:
*
* - we add a hw header to each skb, and if the network stack
* retries, we have no way to know if that skb has it or not.
*
* - network protocols have their own drop-recovery mechanisms
*
* - there is not much else we can do
*
* If the device is idle, we need to wake it up; that is an operation
* that will sleep. See i2400m_net_wake_tx() for details.
*/
static
int i2400m_hard_start_xmit(struct sk_buff *skb,
struct net_device *net_dev)
{
int result;
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(skb %p net_dev %p)\n", skb, net_dev);
if (i2400m->state == I2400M_SS_IDLE)
result = i2400m_net_wake_tx(i2400m, net_dev, skb);
else
result = i2400m_net_tx(i2400m, net_dev, skb);
if (result < 0)
net_dev->stats.tx_dropped++;
else {
net_dev->stats.tx_packets++;
net_dev->stats.tx_bytes += skb->len;
}
kfree_skb(skb);
result = NETDEV_TX_OK;
d_fnend(3, dev, "(skb %p net_dev %p) = %d\n", skb, net_dev, result);
return result;
}
static
int i2400m_change_mtu(struct net_device *net_dev, int new_mtu)
{
int result;
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct device *dev = i2400m_dev(i2400m);
if (new_mtu >= I2400M_MAX_MTU) {
dev_err(dev, "Cannot change MTU to %d (max is %d)\n",
new_mtu, I2400M_MAX_MTU);
result = -EINVAL;
} else {
net_dev->mtu = new_mtu;
result = 0;
}
return result;
}
static
void i2400m_tx_timeout(struct net_device *net_dev)
{
/*
* We might want to kick the device
*
* There is not much we can do though, as the device requires
* that we send the data aggregated. By the time we receive
* this, there might be data pending to be sent or not...
*/
net_dev->stats.tx_errors++;
return;
}
/*
* Create a fake ethernet header
*
* For emulating an ethernet device, every received IP header has to
* be prefixed with an ethernet header.
*
* What we receive has (potentially) many IP packets concatenated with
* no ETH_HLEN bytes prefixed. Thus there is no space for an eth
* header.
*
* We would have to reallocate or do ugly fragment tricks in order to
* add it.
*
* But what we do is use the header space of the RX transaction
* (*msg_hdr) as we don't need it anymore; then we'll point all the
* data skbs there, as they share the same backing store.
*
* We only support IPv4 for v3 firmware.
*/
static
void i2400m_rx_fake_eth_header(struct net_device *net_dev,
void *_eth_hdr)
{
struct ethhdr *eth_hdr = _eth_hdr;
memcpy(eth_hdr->h_dest, net_dev->dev_addr, sizeof(eth_hdr->h_dest));
memset(eth_hdr->h_source, 0, sizeof(eth_hdr->h_dest));
eth_hdr->h_proto = __constant_cpu_to_be16(ETH_P_IP);
}
/*
* i2400m_net_rx - pass a network packet to the stack
*
* @i2400m: device instance
* @skb_rx: the skb where the buffer pointed to by @buf is
* @i: 1 if payload is the only one
* @buf: pointer to the buffer containing the data
* @len: buffer's length
*
* We just clone the skb and set it up so that it's skb->data pointer
* points to "buf" and it's length.
*
* Note that if the payload is the last (or the only one) in a
* multi-payload message, we don't clone the SKB but just reuse it.
*
* This function is normally run from a thread context. However, we
* still use netif_rx() instead of netif_receive_skb() as was
* recommended in the mailing list. Reason is in some stress tests
* when sending/receiving a lot of data we seem to hit a softlock in
* the kernel's TCP implementation [aroudn tcp_delay_timer()]. Using
* netif_rx() took care of the issue.
*
* This is, of course, still open to do more research on why running
* with netif_receive_skb() hits this softlock. FIXME.
*
* FIXME: currently we don't do any efforts at distinguishing if what
* we got was an IPv4 or IPv6 header, to setup the protocol field
* correctly.
*/
void i2400m_net_rx(struct i2400m *i2400m, struct sk_buff *skb_rx,
unsigned i, const void *buf, int buf_len)
{
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *skb;
d_fnstart(2, dev, "(i2400m %p buf %p buf_len %d)\n",
i2400m, buf, buf_len);
if (i) {
skb = skb_get(skb_rx);
d_printf(2, dev, "RX: reusing first payload skb %p\n", skb);
skb_pull(skb, buf - (void *) skb->data);
skb_trim(skb, (void *) skb_end_pointer(skb) - buf);
} else {
/* Yes, this is bad -- a lot of overhead -- see
* comments at the top of the file */
skb = __netdev_alloc_skb(net_dev, buf_len, GFP_KERNEL);
if (skb == NULL) {
dev_err(dev, "NETRX: no memory to realloc skb\n");
net_dev->stats.rx_dropped++;
goto error_skb_realloc;
}
memcpy(skb_put(skb, buf_len), buf, buf_len);
}
i2400m_rx_fake_eth_header(i2400m->wimax_dev.net_dev,
skb->data - ETH_HLEN);
skb_set_mac_header(skb, -ETH_HLEN);
skb->dev = i2400m->wimax_dev.net_dev;
skb->protocol = htons(ETH_P_IP);
net_dev->stats.rx_packets++;
net_dev->stats.rx_bytes += buf_len;
d_printf(3, dev, "NETRX: receiving %d bytes to network stack\n",
buf_len);
d_dump(4, dev, buf, buf_len);
netif_rx_ni(skb); /* see notes in function header */
error_skb_realloc:
d_fnend(2, dev, "(i2400m %p buf %p buf_len %d) = void\n",
i2400m, buf, buf_len);
}
/**
* i2400m_netdev_setup - Setup setup @net_dev's i2400m private data
*
* Called by alloc_netdev()
*/
void i2400m_netdev_setup(struct net_device *net_dev)
{
d_fnstart(3, NULL, "(net_dev %p)\n", net_dev);
ether_setup(net_dev);
net_dev->mtu = I2400M_MAX_MTU;
net_dev->tx_queue_len = I2400M_TX_QLEN;
net_dev->features =
NETIF_F_VLAN_CHALLENGED
| NETIF_F_HIGHDMA;
net_dev->flags =
IFF_NOARP /* i2400m is apure IP device */
& (~IFF_BROADCAST /* i2400m is P2P */
& ~IFF_MULTICAST);
net_dev->watchdog_timeo = I2400M_TX_TIMEOUT;
net_dev->open = i2400m_open;
net_dev->stop = i2400m_stop;
net_dev->hard_start_xmit = i2400m_hard_start_xmit;
net_dev->change_mtu = i2400m_change_mtu;
net_dev->tx_timeout = i2400m_tx_timeout;
d_fnend(3, NULL, "(net_dev %p) = void\n", net_dev);
}
EXPORT_SYMBOL_GPL(i2400m_netdev_setup);

View file

@ -0,0 +1,207 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Implement backend for the WiMAX stack rfkill support
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* The WiMAX kernel stack integrates into RF-Kill and keeps the
* switches's status. We just need to:
*
* - report changes in the HW RF Kill switch [with
* wimax_rfkill_{sw,hw}_report(), which happens when we detect those
* indications coming through hardware reports]. We also do it on
* initialization to let the stack know the intial HW state.
*
* - implement indications from the stack to change the SW RF Kill
* switch (coming from sysfs, the wimax stack or user space).
*/
#include "i2400m.h"
#include <linux/wimax/i2400m.h>
#define D_SUBMODULE rfkill
#include "debug-levels.h"
/*
* Return true if the i2400m radio is in the requested wimax_rf_state state
*
*/
static
int i2400m_radio_is(struct i2400m *i2400m, enum wimax_rf_state state)
{
if (state == WIMAX_RF_OFF)
return i2400m->state == I2400M_SS_RF_OFF
|| i2400m->state == I2400M_SS_RF_SHUTDOWN;
else if (state == WIMAX_RF_ON)
/* state == WIMAX_RF_ON */
return i2400m->state != I2400M_SS_RF_OFF
&& i2400m->state != I2400M_SS_RF_SHUTDOWN;
else
BUG();
}
/*
* WiMAX stack operation: implement SW RFKill toggling
*
* @wimax_dev: device descriptor
* @skb: skb where the message has been received; skb->data is
* expected to point to the message payload.
* @genl_info: passed by the generic netlink layer
*
* Generic Netlink will call this function when a message is sent from
* userspace to change the software RF-Kill switch status.
*
* This function will set the device's sofware RF-Kill switch state to
* match what is requested.
*
* NOTE: the i2400m has a strict state machine; we can only set the
* RF-Kill switch when it is on, the HW RF-Kill is on and the
* device is initialized. So we ignore errors steaming from not
* being in the right state (-EILSEQ).
*/
int i2400m_op_rfkill_sw_toggle(struct wimax_dev *wimax_dev,
enum wimax_rf_state state)
{
int result;
struct i2400m *i2400m = wimax_dev_to_i2400m(wimax_dev);
struct device *dev = i2400m_dev(i2400m);
struct sk_buff *ack_skb;
struct {
struct i2400m_l3l4_hdr hdr;
struct i2400m_tlv_rf_operation sw_rf;
} __attribute__((packed)) *cmd;
char strerr[32];
d_fnstart(4, dev, "(wimax_dev %p state %d)\n", wimax_dev, state);
result = -ENOMEM;
cmd = kzalloc(sizeof(*cmd), GFP_KERNEL);
if (cmd == NULL)
goto error_alloc;
cmd->hdr.type = cpu_to_le16(I2400M_MT_CMD_RF_CONTROL);
cmd->hdr.length = sizeof(cmd->sw_rf);
cmd->hdr.version = cpu_to_le16(I2400M_L3L4_VERSION);
cmd->sw_rf.hdr.type = cpu_to_le16(I2400M_TLV_RF_OPERATION);
cmd->sw_rf.hdr.length = cpu_to_le16(sizeof(cmd->sw_rf.status));
switch (state) {
case WIMAX_RF_OFF: /* RFKILL ON, radio OFF */
cmd->sw_rf.status = cpu_to_le32(2);
break;
case WIMAX_RF_ON: /* RFKILL OFF, radio ON */
cmd->sw_rf.status = cpu_to_le32(1);
break;
default:
BUG();
}
ack_skb = i2400m_msg_to_dev(i2400m, cmd, sizeof(*cmd));
result = PTR_ERR(ack_skb);
if (IS_ERR(ack_skb)) {
dev_err(dev, "Failed to issue 'RF Control' command: %d\n",
result);
goto error_msg_to_dev;
}
result = i2400m_msg_check_status(wimax_msg_data(ack_skb),
strerr, sizeof(strerr));
if (result < 0) {
dev_err(dev, "'RF Control' (0x%04x) command failed: %d - %s\n",
I2400M_MT_CMD_RF_CONTROL, result, strerr);
goto error_cmd;
}
/* Now we wait for the state to change to RADIO_OFF or RADIO_ON */
result = wait_event_timeout(
i2400m->state_wq, i2400m_radio_is(i2400m, state),
5 * HZ);
if (result == 0)
result = -ETIMEDOUT;
if (result < 0)
dev_err(dev, "Error waiting for device to toggle RF state: "
"%d\n", result);
result = 0;
error_cmd:
kfree_skb(ack_skb);
error_msg_to_dev:
error_alloc:
d_fnend(4, dev, "(wimax_dev %p state %d) = %d\n",
wimax_dev, state, result);
return result;
}
/*
* Inform the WiMAX stack of changes in the RF Kill switches reported
* by the device
*
* @i2400m: device descriptor
* @rfss: TLV for RF Switches status; already validated
*
* NOTE: the reports on RF switch status cannot be trusted
* or used until the device is in a state of RADIO_OFF
* or greater.
*/
void i2400m_report_tlv_rf_switches_status(
struct i2400m *i2400m,
const struct i2400m_tlv_rf_switches_status *rfss)
{
struct device *dev = i2400m_dev(i2400m);
enum i2400m_rf_switch_status hw, sw;
enum wimax_st wimax_state;
sw = le32_to_cpu(rfss->sw_rf_switch);
hw = le32_to_cpu(rfss->hw_rf_switch);
d_fnstart(3, dev, "(i2400m %p rfss %p [hw %u sw %u])\n",
i2400m, rfss, hw, sw);
/* We only process rw switch evens when the device has been
* fully initialized */
wimax_state = wimax_state_get(&i2400m->wimax_dev);
if (wimax_state < WIMAX_ST_RADIO_OFF) {
d_printf(3, dev, "ignoring RF switches report, state %u\n",
wimax_state);
goto out;
}
switch (sw) {
case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */
wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_ON);
break;
case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */
wimax_report_rfkill_sw(&i2400m->wimax_dev, WIMAX_RF_OFF);
break;
default:
dev_err(dev, "HW BUG? Unknown RF SW state 0x%x\n", sw);
}
switch (hw) {
case I2400M_RF_SWITCH_ON: /* RF Kill disabled (radio on) */
wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_ON);
break;
case I2400M_RF_SWITCH_OFF: /* RF Kill enabled (radio off) */
wimax_report_rfkill_hw(&i2400m->wimax_dev, WIMAX_RF_OFF);
break;
default:
dev_err(dev, "HW BUG? Unknown RF HW state 0x%x\n", hw);
}
out:
d_fnend(3, dev, "(i2400m %p rfss %p [hw %u sw %u]) = void\n",
i2400m, rfss, hw, sw);
}

View file

@ -0,0 +1,534 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Handle incoming traffic and deliver it to the control or data planes
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Use skb_clone(), break up processing in chunks
* - Split transport/device specific
* - Make buffer size dynamic to exert less memory pressure
*
*
* This handles the RX path.
*
* We receive an RX message from the bus-specific driver, which
* contains one or more payloads that have potentially different
* destinataries (data or control paths).
*
* So we just take that payload from the transport specific code in
* the form of an skb, break it up in chunks (a cloned skb each in the
* case of network packets) and pass it to netdev or to the
* command/ack handler (and from there to the WiMAX stack).
*
* PROTOCOL FORMAT
*
* The format of the buffer is:
*
* HEADER (struct i2400m_msg_hdr)
* PAYLOAD DESCRIPTOR 0 (struct i2400m_pld)
* PAYLOAD DESCRIPTOR 1
* ...
* PAYLOAD DESCRIPTOR N
* PAYLOAD 0 (raw bytes)
* PAYLOAD 1
* ...
* PAYLOAD N
*
* See tx.c for a deeper description on alignment requirements and
* other fun facts of it.
*
* ROADMAP
*
* i2400m_rx
* i2400m_rx_msg_hdr_check
* i2400m_rx_pl_descr_check
* i2400m_rx_payload
* i2400m_net_rx
* i2400m_rx_ctl
* i2400m_msg_size_check
* i2400m_report_hook_work [in a workqueue]
* i2400m_report_hook
* wimax_msg_to_user
* i2400m_rx_ctl_ack
* wimax_msg_to_user_alloc
* i2400m_rx_trace
* i2400m_msg_size_check
* wimax_msg
*/
#include <linux/kernel.h>
#include <linux/if_arp.h>
#include <linux/netdevice.h>
#include <linux/workqueue.h>
#include "i2400m.h"
#define D_SUBMODULE rx
#include "debug-levels.h"
struct i2400m_report_hook_args {
struct sk_buff *skb_rx;
const struct i2400m_l3l4_hdr *l3l4_hdr;
size_t size;
};
/*
* Execute i2400m_report_hook in a workqueue
*
* Unpacks arguments from the deferred call, executes it and then
* drops the references.
*
* Obvious NOTE: References are needed because we are a separate
* thread; otherwise the buffer changes under us because it is
* released by the original caller.
*/
static
void i2400m_report_hook_work(struct work_struct *ws)
{
struct i2400m_work *iw =
container_of(ws, struct i2400m_work, ws);
struct i2400m_report_hook_args *args = (void *) iw->pl;
i2400m_report_hook(iw->i2400m, args->l3l4_hdr, args->size);
kfree_skb(args->skb_rx);
i2400m_put(iw->i2400m);
kfree(iw);
}
/*
* Process an ack to a command
*
* @i2400m: device descriptor
* @payload: pointer to message
* @size: size of the message
*
* Pass the acknodledgment (in an skb) to the thread that is waiting
* for it in i2400m->msg_completion.
*
* We need to coordinate properly with the thread waiting for the
* ack. Check if it is waiting or if it is gone. We loose the spinlock
* to avoid allocating on atomic contexts (yeah, could use GFP_ATOMIC,
* but this is not so speed critical).
*/
static
void i2400m_rx_ctl_ack(struct i2400m *i2400m,
const void *payload, size_t size)
{
struct device *dev = i2400m_dev(i2400m);
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
unsigned long flags;
struct sk_buff *ack_skb;
/* Anyone waiting for an answer? */
spin_lock_irqsave(&i2400m->rx_lock, flags);
if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) {
dev_err(dev, "Huh? reply to command with no waiters\n");
goto error_no_waiter;
}
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
ack_skb = wimax_msg_alloc(wimax_dev, NULL, payload, size, GFP_KERNEL);
/* Check waiter didn't time out waiting for the answer... */
spin_lock_irqsave(&i2400m->rx_lock, flags);
if (i2400m->ack_skb != ERR_PTR(-EINPROGRESS)) {
d_printf(1, dev, "Huh? waiter for command reply cancelled\n");
goto error_waiter_cancelled;
}
if (ack_skb == NULL) {
dev_err(dev, "CMD/GET/SET ack: cannot allocate SKB\n");
i2400m->ack_skb = ERR_PTR(-ENOMEM);
} else
i2400m->ack_skb = ack_skb;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
complete(&i2400m->msg_completion);
return;
error_waiter_cancelled:
if (ack_skb)
kfree_skb(ack_skb);
error_no_waiter:
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
return;
}
/*
* Receive and process a control payload
*
* @i2400m: device descriptor
* @skb_rx: skb that contains the payload (for reference counting)
* @payload: pointer to message
* @size: size of the message
*
* There are two types of control RX messages: reports (asynchronous,
* like your every day interrupts) and 'acks' (reponses to a command,
* get or set request).
*
* If it is a report, we run hooks on it (to extract information for
* things we need to do in the driver) and then pass it over to the
* WiMAX stack to send it to user space.
*
* NOTE: report processing is done in a workqueue specific to the
* generic driver, to avoid deadlocks in the system.
*
* If it is not a report, it is an ack to a previously executed
* command, set or get, so wake up whoever is waiting for it from
* i2400m_msg_to_dev(). i2400m_rx_ctl_ack() takes care of that.
*
* Note that the sizes we pass to other functions from here are the
* sizes of the _l3l4_hdr + payload, not full buffer sizes, as we have
* verified in _msg_size_check() that they are congruent.
*
* For reports: We can't clone the original skb where the data is
* because we need to send this up via netlink; netlink has to add
* headers and we can't overwrite what's preceeding the payload...as
* it is another message. So we just dup them.
*/
static
void i2400m_rx_ctl(struct i2400m *i2400m, struct sk_buff *skb_rx,
const void *payload, size_t size)
{
int result;
struct device *dev = i2400m_dev(i2400m);
const struct i2400m_l3l4_hdr *l3l4_hdr = payload;
unsigned msg_type;
result = i2400m_msg_size_check(i2400m, l3l4_hdr, size);
if (result < 0) {
dev_err(dev, "HW BUG? device sent a bad message: %d\n",
result);
goto error_check;
}
msg_type = le16_to_cpu(l3l4_hdr->type);
d_printf(1, dev, "%s 0x%04x: %zu bytes\n",
msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET",
msg_type, size);
d_dump(2, dev, l3l4_hdr, size);
if (msg_type & I2400M_MT_REPORT_MASK) {
/* These hooks have to be ran serialized; as well, the
* handling might force the execution of commands, and
* that might cause reentrancy issues with
* bus-specific subdrivers and workqueues. So we run
* it in a separate workqueue. */
struct i2400m_report_hook_args args = {
.skb_rx = skb_rx,
.l3l4_hdr = l3l4_hdr,
.size = size
};
if (unlikely(i2400m->ready == 0)) /* only send if up */
return;
skb_get(skb_rx);
i2400m_queue_work(i2400m, i2400m_report_hook_work,
GFP_KERNEL, &args, sizeof(args));
result = wimax_msg(&i2400m->wimax_dev, NULL, l3l4_hdr, size,
GFP_KERNEL);
if (result < 0)
dev_err(dev, "error sending report to userspace: %d\n",
result);
} else /* an ack to a CMD, GET or SET */
i2400m_rx_ctl_ack(i2400m, payload, size);
error_check:
return;
}
/*
* Receive and send up a trace
*
* @i2400m: device descriptor
* @skb_rx: skb that contains the trace (for reference counting)
* @payload: pointer to trace message inside the skb
* @size: size of the message
*
* THe i2400m might produce trace information (diagnostics) and we
* send them through a different kernel-to-user pipe (to avoid
* clogging it).
*
* As in i2400m_rx_ctl(), we can't clone the original skb where the
* data is because we need to send this up via netlink; netlink has to
* add headers and we can't overwrite what's preceeding the
* payload...as it is another message. So we just dup them.
*/
static
void i2400m_rx_trace(struct i2400m *i2400m,
const void *payload, size_t size)
{
int result;
struct device *dev = i2400m_dev(i2400m);
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
const struct i2400m_l3l4_hdr *l3l4_hdr = payload;
unsigned msg_type;
result = i2400m_msg_size_check(i2400m, l3l4_hdr, size);
if (result < 0) {
dev_err(dev, "HW BUG? device sent a bad trace message: %d\n",
result);
goto error_check;
}
msg_type = le16_to_cpu(l3l4_hdr->type);
d_printf(1, dev, "Trace %s 0x%04x: %zu bytes\n",
msg_type & I2400M_MT_REPORT_MASK ? "REPORT" : "CMD/SET/GET",
msg_type, size);
d_dump(2, dev, l3l4_hdr, size);
if (unlikely(i2400m->ready == 0)) /* only send if up */
return;
result = wimax_msg(wimax_dev, "trace", l3l4_hdr, size, GFP_KERNEL);
if (result < 0)
dev_err(dev, "error sending trace to userspace: %d\n",
result);
error_check:
return;
}
/*
* Act on a received payload
*
* @i2400m: device instance
* @skb_rx: skb where the transaction was received
* @single: 1 if there is only one payload, 0 otherwise
* @pld: payload descriptor
* @payload: payload data
*
* Upon reception of a payload, look at its guts in the payload
* descriptor and decide what to do with it.
*/
static
void i2400m_rx_payload(struct i2400m *i2400m, struct sk_buff *skb_rx,
unsigned single, const struct i2400m_pld *pld,
const void *payload)
{
struct device *dev = i2400m_dev(i2400m);
size_t pl_size = i2400m_pld_size(pld);
enum i2400m_pt pl_type = i2400m_pld_type(pld);
switch (pl_type) {
case I2400M_PT_DATA:
d_printf(3, dev, "RX: data payload %zu bytes\n", pl_size);
i2400m_net_rx(i2400m, skb_rx, single, payload, pl_size);
break;
case I2400M_PT_CTRL:
i2400m_rx_ctl(i2400m, skb_rx, payload, pl_size);
break;
case I2400M_PT_TRACE:
i2400m_rx_trace(i2400m, payload, pl_size);
break;
default: /* Anything else shouldn't come to the host */
if (printk_ratelimit())
dev_err(dev, "RX: HW BUG? unexpected payload type %u\n",
pl_type);
}
}
/*
* Check a received transaction's message header
*
* @i2400m: device descriptor
* @msg_hdr: message header
* @buf_size: size of the received buffer
*
* Check that the declarations done by a RX buffer message header are
* sane and consistent with the amount of data that was received.
*/
static
int i2400m_rx_msg_hdr_check(struct i2400m *i2400m,
const struct i2400m_msg_hdr *msg_hdr,
size_t buf_size)
{
int result = -EIO;
struct device *dev = i2400m_dev(i2400m);
if (buf_size < sizeof(*msg_hdr)) {
dev_err(dev, "RX: HW BUG? message with short header (%zu "
"vs %zu bytes expected)\n", buf_size, sizeof(*msg_hdr));
goto error;
}
if (msg_hdr->barker != cpu_to_le32(I2400M_D2H_MSG_BARKER)) {
dev_err(dev, "RX: HW BUG? message received with unknown "
"barker 0x%08x (buf_size %zu bytes)\n",
le32_to_cpu(msg_hdr->barker), buf_size);
goto error;
}
if (msg_hdr->num_pls == 0) {
dev_err(dev, "RX: HW BUG? zero payload packets in message\n");
goto error;
}
if (le16_to_cpu(msg_hdr->num_pls) > I2400M_MAX_PLS_IN_MSG) {
dev_err(dev, "RX: HW BUG? message contains more payload "
"than maximum; ignoring.\n");
goto error;
}
result = 0;
error:
return result;
}
/*
* Check a payload descriptor against the received data
*
* @i2400m: device descriptor
* @pld: payload descriptor
* @pl_itr: offset (in bytes) in the received buffer the payload is
* located
* @buf_size: size of the received buffer
*
* Given a payload descriptor (part of a RX buffer), check it is sane
* and that the data it declares fits in the buffer.
*/
static
int i2400m_rx_pl_descr_check(struct i2400m *i2400m,
const struct i2400m_pld *pld,
size_t pl_itr, size_t buf_size)
{
int result = -EIO;
struct device *dev = i2400m_dev(i2400m);
size_t pl_size = i2400m_pld_size(pld);
enum i2400m_pt pl_type = i2400m_pld_type(pld);
if (pl_size > i2400m->bus_pl_size_max) {
dev_err(dev, "RX: HW BUG? payload @%zu: size %zu is "
"bigger than maximum %zu; ignoring message\n",
pl_itr, pl_size, i2400m->bus_pl_size_max);
goto error;
}
if (pl_itr + pl_size > buf_size) { /* enough? */
dev_err(dev, "RX: HW BUG? payload @%zu: size %zu "
"goes beyond the received buffer "
"size (%zu bytes); ignoring message\n",
pl_itr, pl_size, buf_size);
goto error;
}
if (pl_type >= I2400M_PT_ILLEGAL) {
dev_err(dev, "RX: HW BUG? illegal payload type %u; "
"ignoring message\n", pl_type);
goto error;
}
result = 0;
error:
return result;
}
/**
* i2400m_rx - Receive a buffer of data from the device
*
* @i2400m: device descriptor
* @skb: skbuff where the data has been received
*
* Parse in a buffer of data that contains an RX message sent from the
* device. See the file header for the format. Run all checks on the
* buffer header, then run over each payload's descriptors, verify
* their consistency and act on each payload's contents. If
* everything is succesful, update the device's statistics.
*
* Note: You need to set the skb to contain only the length of the
* received buffer; for that, use skb_trim(skb, RECEIVED_SIZE).
*
* Returns:
*
* 0 if ok, < 0 errno on error
*
* If ok, this function owns now the skb and the caller DOESN'T have
* to run kfree_skb() on it. However, on error, the caller still owns
* the skb and it is responsible for releasing it.
*/
int i2400m_rx(struct i2400m *i2400m, struct sk_buff *skb)
{
int i, result;
struct device *dev = i2400m_dev(i2400m);
const struct i2400m_msg_hdr *msg_hdr;
size_t pl_itr, pl_size, skb_len;
unsigned long flags;
unsigned num_pls;
skb_len = skb->len;
d_fnstart(4, dev, "(i2400m %p skb %p [size %zu])\n",
i2400m, skb, skb_len);
result = -EIO;
msg_hdr = (void *) skb->data;
result = i2400m_rx_msg_hdr_check(i2400m, msg_hdr, skb->len);
if (result < 0)
goto error_msg_hdr_check;
result = -EIO;
num_pls = le16_to_cpu(msg_hdr->num_pls);
pl_itr = sizeof(*msg_hdr) + /* Check payload descriptor(s) */
num_pls * sizeof(msg_hdr->pld[0]);
pl_itr = ALIGN(pl_itr, I2400M_PL_PAD);
if (pl_itr > skb->len) { /* got all the payload descriptors? */
dev_err(dev, "RX: HW BUG? message too short (%u bytes) for "
"%u payload descriptors (%zu each, total %zu)\n",
skb->len, num_pls, sizeof(msg_hdr->pld[0]), pl_itr);
goto error_pl_descr_short;
}
/* Walk each payload payload--check we really got it */
for (i = 0; i < num_pls; i++) {
/* work around old gcc warnings */
pl_size = i2400m_pld_size(&msg_hdr->pld[i]);
result = i2400m_rx_pl_descr_check(i2400m, &msg_hdr->pld[i],
pl_itr, skb->len);
if (result < 0)
goto error_pl_descr_check;
i2400m_rx_payload(i2400m, skb, num_pls == 1, &msg_hdr->pld[i],
skb->data + pl_itr);
pl_itr += ALIGN(pl_size, I2400M_PL_PAD);
cond_resched(); /* Don't monopolize */
}
kfree_skb(skb);
/* Update device statistics */
spin_lock_irqsave(&i2400m->rx_lock, flags);
i2400m->rx_pl_num += i;
if (i > i2400m->rx_pl_max)
i2400m->rx_pl_max = i;
if (i < i2400m->rx_pl_min)
i2400m->rx_pl_min = i;
i2400m->rx_num++;
i2400m->rx_size_acc += skb->len;
if (skb->len < i2400m->rx_size_min)
i2400m->rx_size_min = skb->len;
if (skb->len > i2400m->rx_size_max)
i2400m->rx_size_max = skb->len;
spin_unlock_irqrestore(&i2400m->rx_lock, flags);
error_pl_descr_check:
error_pl_descr_short:
error_msg_hdr_check:
d_fnend(4, dev, "(i2400m %p skb %p [size %zu]) = %d\n",
i2400m, skb, skb_len, result);
return result;
}
EXPORT_SYMBOL_GPL(i2400m_rx);

View file

@ -0,0 +1,22 @@
/*
* debug levels control file for the i2400m module's
*/
#ifndef __debug_levels__h__
#define __debug_levels__h__
/* Maximum compile and run time debug level for all submodules */
#define D_MODULENAME i2400m_sdio
#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL
#include <linux/wimax/debug.h>
/* List of all the enabled modules */
enum d_module {
D_SUBMODULE_DECLARE(main),
D_SUBMODULE_DECLARE(tx),
D_SUBMODULE_DECLARE(rx),
D_SUBMODULE_DECLARE(fw)
};
#endif /* #ifndef __debug_levels__h__ */

View file

@ -0,0 +1,224 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Firmware uploader's SDIO specifics
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Bus generic/specific split for USB
*
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* - Initial implementation for SDIO
*
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - SDIO rehash for changes in the bus-driver model
*
* THE PROCEDURE
*
* See fw.c for the generic description of this procedure.
*
* This file implements only the SDIO specifics. It boils down to how
* to send a command and waiting for an acknowledgement from the
* device. We do polled reads.
*
* COMMAND EXECUTION
*
* THe generic firmware upload code will call i2400m_bus_bm_cmd_send()
* to send commands.
*
* The SDIO devices expects things in 256 byte blocks, so it will pad
* it, compute the checksum (if needed) and pass it to SDIO.
*
* ACK RECEPTION
*
* This works in polling mode -- the fw loader says when to wait for
* data and for that it calls i2400ms_bus_bm_wait_for_ack().
*
* This will poll the device for data until it is received. We need to
* receive at least as much bytes as where asked for (although it'll
* always be a multiple of 256 bytes).
*/
#include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h"
#define D_SUBMODULE fw
#include "sdio-debug-levels.h"
/*
* Send a boot-mode command to the SDIO function
*
* We use a bounce buffer (i2400m->bm_cmd_buf) because we need to
* touch the header if the RAW flag is not set.
*
* @flags: pass thru from i2400m_bm_cmd()
* @return: cmd_size if ok, < 0 errno code on error.
*
* Note the command is padded to the SDIO block size for the device.
*/
ssize_t i2400ms_bus_bm_cmd_send(struct i2400m *i2400m,
const struct i2400m_bootrom_header *_cmd,
size_t cmd_size, int flags)
{
ssize_t result;
struct device *dev = i2400m_dev(i2400m);
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd);
struct i2400m_bootrom_header *cmd;
/* SDIO restriction */
size_t cmd_size_a = ALIGN(cmd_size, I2400MS_BLK_SIZE);
d_fnstart(5, dev, "(i2400m %p cmd %p size %zu)\n",
i2400m, _cmd, cmd_size);
result = -E2BIG;
if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
goto error_too_big;
memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size); /* Prep command */
cmd = i2400m->bm_cmd_buf;
if (cmd_size_a > cmd_size) /* Zero pad space */
memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
if ((flags & I2400M_BM_CMD_RAW) == 0) {
if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0))
dev_warn(dev, "SW BUG: response_required == 0\n");
i2400m_bm_cmd_prepare(cmd);
}
d_printf(4, dev, "BM cmd %d: %zu bytes (%zu padded)\n",
opcode, cmd_size, cmd_size_a);
d_dump(5, dev, cmd, cmd_size);
sdio_claim_host(i2400ms->func); /* Send & check */
result = sdio_memcpy_toio(i2400ms->func, I2400MS_DATA_ADDR,
i2400m->bm_cmd_buf, cmd_size_a);
sdio_release_host(i2400ms->func);
if (result < 0) {
dev_err(dev, "BM cmd %d: cannot send: %ld\n",
opcode, (long) result);
goto error_cmd_send;
}
result = cmd_size;
error_cmd_send:
error_too_big:
d_fnend(5, dev, "(i2400m %p cmd %p size %zu) = %d\n",
i2400m, _cmd, cmd_size, (int) result);
return result;
}
/*
* Read an ack from the device's boot-mode (polling)
*
* @i2400m:
* @_ack: pointer to where to store the read data
* @ack_size: how many bytes we should read
*
* Returns: < 0 errno code on error; otherwise, amount of received bytes.
*
* The ACK for a BM command is always at least sizeof(*ack) bytes, so
* check for that. We don't need to check for device reboots
*
* NOTE: We do an artificial timeout of 1 sec over the SDIO timeout;
* this way we have control over it...there is no way that I know
* of setting an SDIO transaction timeout.
*/
ssize_t i2400ms_bus_bm_wait_for_ack(struct i2400m *i2400m,
struct i2400m_bootrom_header *ack,
size_t ack_size)
{
int result;
ssize_t rx_size;
u64 timeout;
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
BUG_ON(sizeof(*ack) > ack_size);
d_fnstart(5, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size);
timeout = get_jiffies_64() + 2 * HZ;
sdio_claim_host(func);
while (1) {
if (time_after64(get_jiffies_64(), timeout)) {
rx_size = -ETIMEDOUT;
dev_err(dev, "timeout waiting for ack data\n");
goto error_timedout;
}
/* Find the RX size, check if it fits or not -- it if
* doesn't fit, fail, as we have no way to dispose of
* the extra data. */
rx_size = __i2400ms_rx_get_size(i2400ms);
if (rx_size < 0)
goto error_rx_get_size;
result = -ENOSPC; /* Check it fits */
if (rx_size < sizeof(*ack)) {
rx_size = -EIO;
dev_err(dev, "HW BUG? received is too small (%zu vs "
"%zu needed)\n", sizeof(*ack), rx_size);
goto error_too_small;
}
if (rx_size > I2400M_BM_ACK_BUF_SIZE) {
dev_err(dev, "SW BUG? BM_ACK_BUF is too small (%u vs "
"%zu needed)\n", I2400M_BM_ACK_BUF_SIZE,
rx_size);
goto error_too_small;
}
/* Read it */
result = sdio_memcpy_fromio(func, i2400m->bm_ack_buf,
I2400MS_DATA_ADDR, rx_size);
if (result == -ETIMEDOUT || result == -ETIME)
continue;
if (result < 0) {
dev_err(dev, "BM SDIO receive (%zu B) failed: %d\n",
rx_size, result);
goto error_read;
} else
break;
}
rx_size = min((ssize_t)ack_size, rx_size);
memcpy(ack, i2400m->bm_ack_buf, rx_size);
error_read:
error_too_small:
error_rx_get_size:
error_timedout:
sdio_release_host(func);
d_fnend(5, dev, "(i2400m %p ack %p size %zu) = %ld\n",
i2400m, ack, ack_size, (long) rx_size);
return rx_size;
}

View file

@ -0,0 +1,255 @@
/*
* Intel Wireless WiMAX Connection 2400m
* SDIO RX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* - Initial implementation
*
*
* This handles the RX path on SDIO.
*
* The SDIO bus driver calls the "irq" routine when data is available.
* This is not a traditional interrupt routine since the SDIO bus
* driver calls us from its irq thread context. Because of this
* sleeping in the SDIO RX IRQ routine is okay.
*
* From there on, we obtain the size of the data that is available,
* allocate an skb, copy it and then pass it to the generic driver's
* RX routine [i2400m_rx()].
*
* ROADMAP
*
* i2400ms_irq()
* i2400ms_rx()
* __i2400ms_rx_get_size()
* i2400m_rx()
*
* i2400ms_rx_setup()
*
* i2400ms_rx_release()
*/
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/skbuff.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h"
#define D_SUBMODULE rx
#include "sdio-debug-levels.h"
/*
* Read and return the amount of bytes available for RX
*
* The RX size has to be read like this: byte reads of three
* sequential locations; then glue'em together.
*
* sdio_readl() doesn't work.
*/
ssize_t __i2400ms_rx_get_size(struct i2400ms *i2400ms)
{
int ret, cnt, val;
ssize_t rx_size;
unsigned xfer_size_addr;
struct sdio_func *func = i2400ms->func;
struct device *dev = &i2400ms->func->dev;
d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms);
xfer_size_addr = I2400MS_INTR_GET_SIZE_ADDR;
rx_size = 0;
for (cnt = 0; cnt < 3; cnt++) {
val = sdio_readb(func, xfer_size_addr + cnt, &ret);
if (ret < 0) {
dev_err(dev, "RX: Can't read byte %d of RX size from "
"0x%08x: %d\n", cnt, xfer_size_addr + cnt, ret);
rx_size = ret;
goto error_read;
}
rx_size = rx_size << 8 | (val & 0xff);
}
d_printf(6, dev, "RX: rx_size is %ld\n", (long) rx_size);
error_read:
d_fnend(7, dev, "(i2400ms %p) = %ld\n", i2400ms, (long) rx_size);
return rx_size;
}
/*
* Read data from the device (when in normal)
*
* Allocate an SKB of the right size, read the data in and then
* deliver it to the generic layer.
*
* We also check for a reboot barker. That means the device died and
* we have to reboot it.
*/
static
void i2400ms_rx(struct i2400ms *i2400ms)
{
int ret;
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
struct i2400m *i2400m = &i2400ms->i2400m;
struct sk_buff *skb;
ssize_t rx_size;
d_fnstart(7, dev, "(i2400ms %p)\n", i2400ms);
rx_size = __i2400ms_rx_get_size(i2400ms);
if (rx_size < 0) {
ret = rx_size;
goto error_get_size;
}
ret = -ENOMEM;
skb = alloc_skb(rx_size, GFP_ATOMIC);
if (NULL == skb) {
dev_err(dev, "RX: unable to alloc skb\n");
goto error_alloc_skb;
}
ret = sdio_memcpy_fromio(func, skb->data,
I2400MS_DATA_ADDR, rx_size);
if (ret < 0) {
dev_err(dev, "RX: SDIO data read failed: %d\n", ret);
goto error_memcpy_fromio;
}
/* Check if device has reset */
if (!memcmp(skb->data, i2400m_NBOOT_BARKER,
sizeof(i2400m_NBOOT_BARKER))
|| !memcmp(skb->data, i2400m_SBOOT_BARKER,
sizeof(i2400m_SBOOT_BARKER))) {
ret = i2400m_dev_reset_handle(i2400m);
kfree_skb(skb);
} else {
skb_put(skb, rx_size);
i2400m_rx(i2400m, skb);
}
d_fnend(7, dev, "(i2400ms %p) = void\n", i2400ms);
return;
error_memcpy_fromio:
kfree_skb(skb);
error_alloc_skb:
error_get_size:
d_fnend(7, dev, "(i2400ms %p) = %d\n", i2400ms, ret);
return;
}
/*
* Process an interrupt from the SDIO card
*
* FIXME: need to process other events that are not just ready-to-read
*
* Checks there is data ready and then proceeds to read it.
*/
static
void i2400ms_irq(struct sdio_func *func)
{
int ret;
struct i2400ms *i2400ms = sdio_get_drvdata(func);
struct i2400m *i2400m = &i2400ms->i2400m;
struct device *dev = &func->dev;
int val;
d_fnstart(6, dev, "(i2400ms %p)\n", i2400ms);
val = sdio_readb(func, I2400MS_INTR_STATUS_ADDR, &ret);
if (ret < 0) {
dev_err(dev, "RX: Can't read interrupt status: %d\n", ret);
goto error_no_irq;
}
if (!val) {
dev_err(dev, "RX: BUG? got IRQ but no interrupt ready?\n");
goto error_no_irq;
}
sdio_writeb(func, 1, I2400MS_INTR_CLEAR_ADDR, &ret);
if (WARN_ON(i2400m->boot_mode != 0))
dev_err(dev, "RX: SW BUG? boot mode and IRQ is up?\n");
else
i2400ms_rx(i2400ms);
error_no_irq:
d_fnend(6, dev, "(i2400ms %p) = void\n", i2400ms);
return;
}
/*
* Setup SDIO RX
*
* Hooks up the IRQ handler and then enables IRQs.
*/
int i2400ms_rx_setup(struct i2400ms *i2400ms)
{
int result;
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
sdio_claim_host(func);
result = sdio_claim_irq(func, i2400ms_irq);
if (result < 0) {
dev_err(dev, "Cannot claim IRQ: %d\n", result);
goto error_irq_claim;
}
result = 0;
sdio_writeb(func, 1, I2400MS_INTR_ENABLE_ADDR, &result);
if (result < 0) {
sdio_release_irq(func);
dev_err(dev, "Failed to enable interrupts %d\n", result);
}
error_irq_claim:
sdio_release_host(func);
d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
return result;
}
/*
* Tear down SDIO RX
*
* Disables IRQs in the device and removes the IRQ handler.
*/
void i2400ms_rx_release(struct i2400ms *i2400ms)
{
int result;
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
sdio_claim_host(func);
sdio_writeb(func, 0, I2400MS_INTR_ENABLE_ADDR, &result);
sdio_release_irq(func);
sdio_release_host(func);
d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
}

View file

@ -0,0 +1,153 @@
/*
* Intel Wireless WiMAX Connection 2400m
* SDIO TX transaction backends
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* - Initial implementation
*
*
* Takes the TX messages in the i2400m's driver TX FIFO and sends them
* to the device until there are no more.
*
* If we fail sending the message, we just drop it. There isn't much
* we can do at this point. Most of the traffic is network, which has
* recovery methods for dropped packets.
*
* The SDIO functions are not atomic, so we can't run from the context
* where i2400m->bus_tx_kick() [i2400ms_bus_tx_kick()] is being called
* (some times atomic). Thus, the actual TX work is deferred to a
* workqueue.
*
* ROADMAP
*
* i2400ms_bus_tx_kick()
* i2400ms_tx_submit() [through workqueue]
*
* i2400m_tx_setup()
*
* i2400m_tx_release()
*/
#include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h"
#define D_SUBMODULE tx
#include "sdio-debug-levels.h"
/*
* Pull TX transations from the TX FIFO and send them to the device
* until there are no more.
*/
static
void i2400ms_tx_submit(struct work_struct *ws)
{
int result;
struct i2400ms *i2400ms = container_of(ws, struct i2400ms, tx_worker);
struct i2400m *i2400m = &i2400ms->i2400m;
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
struct i2400m_msg_hdr *tx_msg;
size_t tx_msg_size;
d_fnstart(4, dev, "(i2400ms %p, i2400m %p)\n", i2400ms, i2400ms);
while (NULL != (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size))) {
d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
d_dump(5, dev, tx_msg, tx_msg_size);
sdio_claim_host(func);
result = sdio_memcpy_toio(func, 0, tx_msg, tx_msg_size);
sdio_release_host(func);
i2400m_tx_msg_sent(i2400m);
if (result < 0) {
dev_err(dev, "TX: cannot submit TX; tx_msg @%zu %zu B:"
" %d\n", (void *) tx_msg - i2400m->tx_buf,
tx_msg_size, result);
}
d_printf(2, dev, "TX: %zub submitted\n", tx_msg_size);
}
d_fnend(4, dev, "(i2400ms %p) = void\n", i2400ms);
}
/*
* The generic driver notifies us that there is data ready for TX
*
* Schedule a run of i2400ms_tx_submit() to handle it.
*/
void i2400ms_bus_tx_kick(struct i2400m *i2400m)
{
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct device *dev = &i2400ms->func->dev;
d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
/* schedule tx work, this is because tx may block, therefore
* it has to run in a thread context.
*/
queue_work(i2400ms->tx_workqueue, &i2400ms->tx_worker);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
int i2400ms_tx_setup(struct i2400ms *i2400ms)
{
int result;
struct device *dev = &i2400ms->func->dev;
struct i2400m *i2400m = &i2400ms->i2400m;
d_fnstart(5, dev, "(i2400ms %p)\n", i2400ms);
INIT_WORK(&i2400ms->tx_worker, i2400ms_tx_submit);
snprintf(i2400ms->tx_wq_name, sizeof(i2400ms->tx_wq_name),
"%s-tx", i2400m->wimax_dev.name);
i2400ms->tx_workqueue =
create_singlethread_workqueue(i2400ms->tx_wq_name);
if (NULL == i2400ms->tx_workqueue) {
dev_err(dev, "TX: failed to create workqueue\n");
result = -ENOMEM;
} else
result = 0;
d_fnend(5, dev, "(i2400ms %p) = %d\n", i2400ms, result);
return result;
}
void i2400ms_tx_release(struct i2400ms *i2400ms)
{
destroy_workqueue(i2400ms->tx_workqueue);
}

View file

@ -0,0 +1,511 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Linux driver model glue for the SDIO device, reset & fw upload
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Dirk Brandewie <dirk.j.brandewie@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* See i2400m-sdio.h for a general description of this driver.
*
* This file implements driver model glue, and hook ups for the
* generic driver to implement the bus-specific functions (device
* communication setup/tear down, firmware upload and resetting).
*
* ROADMAP
*
* i2400m_probe()
* alloc_netdev()
* i2400ms_netdev_setup()
* i2400ms_init()
* i2400m_netdev_setup()
* i2400ms_enable_function()
* i2400m_setup()
*
* i2400m_remove()
* i2400m_release()
* free_netdev(net_dev)
*
* i2400ms_bus_reset() Called by i2400m->bus_reset
* __i2400ms_reset()
* __i2400ms_send_barker()
*
* i2400ms_bus_dev_start() Called by i2400m_dev_start() [who is
* i2400ms_tx_setup() called by i2400m_setup()]
* i2400ms_rx_setup()
*
* i2400ms_bus_dev_stop() Called by i2400m_dev_stop() [who is
* i2400ms_rx_release() is called by i2400m_release()]
* i2400ms_tx_release()
*
*/
#include <linux/debugfs.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sdio_func.h>
#include "i2400m-sdio.h"
#include <linux/wimax/i2400m.h>
#define D_SUBMODULE main
#include "sdio-debug-levels.h"
/* IOE WiMAX function timeout in seconds */
static int ioe_timeout = 2;
module_param(ioe_timeout, int, 0);
/* Our firmware file name */
#define I2400MS_FW_FILE_NAME "i2400m-fw-sdio-" I2400M_FW_VERSION ".sbcf"
/*
* Enable the SDIO function
*
* Tries to enable the SDIO function; might fail if it is still not
* ready (in some hardware, the SDIO WiMAX function is only enabled
* when we ask it to explicitly doing). Tries until a timeout is
* reached.
*
* The reverse of this is...sdio_disable_function()
*
* Returns: 0 if the SDIO function was enabled, < 0 errno code on
* error (-ENODEV when it was unable to enable the function).
*/
static
int i2400ms_enable_function(struct sdio_func *func)
{
u64 timeout;
int err;
struct device *dev = &func->dev;
d_fnstart(3, dev, "(func %p)\n", func);
/* Setup timeout (FIXME: This needs to read the CIS table to
* get a real timeout) and then wait for the device to signal
* it is ready */
timeout = get_jiffies_64() + ioe_timeout * HZ;
err = -ENODEV;
while (err != 0 && time_before64(get_jiffies_64(), timeout)) {
sdio_claim_host(func);
err = sdio_enable_func(func);
if (0 == err) {
sdio_release_host(func);
d_printf(2, dev, "SDIO function enabled\n");
goto function_enabled;
}
d_printf(2, dev, "SDIO function failed to enable: %d\n", err);
sdio_disable_func(func);
sdio_release_host(func);
msleep(I2400MS_INIT_SLEEP_INTERVAL);
}
/* If timed out, device is not there yet -- get -ENODEV so
* the device driver core will retry later on. */
if (err == -ETIME) {
dev_err(dev, "Can't enable WiMAX function; "
" has the function been enabled?\n");
err = -ENODEV;
}
function_enabled:
d_fnend(3, dev, "(func %p) = %d\n", func, err);
return err;
}
/*
* Setup driver resources needed to communicate with the device
*
* The fw needs some time to settle, and it was just uploaded,
* so give it a break first. I'd prefer to just wait for the device to
* send something, but seems the poking we do to enable SDIO stuff
* interferes with it, so just give it a break before starting...
*/
static
int i2400ms_bus_dev_start(struct i2400m *i2400m)
{
int result;
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
msleep(200);
result = i2400ms_rx_setup(i2400ms);
if (result < 0)
goto error_rx_setup;
result = i2400ms_tx_setup(i2400ms);
if (result < 0)
goto error_tx_setup;
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
i2400ms_tx_release(i2400ms);
error_tx_setup:
i2400ms_rx_release(i2400ms);
error_rx_setup:
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
return result;
}
static
void i2400ms_bus_dev_stop(struct i2400m *i2400m)
{
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
i2400ms_rx_release(i2400ms);
i2400ms_tx_release(i2400ms);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
/*
* Sends a barker buffer to the device
*
* This helper will allocate a kmalloced buffer and use it to transmit
* (then free it). Reason for this is that the SDIO host controller
* expects alignment (unknown exactly which) which the stack won't
* really provide and certain arches/host-controller combinations
* cannot use stack/vmalloc/text areas for DMA transfers.
*/
static
int __i2400ms_send_barker(struct i2400ms *i2400ms,
const __le32 *barker, size_t barker_size)
{
int ret;
struct sdio_func *func = i2400ms->func;
struct device *dev = &func->dev;
void *buffer;
ret = -ENOMEM;
buffer = kmalloc(I2400MS_BLK_SIZE, GFP_KERNEL);
if (buffer == NULL)
goto error_kzalloc;
memcpy(buffer, barker, barker_size);
sdio_claim_host(func);
ret = sdio_memcpy_toio(func, 0, buffer, I2400MS_BLK_SIZE);
sdio_release_host(func);
if (ret < 0)
d_printf(0, dev, "E: barker error: %d\n", ret);
kfree(buffer);
error_kzalloc:
return ret;
}
/*
* Reset a device at different levels (warm, cold or bus)
*
* @i2400ms: device descriptor
* @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS)
*
* FIXME: not tested -- need to confirm expected effects
*
* Warm and cold resets get an SDIO reset if they fail (unimplemented)
*
* Warm reset:
*
* The device will be fully reset internally, but won't be
* disconnected from the USB bus (so no reenumeration will
* happen). Firmware upload will be neccessary.
*
* The device will send a reboot barker in the notification endpoint
* that will trigger the driver to reinitialize the state
* automatically from notif.c:i2400m_notification_grok() into
* i2400m_dev_bootstrap_delayed().
*
* Cold and bus (USB) reset:
*
* The device will be fully reset internally, disconnected from the
* USB bus an a reenumeration will happen. Firmware upload will be
* neccessary. Thus, we don't do any locking or struct
* reinitialization, as we are going to be fully disconnected and
* reenumerated.
*
* Note we need to return -ENODEV if a warm reset was requested and we
* had to resort to a bus reset. See i2400m_op_reset(), wimax_reset()
* and wimax_dev->op_reset.
*
* WARNING: no driver state saved/fixed
*/
static
int i2400ms_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
{
int result;
struct i2400ms *i2400ms =
container_of(i2400m, struct i2400ms, i2400m);
struct device *dev = i2400m_dev(i2400m);
static const __le32 i2400m_WARM_BOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
};
static const __le32 i2400m_COLD_BOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
};
if (rt == I2400M_RT_WARM)
result = __i2400ms_send_barker(i2400ms, i2400m_WARM_BOOT_BARKER,
sizeof(i2400m_WARM_BOOT_BARKER));
else if (rt == I2400M_RT_COLD)
result = __i2400ms_send_barker(i2400ms, i2400m_COLD_BOOT_BARKER,
sizeof(i2400m_COLD_BOOT_BARKER));
else if (rt == I2400M_RT_BUS) {
do_bus_reset:
dev_err(dev, "FIXME: SDIO bus reset not implemented\n");
result = rt == I2400M_RT_WARM ? -ENODEV : -ENOSYS;
} else
BUG();
if (result < 0 && rt != I2400M_RT_BUS) {
dev_err(dev, "%s reset failed (%d); trying SDIO reset\n",
rt == I2400M_RT_WARM ? "warm" : "cold", result);
rt = I2400M_RT_BUS;
goto do_bus_reset;
}
return result;
}
static
void i2400ms_netdev_setup(struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct i2400ms *i2400ms = container_of(i2400m, struct i2400ms, i2400m);
i2400ms_init(i2400ms);
i2400m_netdev_setup(net_dev);
}
/*
* Debug levels control; see debug.h
*/
struct d_level D_LEVEL[] = {
D_SUBMODULE_DEFINE(main),
D_SUBMODULE_DEFINE(tx),
D_SUBMODULE_DEFINE(rx),
D_SUBMODULE_DEFINE(fw),
};
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
#define __debugfs_register(prefix, name, parent) \
do { \
result = d_level_register_debugfs(prefix, name, parent); \
if (result < 0) \
goto error; \
} while (0)
static
int i2400ms_debugfs_add(struct i2400ms *i2400ms)
{
int result;
struct dentry *dentry = i2400ms->i2400m.wimax_dev.debugfs_dentry;
dentry = debugfs_create_dir("i2400m-usb", dentry);
result = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
if (result == -ENODEV)
result = 0; /* No debugfs support */
goto error;
}
i2400ms->debugfs_dentry = dentry;
__debugfs_register("dl_", main, dentry);
__debugfs_register("dl_", tx, dentry);
__debugfs_register("dl_", rx, dentry);
__debugfs_register("dl_", fw, dentry);
return 0;
error:
debugfs_remove_recursive(i2400ms->debugfs_dentry);
return result;
}
/*
* Probe a i2400m interface and register it
*
* @func: SDIO function
* @id: SDIO device ID
* @returns: 0 if ok, < 0 errno code on error.
*
* Alloc a net device, initialize the bus-specific details and then
* calls the bus-generic initialization routine. That will register
* the wimax and netdev devices, upload the firmware [using
* _bus_bm_*()], call _bus_dev_start() to finalize the setup of the
* communication with the device and then will start to talk to it to
* finnish setting it up.
*
* Initialization is tricky; some instances of the hw are packed with
* others in a way that requires a third driver that enables the WiMAX
* function. In those cases, we can't enable the SDIO function and
* we'll return with -ENODEV. When the driver that enables the WiMAX
* function does its thing, it has to do a bus_rescan_devices() on the
* SDIO bus so this driver is called again to enumerate the WiMAX
* function.
*/
static
int i2400ms_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
int result;
struct net_device *net_dev;
struct device *dev = &func->dev;
struct i2400m *i2400m;
struct i2400ms *i2400ms;
/* Allocate instance [calls i2400m_netdev_setup() on it]. */
result = -ENOMEM;
net_dev = alloc_netdev(sizeof(*i2400ms), "wmx%d",
i2400ms_netdev_setup);
if (net_dev == NULL) {
dev_err(dev, "no memory for network device instance\n");
goto error_alloc_netdev;
}
SET_NETDEV_DEV(net_dev, dev);
i2400m = net_dev_to_i2400m(net_dev);
i2400ms = container_of(i2400m, struct i2400ms, i2400m);
i2400m->wimax_dev.net_dev = net_dev;
i2400ms->func = func;
sdio_set_drvdata(func, i2400ms);
i2400m->bus_tx_block_size = I2400MS_BLK_SIZE;
i2400m->bus_pl_size_max = I2400MS_PL_SIZE_MAX;
i2400m->bus_dev_start = i2400ms_bus_dev_start;
i2400m->bus_dev_stop = i2400ms_bus_dev_stop;
i2400m->bus_tx_kick = i2400ms_bus_tx_kick;
i2400m->bus_reset = i2400ms_bus_reset;
i2400m->bus_bm_cmd_send = i2400ms_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400ms_bus_bm_wait_for_ack;
i2400m->bus_fw_name = I2400MS_FW_FILE_NAME;
i2400m->bus_bm_mac_addr_impaired = 1;
result = i2400ms_enable_function(i2400ms->func);
if (result < 0) {
dev_err(dev, "Cannot enable SDIO function: %d\n", result);
goto error_func_enable;
}
sdio_claim_host(func);
result = sdio_set_block_size(func, I2400MS_BLK_SIZE);
if (result < 0) {
dev_err(dev, "Failed to set block size: %d\n", result);
goto error_set_blk_size;
}
sdio_release_host(func);
result = i2400m_setup(i2400m, I2400M_BRI_NO_REBOOT);
if (result < 0) {
dev_err(dev, "cannot setup device: %d\n", result);
goto error_setup;
}
result = i2400ms_debugfs_add(i2400ms);
if (result < 0) {
dev_err(dev, "cannot create SDIO debugfs: %d\n",
result);
goto error_debugfs_add;
}
return 0;
error_debugfs_add:
i2400m_release(i2400m);
error_setup:
sdio_set_drvdata(func, NULL);
sdio_claim_host(func);
error_set_blk_size:
sdio_disable_func(func);
sdio_release_host(func);
error_func_enable:
free_netdev(net_dev);
error_alloc_netdev:
return result;
}
static
void i2400ms_remove(struct sdio_func *func)
{
struct device *dev = &func->dev;
struct i2400ms *i2400ms = sdio_get_drvdata(func);
struct i2400m *i2400m = &i2400ms->i2400m;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
d_fnstart(3, dev, "SDIO func %p\n", func);
debugfs_remove_recursive(i2400ms->debugfs_dentry);
i2400m_release(i2400m);
sdio_set_drvdata(func, NULL);
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
free_netdev(net_dev);
d_fnend(3, dev, "SDIO func %p\n", func);
}
enum {
I2400MS_INTEL_VID = 0x89,
};
static
const struct sdio_device_id i2400ms_sdio_ids[] = {
/* Intel: i2400m WiMAX over SDIO */
{ SDIO_DEVICE(I2400MS_INTEL_VID, 0x1402) },
{ }, /* end: all zeroes */
};
MODULE_DEVICE_TABLE(sdio, i2400ms_sdio_ids);
static
struct sdio_driver i2400m_sdio_driver = {
.name = KBUILD_MODNAME,
.probe = i2400ms_probe,
.remove = i2400ms_remove,
.id_table = i2400ms_sdio_ids,
};
static
int __init i2400ms_driver_init(void)
{
return sdio_register_driver(&i2400m_sdio_driver);
}
module_init(i2400ms_driver_init);
static
void __exit i2400ms_driver_exit(void)
{
flush_scheduled_work(); /* for the stuff we schedule */
sdio_unregister_driver(&i2400m_sdio_driver);
}
module_exit(i2400ms_driver_exit);
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
MODULE_DESCRIPTION("Intel 2400M WiMAX networking for SDIO");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(I2400MS_FW_FILE_NAME);

View file

@ -0,0 +1,817 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Generic (non-bus specific) TX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
*
* Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Rewritten to use a single FIFO to lower the memory allocation
* pressure and optimize cache hits when copying to the queue, as
* well as splitting out bus-specific code.
*
*
* Implements data transmission to the device; this is done through a
* software FIFO, as data/control frames can be coalesced (while the
* device is reading the previous tx transaction, others accumulate).
*
* A FIFO is used because at the end it is resource-cheaper that trying
* to implement scatter/gather over USB. As well, most traffic is going
* to be download (vs upload).
*
* The format for sending/receiving data to/from the i2400m is
* described in detail in rx.c:PROTOCOL FORMAT. In here we implement
* the transmission of that. This is split between a bus-independent
* part that just prepares everything and a bus-specific part that
* does the actual transmission over the bus to the device (in the
* bus-specific driver).
*
*
* The general format of a device-host transaction is MSG-HDR, PLD1,
* PLD2...PLDN, PL1, PL2,...PLN, PADDING.
*
* Because we need the send payload descriptors and then payloads and
* because it is kind of expensive to do scatterlists in USB (one URB
* per node), it becomes cheaper to append all the data to a FIFO
* (copying to a FIFO potentially in cache is cheaper).
*
* Then the bus-specific code takes the parts of that FIFO that are
* written and passes them to the device.
*
* So the concepts to keep in mind there are:
*
* We use a FIFO to queue the data in a linear buffer. We first append
* a MSG-HDR, space for I2400M_TX_PLD_MAX payload descriptors and then
* go appending payloads until we run out of space or of payload
* descriptors. Then we append padding to make the whole transaction a
* multiple of i2400m->bus_tx_block_size (as defined by the bus layer).
*
* - A TX message: a combination of a message header, payload
* descriptors and payloads.
*
* Open: it is marked as active (i2400m->tx_msg is valid) and we
* can keep adding payloads to it.
*
* Closed: we are not appending more payloads to this TX message
* (exahusted space in the queue, too many payloads or
* whichever). We have appended padding so the whole message
* length is aligned to i2400m->bus_tx_block_size (as set by the
* bus/transport layer).
*
* - Most of the time we keep a TX message open to which we append
* payloads.
*
* - If we are going to append and there is no more space (we are at
* the end of the FIFO), we close the message, mark the rest of the
* FIFO space unusable (skip_tail), create a new message at the
* beginning of the FIFO (if there is space) and append the message
* there.
*
* This is because we need to give linear TX messages to the bus
* engine. So we don't write a message to the remaining FIFO space
* until the tail and continue at the head of it.
*
* - We overload one of the fields in the message header to use it as
* 'size' of the TX message, so we can iterate over them. It also
* contains a flag that indicates if we have to skip it or not.
* When we send the buffer, we update that to its real on-the-wire
* value.
*
* - The MSG-HDR PLD1...PLD2 stuff has to be a size multiple of 16.
*
* It follows that if MSG-HDR says we have N messages, the whole
* header + descriptors is 16 + 4*N; for those to be a multiple of
* 16, it follows that N can be 4, 8, 12, ... (32, 48, 64, 80...
* bytes).
*
* So if we have only 1 payload, we have to submit a header that in
* all truth has space for 4.
*
* The implication is that we reserve space for 12 (64 bytes); but
* if we fill up only (eg) 2, our header becomes 32 bytes only. So
* the TX engine has to shift those 32 bytes of msg header and 2
* payloads and padding so that right after it the payloads start
* and the TX engine has to know about that.
*
* It is cheaper to move the header up than the whole payloads down.
*
* We do this in i2400m_tx_close(). See 'i2400m_msg_hdr->offset'.
*
* - Each payload has to be size-padded to 16 bytes; before appending
* it, we just do it.
*
* - The whole message has to be padded to i2400m->bus_tx_block_size;
* we do this at close time. Thus, when reserving space for the
* payload, we always make sure there is also free space for this
* padding that sooner or later will happen.
*
* When we append a message, we tell the bus specific code to kick in
* TXs. It will TX (in parallel) until the buffer is exhausted--hence
* the lockin we do. The TX code will only send a TX message at the
* time (which remember, might contain more than one payload). Of
* course, when the bus-specific driver attempts to TX a message that
* is still open, it gets closed first.
*
* Gee, this is messy; well a picture. In the example below we have a
* partially full FIFO, with a closed message ready to be delivered
* (with a moved message header to make sure it is size-aligned to
* 16), TAIL room that was unusable (and thus is marked with a message
* header that says 'skip this') and at the head of the buffer, an
* imcomplete message with a couple of payloads.
*
* N ___________________________________________________
* | |
* | TAIL room |
* | |
* | msg_hdr to skip (size |= 0x80000) |
* |---------------------------------------------------|-------
* | | /|\
* | | |
* | TX message padding | |
* | | |
* | | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -| |
* | | |
* | payload 1 | |
* | | N * tx_block_size
* | | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -| |
* | | |
* | payload 1 | |
* | | |
* | | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -|- -|- - - -
* | padding 3 /|\ | | /|\
* | padding 2 | | | |
* | pld 1 32 bytes (2 * 16) | | |
* | pld 0 | | | |
* | moved msg_hdr \|/ | \|/ |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -|- - - |
* | | _PLD_SIZE
* | unused | |
* | | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -| |
* | msg_hdr (size X) [this message is closed] | \|/
* |===================================================|========== <=== OUT
* | |
* | |
* | |
* | Free rooom |
* | |
* | |
* | |
* | |
* | |
* | |
* | |
* | |
* | |
* |===================================================|========== <=== IN
* | |
* | |
* | |
* | |
* | payload 1 |
* | |
* | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -|
* | |
* | payload 0 |
* | |
* | |
* |- - - - - - - - - - - - - - - - - - - - - - - - - -|
* | pld 11 /|\ |
* | ... | |
* | pld 1 64 bytes (2 * 16) |
* | pld 0 | |
* | msg_hdr (size X) \|/ [message is open] |
* 0 ---------------------------------------------------
*
*
* ROADMAP
*
* i2400m_tx_setup() Called by i2400m_setup
* i2400m_tx_release() Called by i2400m_release()
*
* i2400m_tx() Called to send data or control frames
* i2400m_tx_fifo_push() Allocates append-space in the FIFO
* i2400m_tx_new() Opens a new message in the FIFO
* i2400m_tx_fits() Checks if a new payload fits in the message
* i2400m_tx_close() Closes an open message in the FIFO
* i2400m_tx_skip_tail() Marks unusable FIFO tail space
* i2400m->bus_tx_kick()
*
* Now i2400m->bus_tx_kick() is the the bus-specific driver backend
* implementation; that would do:
*
* i2400m->bus_tx_kick()
* i2400m_tx_msg_get() Gets first message ready to go
* ...sends it...
* i2400m_tx_msg_sent() Ack the message is sent; repeat from
* _tx_msg_get() until it returns NULL
* (FIFO empty).
*/
#include <linux/netdevice.h>
#include "i2400m.h"
#define D_SUBMODULE tx
#include "debug-levels.h"
enum {
/**
* TX Buffer size
*
* Doc says maximum transaction is 16KiB. If we had 16KiB en
* route and 16KiB being queued, it boils down to needing
* 32KiB.
*/
I2400M_TX_BUF_SIZE = 32768,
/**
* Message header and payload descriptors have to be 16
* aligned (16 + 4 * N = 16 * M). If we take that average sent
* packets are MTU size (~1400-~1500) it follows that we could
* fit at most 10-11 payloads in one transaction. To meet the
* alignment requirement, that means we need to leave space
* for 12 (64 bytes). To simplify, we leave space for that. If
* at the end there are less, we pad up to the nearest
* multiple of 16.
*/
I2400M_TX_PLD_MAX = 12,
I2400M_TX_PLD_SIZE = sizeof(struct i2400m_msg_hdr)
+ I2400M_TX_PLD_MAX * sizeof(struct i2400m_pld),
I2400M_TX_SKIP = 0x80000000,
};
#define TAIL_FULL ((void *)~(unsigned long)NULL)
/*
* Allocate @size bytes in the TX fifo, return a pointer to it
*
* @i2400m: device descriptor
* @size: size of the buffer we need to allocate
* @padding: ensure that there is at least this many bytes of free
* contiguous space in the fifo. This is needed because later on
* we might need to add padding.
*
* Returns:
*
* Pointer to the allocated space. NULL if there is no
* space. TAIL_FULL if there is no space at the tail but there is at
* the head (Case B below).
*
* These are the two basic cases we need to keep an eye for -- it is
* much better explained in linux/kernel/kfifo.c, but this code
* basically does the same. No rocket science here.
*
* Case A Case B
* N ___________ ___________
* | tail room | | data |
* | | | |
* |<- IN ->| |<- OUT ->|
* | | | |
* | data | | room |
* | | | |
* |<- OUT ->| |<- IN ->|
* | | | |
* | head room | | data |
* 0 ----------- -----------
*
* We allocate only *contiguous* space.
*
* We can allocate only from 'room'. In Case B, it is simple; in case
* A, we only try from the tail room; if it is not enough, we just
* fail and return TAIL_FULL and let the caller figure out if we wants to
* skip the tail room and try to allocate from the head.
*
* Note:
*
* Assumes i2400m->tx_lock is taken, and we use that as a barrier
*
* The indexes keep increasing and we reset them to zero when we
* pop data off the queue
*/
static
void *i2400m_tx_fifo_push(struct i2400m *i2400m, size_t size, size_t padding)
{
struct device *dev = i2400m_dev(i2400m);
size_t room, tail_room, needed_size;
void *ptr;
needed_size = size + padding;
room = I2400M_TX_BUF_SIZE - (i2400m->tx_in - i2400m->tx_out);
if (room < needed_size) { /* this takes care of Case B */
d_printf(2, dev, "fifo push %zu/%zu: no space\n",
size, padding);
return NULL;
}
/* Is there space at the tail? */
tail_room = I2400M_TX_BUF_SIZE - i2400m->tx_in % I2400M_TX_BUF_SIZE;
if (tail_room < needed_size) {
if (i2400m->tx_out % I2400M_TX_BUF_SIZE
< i2400m->tx_in % I2400M_TX_BUF_SIZE) {
d_printf(2, dev, "fifo push %zu/%zu: tail full\n",
size, padding);
return TAIL_FULL; /* There might be head space */
} else {
d_printf(2, dev, "fifo push %zu/%zu: no head space\n",
size, padding);
return NULL; /* There is no space */
}
}
ptr = i2400m->tx_buf + i2400m->tx_in % I2400M_TX_BUF_SIZE;
d_printf(2, dev, "fifo push %zu/%zu: at @%zu\n", size, padding,
i2400m->tx_in % I2400M_TX_BUF_SIZE);
i2400m->tx_in += size;
return ptr;
}
/*
* Mark the tail of the FIFO buffer as 'to-skip'
*
* We should never hit the BUG_ON() because all the sizes we push to
* the FIFO are padded to be a multiple of 16 -- the size of *msg
* (I2400M_PL_PAD for the payloads, I2400M_TX_PLD_SIZE for the
* header).
*
* Note:
*
* Assumes i2400m->tx_lock is taken, and we use that as a barrier
*/
static
void i2400m_tx_skip_tail(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
size_t tx_in = i2400m->tx_in % I2400M_TX_BUF_SIZE;
size_t tail_room = I2400M_TX_BUF_SIZE - tx_in;
struct i2400m_msg_hdr *msg = i2400m->tx_buf + tx_in;
BUG_ON(tail_room < sizeof(*msg));
msg->size = tail_room | I2400M_TX_SKIP;
d_printf(2, dev, "skip tail: skipping %zu bytes @%zu\n",
tail_room, tx_in);
i2400m->tx_in += tail_room;
}
/*
* Check if a skb will fit in the TX queue's current active TX
* message (if there are still descriptors left unused).
*
* Returns:
* 0 if the message won't fit, 1 if it will.
*
* Note:
*
* Assumes a TX message is active (i2400m->tx_msg).
*
* Assumes i2400m->tx_lock is taken, and we use that as a barrier
*/
static
unsigned i2400m_tx_fits(struct i2400m *i2400m)
{
struct i2400m_msg_hdr *msg_hdr = i2400m->tx_msg;
return le16_to_cpu(msg_hdr->num_pls) < I2400M_TX_PLD_MAX;
}
/*
* Start a new TX message header in the queue.
*
* Reserve memory from the base FIFO engine and then just initialize
* the message header.
*
* We allocate the biggest TX message header we might need (one that'd
* fit I2400M_TX_PLD_MAX payloads) -- when it is closed it will be
* 'ironed it out' and the unneeded parts removed.
*
* NOTE:
*
* Assumes that the previous message is CLOSED (eg: either
* there was none or 'i2400m_tx_close()' was called on it).
*
* Assumes i2400m->tx_lock is taken, and we use that as a barrier
*/
static
void i2400m_tx_new(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
struct i2400m_msg_hdr *tx_msg;
BUG_ON(i2400m->tx_msg != NULL);
try_head:
tx_msg = i2400m_tx_fifo_push(i2400m, I2400M_TX_PLD_SIZE, 0);
if (tx_msg == NULL)
goto out;
else if (tx_msg == TAIL_FULL) {
i2400m_tx_skip_tail(i2400m);
d_printf(2, dev, "new TX message: tail full, trying head\n");
goto try_head;
}
memset(tx_msg, 0, I2400M_TX_PLD_SIZE);
tx_msg->size = I2400M_TX_PLD_SIZE;
out:
i2400m->tx_msg = tx_msg;
d_printf(2, dev, "new TX message: %p @%zu\n",
tx_msg, (void *) tx_msg - i2400m->tx_buf);
}
/*
* Finalize the current TX message header
*
* Sets the message header to be at the proper location depending on
* how many descriptors we have (check documentation at the file's
* header for more info on that).
*
* Appends padding bytes to make sure the whole TX message (counting
* from the 'relocated' message header) is aligned to
* tx_block_size. We assume the _append() code has left enough space
* in the FIFO for that. If there are no payloads, just pass, as it
* won't be transferred.
*
* The amount of padding bytes depends on how many payloads are in the
* TX message, as the "msg header and payload descriptors" will be
* shifted up in the buffer.
*/
static
void i2400m_tx_close(struct i2400m *i2400m)
{
struct device *dev = i2400m_dev(i2400m);
struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg;
struct i2400m_msg_hdr *tx_msg_moved;
size_t aligned_size, padding, hdr_size;
void *pad_buf;
if (tx_msg->size & I2400M_TX_SKIP) /* a skipper? nothing to do */
goto out;
/* Relocate the message header
*
* Find the current header size, align it to 16 and if we need
* to move it so the tail is next to the payloads, move it and
* set the offset.
*
* If it moved, this header is good only for transmission; the
* original one (it is kept if we moved) is still used to
* figure out where the next TX message starts (and where the
* offset to the moved header is).
*/
hdr_size = sizeof(*tx_msg)
+ le16_to_cpu(tx_msg->num_pls) * sizeof(tx_msg->pld[0]);
hdr_size = ALIGN(hdr_size, I2400M_PL_PAD);
tx_msg->offset = I2400M_TX_PLD_SIZE - hdr_size;
tx_msg_moved = (void *) tx_msg + tx_msg->offset;
memmove(tx_msg_moved, tx_msg, hdr_size);
tx_msg_moved->size -= tx_msg->offset;
/*
* Now figure out how much we have to add to the (moved!)
* message so the size is a multiple of i2400m->bus_tx_block_size.
*/
aligned_size = ALIGN(tx_msg_moved->size, i2400m->bus_tx_block_size);
padding = aligned_size - tx_msg_moved->size;
if (padding > 0) {
pad_buf = i2400m_tx_fifo_push(i2400m, padding, 0);
if (unlikely(WARN_ON(pad_buf == NULL
|| pad_buf == TAIL_FULL))) {
/* This should not happen -- append should verify
* there is always space left at least to append
* tx_block_size */
dev_err(dev,
"SW BUG! Possible data leakage from memory the "
"device should not read for padding - "
"size %lu aligned_size %zu tx_buf %p in "
"%zu out %zu\n",
(unsigned long) tx_msg_moved->size,
aligned_size, i2400m->tx_buf, i2400m->tx_in,
i2400m->tx_out);
} else
memset(pad_buf, 0xad, padding);
}
tx_msg_moved->padding = cpu_to_le16(padding);
tx_msg_moved->size += padding;
if (tx_msg != tx_msg_moved)
tx_msg->size += padding;
out:
i2400m->tx_msg = NULL;
}
/**
* i2400m_tx - send the data in a buffer to the device
*
* @buf: pointer to the buffer to transmit
*
* @buf_len: buffer size
*
* @pl_type: type of the payload we are sending.
*
* Returns:
* 0 if ok, < 0 errno code on error (-ENOSPC, if there is no more
* room for the message in the queue).
*
* Appends the buffer to the TX FIFO and notifies the bus-specific
* part of the driver that there is new data ready to transmit.
* Once this function returns, the buffer has been copied, so it can
* be reused.
*
* The steps followed to append are explained in detail in the file
* header.
*
* Whenever we write to a message, we increase msg->size, so it
* reflects exactly how big the message is. This is needed so that if
* we concatenate two messages before they can be sent, the code that
* sends the messages can find the boundaries (and it will replace the
* size with the real barker before sending).
*
* Note:
*
* Cold and warm reset payloads need to be sent as a single
* payload, so we handle that.
*/
int i2400m_tx(struct i2400m *i2400m, const void *buf, size_t buf_len,
enum i2400m_pt pl_type)
{
int result = -ENOSPC;
struct device *dev = i2400m_dev(i2400m);
unsigned long flags;
size_t padded_len;
void *ptr;
unsigned is_singleton = pl_type == I2400M_PT_RESET_WARM
|| pl_type == I2400M_PT_RESET_COLD;
d_fnstart(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u)\n",
i2400m, buf, buf_len, pl_type);
padded_len = ALIGN(buf_len, I2400M_PL_PAD);
d_printf(5, dev, "padded_len %zd buf_len %zd\n", padded_len, buf_len);
/* If there is no current TX message, create one; if the
* current one is out of payload slots or we have a singleton,
* close it and start a new one */
spin_lock_irqsave(&i2400m->tx_lock, flags);
try_new:
if (unlikely(i2400m->tx_msg == NULL))
i2400m_tx_new(i2400m);
else if (unlikely(!i2400m_tx_fits(i2400m)
|| (is_singleton && i2400m->tx_msg->num_pls != 0))) {
d_printf(2, dev, "closing TX message (fits %u singleton "
"%u num_pls %u)\n", i2400m_tx_fits(i2400m),
is_singleton, i2400m->tx_msg->num_pls);
i2400m_tx_close(i2400m);
i2400m_tx_new(i2400m);
}
if (i2400m->tx_msg->size + padded_len > I2400M_TX_BUF_SIZE / 2) {
d_printf(2, dev, "TX: message too big, going new\n");
i2400m_tx_close(i2400m);
i2400m_tx_new(i2400m);
}
if (i2400m->tx_msg == NULL)
goto error_tx_new;
/* So we have a current message header; now append space for
* the message -- if there is not enough, try the head */
ptr = i2400m_tx_fifo_push(i2400m, padded_len,
i2400m->bus_tx_block_size);
if (ptr == TAIL_FULL) { /* Tail is full, try head */
d_printf(2, dev, "pl append: tail full\n");
i2400m_tx_close(i2400m);
i2400m_tx_skip_tail(i2400m);
goto try_new;
} else if (ptr == NULL) { /* All full */
result = -ENOSPC;
d_printf(2, dev, "pl append: all full\n");
} else { /* Got space, copy it, set padding */
struct i2400m_msg_hdr *tx_msg = i2400m->tx_msg;
unsigned num_pls = le16_to_cpu(tx_msg->num_pls);
memcpy(ptr, buf, buf_len);
memset(ptr + buf_len, 0xad, padded_len - buf_len);
i2400m_pld_set(&tx_msg->pld[num_pls], buf_len, pl_type);
d_printf(3, dev, "pld 0x%08x (type 0x%1x len 0x%04zx\n",
le32_to_cpu(tx_msg->pld[num_pls].val),
pl_type, buf_len);
tx_msg->num_pls = le16_to_cpu(num_pls+1);
tx_msg->size += padded_len;
d_printf(2, dev, "TX: appended %zu b (up to %u b) pl #%u \n",
padded_len, tx_msg->size, num_pls+1);
d_printf(2, dev,
"TX: appended hdr @%zu %zu b pl #%u @%zu %zu/%zu b\n",
(void *)tx_msg - i2400m->tx_buf, (size_t)tx_msg->size,
num_pls+1, ptr - i2400m->tx_buf, buf_len, padded_len);
result = 0;
if (is_singleton)
i2400m_tx_close(i2400m);
}
error_tx_new:
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
i2400m->bus_tx_kick(i2400m); /* always kick, might free up space */
d_fnend(3, dev, "(i2400m %p skb %p [%zu bytes] pt %u) = %d\n",
i2400m, buf, buf_len, pl_type, result);
return result;
}
EXPORT_SYMBOL_GPL(i2400m_tx);
/**
* i2400m_tx_msg_get - Get the first TX message in the FIFO to start sending it
*
* @i2400m: device descriptors
* @bus_size: where to place the size of the TX message
*
* Called by the bus-specific driver to get the first TX message at
* the FIF that is ready for transmission.
*
* It sets the state in @i2400m to indicate the bus-specific driver is
* transfering that message (i2400m->tx_msg_size).
*
* Once the transfer is completed, call i2400m_tx_msg_sent().
*
* Notes:
*
* The size of the TX message to be transmitted might be smaller than
* that of the TX message in the FIFO (in case the header was
* shorter). Hence, we copy it in @bus_size, for the bus layer to
* use. We keep the message's size in i2400m->tx_msg_size so that
* when the bus later is done transferring we know how much to
* advance the fifo.
*
* We collect statistics here as all the data is available and we
* assume it is going to work [see i2400m_tx_msg_sent()].
*/
struct i2400m_msg_hdr *i2400m_tx_msg_get(struct i2400m *i2400m,
size_t *bus_size)
{
struct device *dev = i2400m_dev(i2400m);
struct i2400m_msg_hdr *tx_msg, *tx_msg_moved;
unsigned long flags, pls;
d_fnstart(3, dev, "(i2400m %p bus_size %p)\n", i2400m, bus_size);
spin_lock_irqsave(&i2400m->tx_lock, flags);
skip:
tx_msg_moved = NULL;
if (i2400m->tx_in == i2400m->tx_out) { /* Empty FIFO? */
i2400m->tx_in = 0;
i2400m->tx_out = 0;
d_printf(2, dev, "TX: FIFO empty: resetting\n");
goto out_unlock;
}
tx_msg = i2400m->tx_buf + i2400m->tx_out % I2400M_TX_BUF_SIZE;
if (tx_msg->size & I2400M_TX_SKIP) { /* skip? */
d_printf(2, dev, "TX: skip: msg @%zu (%zu b)\n",
i2400m->tx_out % I2400M_TX_BUF_SIZE,
(size_t) tx_msg->size & ~I2400M_TX_SKIP);
i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP;
goto skip;
}
if (tx_msg->num_pls == 0) { /* No payloads? */
if (tx_msg == i2400m->tx_msg) { /* open, we are done */
d_printf(2, dev,
"TX: FIFO empty: open msg w/o payloads @%zu\n",
(void *) tx_msg - i2400m->tx_buf);
tx_msg = NULL;
goto out_unlock;
} else { /* closed, skip it */
d_printf(2, dev,
"TX: skip msg w/o payloads @%zu (%zu b)\n",
(void *) tx_msg - i2400m->tx_buf,
(size_t) tx_msg->size);
i2400m->tx_out += tx_msg->size & ~I2400M_TX_SKIP;
goto skip;
}
}
if (tx_msg == i2400m->tx_msg) /* open msg? */
i2400m_tx_close(i2400m);
/* Now we have a valid TX message (with payloads) to TX */
tx_msg_moved = (void *) tx_msg + tx_msg->offset;
i2400m->tx_msg_size = tx_msg->size;
*bus_size = tx_msg_moved->size;
d_printf(2, dev, "TX: pid %d msg hdr at @%zu offset +@%zu "
"size %zu bus_size %zu\n",
current->pid, (void *) tx_msg - i2400m->tx_buf,
(size_t) tx_msg->offset, (size_t) tx_msg->size,
(size_t) tx_msg_moved->size);
tx_msg_moved->barker = le32_to_cpu(I2400M_H2D_PREVIEW_BARKER);
tx_msg_moved->sequence = le32_to_cpu(i2400m->tx_sequence++);
pls = le32_to_cpu(tx_msg_moved->num_pls);
i2400m->tx_pl_num += pls; /* Update stats */
if (pls > i2400m->tx_pl_max)
i2400m->tx_pl_max = pls;
if (pls < i2400m->tx_pl_min)
i2400m->tx_pl_min = pls;
i2400m->tx_num++;
i2400m->tx_size_acc += *bus_size;
if (*bus_size < i2400m->tx_size_min)
i2400m->tx_size_min = *bus_size;
if (*bus_size > i2400m->tx_size_max)
i2400m->tx_size_max = *bus_size;
out_unlock:
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnstart(3, dev, "(i2400m %p bus_size %p [%zu]) = %p\n",
i2400m, bus_size, *bus_size, tx_msg_moved);
return tx_msg_moved;
}
EXPORT_SYMBOL_GPL(i2400m_tx_msg_get);
/**
* i2400m_tx_msg_sent - indicate the transmission of a TX message
*
* @i2400m: device descriptor
*
* Called by the bus-specific driver when a message has been sent;
* this pops it from the FIFO; and as there is space, start the queue
* in case it was stopped.
*
* Should be called even if the message send failed and we are
* dropping this TX message.
*/
void i2400m_tx_msg_sent(struct i2400m *i2400m)
{
unsigned n;
unsigned long flags;
struct device *dev = i2400m_dev(i2400m);
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
spin_lock_irqsave(&i2400m->tx_lock, flags);
i2400m->tx_out += i2400m->tx_msg_size;
d_printf(2, dev, "TX: sent %zu b\n", (size_t) i2400m->tx_msg_size);
i2400m->tx_msg_size = 0;
BUG_ON(i2400m->tx_out > i2400m->tx_in);
/* level them FIFO markers off */
n = i2400m->tx_out / I2400M_TX_BUF_SIZE;
i2400m->tx_out %= I2400M_TX_BUF_SIZE;
i2400m->tx_in -= n * I2400M_TX_BUF_SIZE;
netif_start_queue(i2400m->wimax_dev.net_dev);
spin_unlock_irqrestore(&i2400m->tx_lock, flags);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
EXPORT_SYMBOL_GPL(i2400m_tx_msg_sent);
/**
* i2400m_tx_setup - Initialize the TX queue and infrastructure
*
* Make sure we reset the TX sequence to zero, as when this function
* is called, the firmware has been just restarted.
*/
int i2400m_tx_setup(struct i2400m *i2400m)
{
int result;
/* Do this here only once -- can't do on
* i2400m_hard_start_xmit() as we'll cause race conditions if
* the WS was scheduled on another CPU */
INIT_WORK(&i2400m->wake_tx_ws, i2400m_wake_tx_work);
i2400m->tx_sequence = 0;
i2400m->tx_buf = kmalloc(I2400M_TX_BUF_SIZE, GFP_KERNEL);
if (i2400m->tx_buf == NULL)
result = -ENOMEM;
else
result = 0;
/* Huh? the bus layer has to define this... */
BUG_ON(i2400m->bus_tx_block_size == 0);
return result;
}
/**
* i2400m_tx_release - Tear down the TX queue and infrastructure
*/
void i2400m_tx_release(struct i2400m *i2400m)
{
kfree(i2400m->tx_buf);
}

View file

@ -0,0 +1,42 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Debug levels control file for the i2400m-usb module
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
#ifndef __debug_levels__h__
#define __debug_levels__h__
/* Maximum compile and run time debug level for all submodules */
#define D_MODULENAME i2400m_usb
#define D_MASTER CONFIG_WIMAX_I2400M_DEBUG_LEVEL
#include <linux/wimax/debug.h>
/* List of all the enabled modules */
enum d_module {
D_SUBMODULE_DECLARE(usb),
D_SUBMODULE_DECLARE(fw),
D_SUBMODULE_DECLARE(notif),
D_SUBMODULE_DECLARE(rx),
D_SUBMODULE_DECLARE(tx),
};
#endif /* #ifndef __debug_levels__h__ */

View file

@ -0,0 +1,340 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Firmware uploader's USB specifics
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - bus generic/specific split
*
* THE PROCEDURE
*
* See fw.c for the generic description of this procedure.
*
* This file implements only the USB specifics. It boils down to how
* to send a command and waiting for an acknowledgement from the
* device.
*
* This code (and process) is single threaded. It assumes it is the
* only thread poking around (guaranteed by fw.c).
*
* COMMAND EXECUTION
*
* A write URB is posted with the buffer to the bulk output endpoint.
*
* ACK RECEPTION
*
* We just post a URB to the notification endpoint and wait for
* data. We repeat until we get all the data we expect (as indicated
* by the call from the bus generic code).
*
* The data is not read from the bulk in endpoint for boot mode.
*
* ROADMAP
*
* i2400mu_bus_bm_cmd_send
* i2400m_bm_cmd_prepare...
* i2400mu_tx_bulk_out
*
* i2400mu_bus_bm_wait_for_ack
* i2400m_notif_submit
*/
#include <linux/usb.h>
#include "i2400m-usb.h"
#define D_SUBMODULE fw
#include "usb-debug-levels.h"
/*
* Synchronous write to the device
*
* Takes care of updating EDC counts and thus, handle device errors.
*/
static
ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
{
int result;
struct device *dev = &i2400mu->usb_iface->dev;
int len;
struct usb_endpoint_descriptor *epd;
int pipe, do_autopm = 1;
result = usb_autopm_get_interface(i2400mu->usb_iface);
if (result < 0) {
dev_err(dev, "BM-CMD: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
result = usb_bulk_msg(i2400mu->usb_dev, pipe, buf, buf_size, &len, HZ);
switch (result) {
case 0:
if (len != buf_size) {
dev_err(dev, "BM-CMD: short write (%u B vs %zu "
"expected)\n", len, buf_size);
result = -EIO;
break;
}
result = len;
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
break;
case -ETIMEDOUT: /* bah... */
break;
default: /* any other? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: maximum errors in "
"URB exceeded; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
result = -ENODEV;
break;
}
dev_err(dev, "BM-CMD: URB error %d, retrying\n",
result);
goto retry;
}
result = len;
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
return result;
}
/*
* Send a boot-mode command over the bulk-out pipe
*
* Command can be a raw command, which requires no preparation (and
* which might not even be following the command format). Checks that
* the right amount of data was transfered.
*
* To satisfy USB requirements (no onstack, vmalloc or in data segment
* buffers), we copy the command to i2400m->bm_cmd_buf and send it from
* there.
*
* @flags: pass thru from i2400m_bm_cmd()
* @return: cmd_size if ok, < 0 errno code on error.
*/
ssize_t i2400mu_bus_bm_cmd_send(struct i2400m *i2400m,
const struct i2400m_bootrom_header *_cmd,
size_t cmd_size, int flags)
{
ssize_t result;
struct device *dev = i2400m_dev(i2400m);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
int opcode = _cmd == NULL ? -1 : i2400m_brh_get_opcode(_cmd);
struct i2400m_bootrom_header *cmd;
size_t cmd_size_a = ALIGN(cmd_size, 16); /* USB restriction */
d_fnstart(8, dev, "(i2400m %p cmd %p size %zu)\n",
i2400m, _cmd, cmd_size);
result = -E2BIG;
if (cmd_size > I2400M_BM_CMD_BUF_SIZE)
goto error_too_big;
memcpy(i2400m->bm_cmd_buf, _cmd, cmd_size);
cmd = i2400m->bm_cmd_buf;
if (cmd_size_a > cmd_size) /* Zero pad space */
memset(i2400m->bm_cmd_buf + cmd_size, 0, cmd_size_a - cmd_size);
if ((flags & I2400M_BM_CMD_RAW) == 0) {
if (WARN_ON(i2400m_brh_get_response_required(cmd) == 0))
dev_warn(dev, "SW BUG: response_required == 0\n");
i2400m_bm_cmd_prepare(cmd);
}
result = i2400mu_tx_bulk_out(i2400mu, i2400m->bm_cmd_buf, cmd_size);
if (result < 0) {
dev_err(dev, "boot-mode cmd %d: cannot send: %zd\n",
opcode, result);
goto error_cmd_send;
}
if (result != cmd_size) { /* all was transferred? */
dev_err(dev, "boot-mode cmd %d: incomplete transfer "
"(%zu vs %zu submitted)\n", opcode, result, cmd_size);
result = -EIO;
goto error_cmd_size;
}
error_cmd_size:
error_cmd_send:
error_too_big:
d_fnend(8, dev, "(i2400m %p cmd %p size %zu) = %zd\n",
i2400m, _cmd, cmd_size, result);
return result;
}
static
void __i2400mu_bm_notif_cb(struct urb *urb)
{
complete(urb->context);
}
/*
* submit a read to the notification endpoint
*
* @i2400m: device descriptor
* @urb: urb to use
* @completion: completion varible to complete when done
*
* Data is always read to i2400m->bm_ack_buf
*/
static
int i2400mu_notif_submit(struct i2400mu *i2400mu, struct urb *urb,
struct completion *completion)
{
struct i2400m *i2400m = &i2400mu->i2400m;
struct usb_endpoint_descriptor *epd;
int pipe;
epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION);
pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(urb, i2400mu->usb_dev, pipe,
i2400m->bm_ack_buf, I2400M_BM_ACK_BUF_SIZE,
__i2400mu_bm_notif_cb, completion,
epd->bInterval);
return usb_submit_urb(urb, GFP_KERNEL);
}
/*
* Read an ack from the notification endpoint
*
* @i2400m:
* @_ack: pointer to where to store the read data
* @ack_size: how many bytes we should read
*
* Returns: < 0 errno code on error; otherwise, amount of received bytes.
*
* Submits a notification read, appends the read data to the given ack
* buffer and then repeats (until @ack_size bytes have been
* received).
*/
ssize_t i2400mu_bus_bm_wait_for_ack(struct i2400m *i2400m,
struct i2400m_bootrom_header *_ack,
size_t ack_size)
{
ssize_t result = -ENOMEM;
struct device *dev = i2400m_dev(i2400m);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct urb notif_urb;
void *ack = _ack;
size_t offset, len;
long val;
int do_autopm = 1;
DECLARE_COMPLETION_ONSTACK(notif_completion);
d_fnstart(8, dev, "(i2400m %p ack %p size %zu)\n",
i2400m, ack, ack_size);
BUG_ON(_ack == i2400m->bm_ack_buf);
result = usb_autopm_get_interface(i2400mu->usb_iface);
if (result < 0) {
dev_err(dev, "BM-ACK: can't get autopm: %d\n", (int) result);
do_autopm = 0;
}
usb_init_urb(&notif_urb); /* ready notifications */
usb_get_urb(&notif_urb);
offset = 0;
while (offset < ack_size) {
init_completion(&notif_completion);
result = i2400mu_notif_submit(i2400mu, &notif_urb,
&notif_completion);
if (result < 0)
goto error_notif_urb_submit;
val = wait_for_completion_interruptible_timeout(
&notif_completion, HZ);
if (val == 0) {
result = -ETIMEDOUT;
usb_kill_urb(&notif_urb); /* Timedout */
goto error_notif_wait;
}
if (val == -ERESTARTSYS) {
result = -EINTR; /* Interrupted */
usb_kill_urb(&notif_urb);
goto error_notif_wait;
}
result = notif_urb.status; /* How was the ack? */
switch (result) {
case 0:
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
goto error_dev_gone;
default: /* any other? */
usb_kill_urb(&notif_urb); /* Timedout */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_exceeded;
dev_err(dev, "BM-ACK: URB error %d, "
"retrying\n", notif_urb.status);
continue; /* retry */
}
if (notif_urb.actual_length == 0) {
d_printf(6, dev, "ZLP received, retrying\n");
continue;
}
/* Got data, append it to the buffer */
len = min(ack_size - offset, (size_t) notif_urb.actual_length);
memcpy(ack + offset, i2400m->bm_ack_buf, len);
offset += len;
}
result = offset;
error_notif_urb_submit:
error_notif_wait:
error_dev_gone:
out:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(8, dev, "(i2400m %p ack %p size %zu) = %zd\n",
i2400m, ack, ack_size, result);
return result;
error_exceeded:
dev_err(dev, "bm: maximum errors in notification URB exceeded; "
"resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
goto out;
}

View file

@ -0,0 +1,269 @@
/*
* Intel Wireless WiMAX Connection 2400m over USB
* Notification handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Initial implementation
*
*
* The notification endpoint is active when the device is not in boot
* mode; in here we just read and get notifications; based on those,
* we act to either reinitialize the device after a reboot or to
* submit a RX request.
*
* ROADMAP
*
* i2400mu_usb_notification_setup()
*
* i2400mu_usb_notification_release()
*
* i2400mu_usb_notification_cb() Called when a URB is ready
* i2400mu_notif_grok()
* i2400m_dev_reset_handle()
* i2400mu_rx_kick()
*/
#include <linux/usb.h>
#include "i2400m-usb.h"
#define D_SUBMODULE notif
#include "usb-debug-levels.h"
static const
__le32 i2400m_ZERO_BARKER[4] = { 0, 0, 0, 0 };
/*
* Process a received notification
*
* In normal operation mode, we can only receive two types of payloads
* on the notification endpoint:
*
* - a reboot barker, we do a bootstrap (the device has reseted).
*
* - a block of zeroes: there is pending data in the IN endpoint
*/
static
int i2400mu_notification_grok(struct i2400mu *i2400mu, const void *buf,
size_t buf_len)
{
int ret;
struct device *dev = &i2400mu->usb_iface->dev;
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(4, dev, "(i2400m %p buf %p buf_len %zu)\n",
i2400mu, buf, buf_len);
ret = -EIO;
if (buf_len < sizeof(i2400m_NBOOT_BARKER))
/* Not a bug, just ignore */
goto error_bad_size;
if (!memcmp(i2400m_NBOOT_BARKER, buf, sizeof(i2400m_NBOOT_BARKER))
|| !memcmp(i2400m_SBOOT_BARKER, buf, sizeof(i2400m_SBOOT_BARKER)))
ret = i2400m_dev_reset_handle(i2400m);
else if (!memcmp(i2400m_ZERO_BARKER, buf, sizeof(i2400m_ZERO_BARKER))) {
i2400mu_rx_kick(i2400mu);
ret = 0;
} else { /* Unknown or unexpected data in the notif message */
char prefix[64];
ret = -EIO;
dev_err(dev, "HW BUG? Unknown/unexpected data in notification "
"message (%zu bytes)\n", buf_len);
snprintf(prefix, sizeof(prefix), "%s %s: ",
dev_driver_string(dev) , dev->bus_id);
if (buf_len > 64) {
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
8, 4, buf, 64, 0);
printk(KERN_ERR "%s... (only first 64 bytes "
"dumped)\n", prefix);
} else
print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_OFFSET,
8, 4, buf, buf_len, 0);
}
error_bad_size:
d_fnend(4, dev, "(i2400m %p buf %p buf_len %zu) = %d\n",
i2400mu, buf, buf_len, ret);
return ret;
}
/*
* URB callback for the notification endpoint
*
* @urb: the urb received from the notification endpoint
*
* This function will just process the USB side of the transaction,
* checking everything is fine, pass the processing to
* i2400m_notification_grok() and resubmit the URB.
*/
static
void i2400mu_notification_cb(struct urb *urb)
{
int ret;
struct i2400mu *i2400mu = urb->context;
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(4, dev, "(urb %p status %d actual_length %d)\n",
urb, urb->status, urb->actual_length);
ret = urb->status;
switch (ret) {
case 0:
ret = i2400mu_notification_grok(i2400mu, urb->transfer_buffer,
urb->actual_length);
if (ret == -EIO && edc_inc(&i2400mu->urb_edc, EDC_MAX_ERRORS,
EDC_ERROR_TIMEFRAME))
goto error_exceeded;
if (ret == -ENOMEM) /* uff...power cycle? shutdown? */
goto error_exceeded;
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* ditto */
case -ESHUTDOWN: /* URB killed */
case -ECONNRESET: /* disconnection */
goto out; /* Notify around */
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_exceeded;
dev_err(dev, "notification: URB error %d, retrying\n",
urb->status);
}
usb_mark_last_busy(i2400mu->usb_dev);
ret = usb_submit_urb(i2400mu->notif_urb, GFP_ATOMIC);
switch (ret) {
case 0:
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* ditto */
case -ESHUTDOWN: /* URB killed */
case -ECONNRESET: /* disconnection */
break; /* just ignore */
default: /* Some error? */
dev_err(dev, "notification: cannot submit URB: %d\n", ret);
goto error_submit;
}
d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
urb, urb->status, urb->actual_length);
return;
error_exceeded:
dev_err(dev, "maximum errors in notification URB exceeded; "
"resetting device\n");
error_submit:
usb_queue_reset_device(i2400mu->usb_iface);
out:
d_fnend(4, dev, "(urb %p status %d actual_length %d) = void\n",
urb, urb->status, urb->actual_length);
return;
}
/*
* setup the notification endpoint
*
* @i2400m: device descriptor
*
* This procedure prepares the notification urb and handler for receiving
* unsolicited barkers from the device.
*/
int i2400mu_notification_setup(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, ret = 0;
struct usb_endpoint_descriptor *epd;
char *buf;
d_fnstart(4, dev, "(i2400m %p)\n", i2400mu);
buf = kmalloc(I2400MU_MAX_NOTIFICATION_LEN, GFP_KERNEL | GFP_DMA);
if (buf == NULL) {
dev_err(dev, "notification: buffer allocation failed\n");
ret = -ENOMEM;
goto error_buf_alloc;
}
i2400mu->notif_urb = usb_alloc_urb(0, GFP_KERNEL);
if (!i2400mu->notif_urb) {
ret = -ENOMEM;
dev_err(dev, "notification: cannot allocate URB\n");
goto error_alloc_urb;
}
epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_NOTIFICATION);
usb_pipe = usb_rcvintpipe(i2400mu->usb_dev, epd->bEndpointAddress);
usb_fill_int_urb(i2400mu->notif_urb, i2400mu->usb_dev, usb_pipe,
buf, I2400MU_MAX_NOTIFICATION_LEN,
i2400mu_notification_cb, i2400mu, epd->bInterval);
ret = usb_submit_urb(i2400mu->notif_urb, GFP_KERNEL);
if (ret != 0) {
dev_err(dev, "notification: cannot submit URB: %d\n", ret);
goto error_submit;
}
d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
return ret;
error_submit:
usb_free_urb(i2400mu->notif_urb);
error_alloc_urb:
kfree(buf);
error_buf_alloc:
d_fnend(4, dev, "(i2400m %p) = %d\n", i2400mu, ret);
return ret;
}
/*
* Tear down of the notification mechanism
*
* @i2400m: device descriptor
*
* Kill the interrupt endpoint urb, free any allocated resources.
*
* We need to check if we have done it before as for example,
* _suspend() call this; if after a suspend() we get a _disconnect()
* (as the case is when hibernating), nothing bad happens.
*/
void i2400mu_notification_release(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
if (i2400mu->notif_urb != NULL) {
usb_kill_urb(i2400mu->notif_urb);
kfree(i2400mu->notif_urb->transfer_buffer);
usb_free_urb(i2400mu->notif_urb);
i2400mu->notif_urb = NULL;
}
d_fnend(4, dev, "(i2400mu %p)\n", i2400mu);
}

View file

@ -0,0 +1,417 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB RX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Use skb_clone(), break up processing in chunks
* - Split transport/device specific
* - Make buffer size dynamic to exert less memory pressure
*
*
* This handles the RX path on USB.
*
* When a notification is received that says 'there is RX data ready',
* we call i2400mu_rx_kick(); that wakes up the RX kthread, which
* reads a buffer from USB and passes it to i2400m_rx() in the generic
* handling code. The RX buffer has an specific format that is
* described in rx.c.
*
* We use a kernel thread in a loop because:
*
* - we want to be able to call the USB power management get/put
* functions (blocking) before each transaction.
*
* - We might get a lot of notifications and we don't want to submit
* a zillion reads; by serializing, we are throttling.
*
* - RX data processing can get heavy enough so that it is not
* appropiate for doing it in the USB callback; thus we run it in a
* process context.
*
* We provide a read buffer of an arbitrary size (short of a page); if
* the callback reports -EOVERFLOW, it means it was too small, so we
* just double the size and retry (being careful to append, as
* sometimes the device provided some data). Every now and then we
* check if the average packet size is smaller than the current packet
* size and if so, we halve it. At the end, the size of the
* preallocated buffer should be following the average received
* transaction size, adapting dynamically to it.
*
* ROADMAP
*
* i2400mu_rx_kick() Called from notif.c when we get a
* 'data ready' notification
* i2400mu_rxd() Kernel RX daemon
* i2400mu_rx() Receive USB data
* i2400m_rx() Send data to generic i2400m RX handling
*
* i2400mu_rx_setup() called from i2400mu_bus_dev_start()
*
* i2400mu_rx_release() called from i2400mu_bus_dev_stop()
*/
#include <linux/workqueue.h>
#include <linux/usb.h>
#include "i2400m-usb.h"
#define D_SUBMODULE rx
#include "usb-debug-levels.h"
/*
* Dynamic RX size
*
* We can't let the rx_size be a multiple of 512 bytes (the RX
* endpoint's max packet size). On some USB host controllers (we
* haven't been able to fully characterize which), if the device is
* about to send (for example) X bytes and we only post a buffer to
* receive n*512, it will fail to mark that as babble (so that
* i2400mu_rx() [case -EOVERFLOW] can resize the buffer and get the
* rest).
*
* So on growing or shrinking, if it is a multiple of the
* maxpacketsize, we remove some (instead of incresing some, so in a
* buddy allocator we try to waste less space).
*
* Note we also need a hook for this on i2400mu_rx() -- when we do the
* first read, we are sure we won't hit this spot because
* i240mm->rx_size has been set properly. However, if we have to
* double because of -EOVERFLOW, when we launch the read to get the
* rest of the data, we *have* to make sure that also is not a
* multiple of the max_pkt_size.
*/
static
size_t i2400mu_rx_size_grow(struct i2400mu *i2400mu)
{
struct device *dev = &i2400mu->usb_iface->dev;
size_t rx_size;
const size_t max_pkt_size = 512;
rx_size = 2 * i2400mu->rx_size;
if (rx_size % max_pkt_size == 0) {
rx_size -= 8;
d_printf(1, dev,
"RX: expected size grew to %zu [adjusted -8] "
"from %zu\n",
rx_size, i2400mu->rx_size);
} else
d_printf(1, dev,
"RX: expected size grew to %zu from %zu\n",
rx_size, i2400mu->rx_size);
return rx_size;
}
static
void i2400mu_rx_size_maybe_shrink(struct i2400mu *i2400mu)
{
const size_t max_pkt_size = 512;
struct device *dev = &i2400mu->usb_iface->dev;
if (unlikely(i2400mu->rx_size_cnt >= 100
&& i2400mu->rx_size_auto_shrink)) {
size_t avg_rx_size =
i2400mu->rx_size_acc / i2400mu->rx_size_cnt;
size_t new_rx_size = i2400mu->rx_size / 2;
if (avg_rx_size < new_rx_size) {
if (new_rx_size % max_pkt_size == 0) {
new_rx_size -= 8;
d_printf(1, dev,
"RX: expected size shrank to %zu "
"[adjusted -8] from %zu\n",
new_rx_size, i2400mu->rx_size);
} else
d_printf(1, dev,
"RX: expected size shrank to %zu "
"from %zu\n",
new_rx_size, i2400mu->rx_size);
i2400mu->rx_size = new_rx_size;
i2400mu->rx_size_cnt = 0;
i2400mu->rx_size_acc = i2400mu->rx_size;
}
}
}
/*
* Receive a message with payloads from the USB bus into an skb
*
* @i2400mu: USB device descriptor
* @rx_skb: skb where to place the received message
*
* Deals with all the USB-specifics of receiving, dynamically
* increasing the buffer size if so needed. Returns the payload in the
* skb, ready to process. On a zero-length packet, we retry.
*
* On soft USB errors, we retry (until they become too frequent and
* then are promoted to hard); on hard USB errors, we reset the
* device. On other errors (skb realloacation, we just drop it and
* hope for the next invocation to solve it).
*
* Returns: pointer to the skb if ok, ERR_PTR on error.
* NOTE: this function might realloc the skb (if it is too small),
* so always update with the one returned.
* ERR_PTR() is < 0 on error.
*/
static
struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)
{
int result = 0;
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, read_size, rx_size, do_autopm;
struct usb_endpoint_descriptor *epd;
const size_t max_pkt_size = 512;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
do_autopm = atomic_read(&i2400mu->do_autopm);
result = do_autopm ?
usb_autopm_get_interface(i2400mu->usb_iface) : 0;
if (result < 0) {
dev_err(dev, "RX: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_IN);
usb_pipe = usb_rcvbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
rx_size = skb_end_pointer(rx_skb) - rx_skb->data - rx_skb->len;
if (unlikely(rx_size % max_pkt_size == 0)) {
rx_size -= 8;
d_printf(1, dev, "RX: rx_size adapted to %d [-8]\n", rx_size);
}
result = usb_bulk_msg(
i2400mu->usb_dev, usb_pipe, rx_skb->data + rx_skb->len,
rx_size, &read_size, HZ);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
if (read_size == 0)
goto retry; /* ZLP, just resubmit */
skb_put(rx_skb, read_size);
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN:
case -ECONNRESET:
break;
case -EOVERFLOW: { /* too small, reallocate */
struct sk_buff *new_skb;
rx_size = i2400mu_rx_size_grow(i2400mu);
if (rx_size <= (1 << 16)) /* cap it */
i2400mu->rx_size = rx_size;
else if (printk_ratelimit()) {
dev_err(dev, "BUG? rx_size up to %d\n", rx_size);
result = -EINVAL;
goto out;
}
skb_put(rx_skb, read_size);
new_skb = skb_copy_expand(rx_skb, 0, rx_size - rx_skb->len,
GFP_KERNEL);
if (new_skb == NULL) {
if (printk_ratelimit())
dev_err(dev, "RX: Can't reallocate skb to %d; "
"RX dropped\n", rx_size);
kfree(rx_skb);
result = 0;
goto out; /* drop it...*/
}
kfree_skb(rx_skb);
rx_skb = new_skb;
i2400mu->rx_size_cnt = 0;
i2400mu->rx_size_acc = i2400mu->rx_size;
d_printf(1, dev, "RX: size changed to %d, received %d, "
"copied %d, capacity %ld\n",
rx_size, read_size, rx_skb->len,
(long) (skb_end_pointer(new_skb) - new_skb->head));
goto retry;
}
/* In most cases, it happens due to the hardware scheduling a
* read when there was no data - unfortunately, we have no way
* to tell this timeout from a USB timeout. So we just ignore
* it. */
case -ETIMEDOUT:
dev_err(dev, "RX: timeout: %d\n", result);
result = 0;
break;
default: /* Any error */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME))
goto error_reset;
dev_err(dev, "RX: error receiving URB: %d, retrying\n", result);
goto retry;
}
out:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(4, dev, "(i2400mu %p) = %p\n", i2400mu, rx_skb);
return rx_skb;
error_reset:
dev_err(dev, "RX: maximum errors in URB exceeded; "
"resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
rx_skb = ERR_PTR(result);
goto out;
}
/*
* Kernel thread for USB reception of data
*
* This thread waits for a kick; once kicked, it will allocate an skb
* and receive a single message to it from USB (using
* i2400mu_rx()). Once received, it is passed to the generic i2400m RX
* code for processing.
*
* When done processing, it runs some dirty statistics to verify if
* the last 100 messages received were smaller than half of the
* current RX buffer size. In that case, the RX buffer size is
* halved. This will helps lowering the pressure on the memory
* allocator.
*
* Hard errors force the thread to exit.
*/
static
int i2400mu_rxd(void *_i2400mu)
{
int result = 0;
struct i2400mu *i2400mu = _i2400mu;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
size_t pending;
int rx_size;
struct sk_buff *rx_skb;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
while (1) {
d_printf(2, dev, "TX: waiting for messages\n");
pending = 0;
wait_event_interruptible(
i2400mu->rx_wq,
(kthread_should_stop() /* check this first! */
|| (pending = atomic_read(&i2400mu->rx_pending_count)))
);
if (kthread_should_stop())
break;
if (pending == 0)
continue;
rx_size = i2400mu->rx_size;
d_printf(2, dev, "RX: reading up to %d bytes\n", rx_size);
rx_skb = __netdev_alloc_skb(net_dev, rx_size, GFP_KERNEL);
if (rx_skb == NULL) {
dev_err(dev, "RX: can't allocate skb [%d bytes]\n",
rx_size);
msleep(50); /* give it some time? */
continue;
}
/* Receive the message with the payloads */
rx_skb = i2400mu_rx(i2400mu, rx_skb);
result = PTR_ERR(rx_skb);
if (IS_ERR(rx_skb))
goto out;
atomic_dec(&i2400mu->rx_pending_count);
if (rx_skb->len == 0) { /* some ignorable condition */
kfree_skb(rx_skb);
continue;
}
/* Deliver the message to the generic i2400m code */
i2400mu->rx_size_cnt++;
i2400mu->rx_size_acc += rx_skb->len;
result = i2400m_rx(i2400m, rx_skb);
if (result == -EIO
&& edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
goto error_reset;
}
/* Maybe adjust RX buffer size */
i2400mu_rx_size_maybe_shrink(i2400mu);
}
result = 0;
out:
d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
return result;
error_reset:
dev_err(dev, "RX: maximum errors in received buffer exceeded; "
"resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
goto out;
}
/*
* Start reading from the device
*
* @i2400m: device instance
*
* Notify the RX thread that there is data pending.
*/
void i2400mu_rx_kick(struct i2400mu *i2400mu)
{
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400mu %p)\n", i2400m);
atomic_inc(&i2400mu->rx_pending_count);
wake_up_all(&i2400mu->rx_wq);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
int i2400mu_rx_setup(struct i2400mu *i2400mu)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
i2400mu->rx_kthread = kthread_run(i2400mu_rxd, i2400mu, "%s-rx",
wimax_dev->name);
if (IS_ERR(i2400mu->rx_kthread)) {
result = PTR_ERR(i2400mu->rx_kthread);
dev_err(dev, "RX: cannot start thread: %d\n", result);
}
return result;
}
void i2400mu_rx_release(struct i2400mu *i2400mu)
{
kthread_stop(i2400mu->rx_kthread);
}

View file

@ -0,0 +1,229 @@
/*
* Intel Wireless WiMAX Connection 2400m
* USB specific TX handling
*
*
* Copyright (C) 2007-2008 Intel Corporation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Intel Corporation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*
* Intel Corporation <linux-wimax@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
* - Initial implementation
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* - Split transport/device specific
*
*
* Takes the TX messages in the i2400m's driver TX FIFO and sends them
* to the device until there are no more.
*
* If we fail sending the message, we just drop it. There isn't much
* we can do at this point. We could also retry, but the USB stack has
* already retried and still failed, so there is not much of a
* point. As well, most of the traffic is network, which has recovery
* methods for dropped packets.
*
* For sending we just obtain a FIFO buffer to send, send it to the
* USB bulk out, tell the TX FIFO code we have sent it; query for
* another one, etc... until done.
*
* We use a thread so we can call usb_autopm_enable() and
* usb_autopm_disable() for each transaction; this way when the device
* goes idle, it will suspend. It also has less overhead than a
* dedicated workqueue, as it is being used for a single task.
*
* ROADMAP
*
* i2400mu_tx_setup()
* i2400mu_tx_release()
*
* i2400mu_bus_tx_kick() - Called by the tx.c code when there
* is new data in the FIFO.
* i2400mu_txd()
* i2400m_tx_msg_get()
* i2400m_tx_msg_sent()
*/
#include "i2400m-usb.h"
#define D_SUBMODULE tx
#include "usb-debug-levels.h"
/*
* Get the next TX message in the TX FIFO and send it to the device
*
* Note that any iteration consumes a message to be sent, no matter if
* it succeeds or fails (we have no real way to retry or complain).
*
* Return: 0 if ok, < 0 errno code on hard error.
*/
static
int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
size_t tx_msg_size)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
int usb_pipe, sent_size, do_autopm;
struct usb_endpoint_descriptor *epd;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
do_autopm = atomic_read(&i2400mu->do_autopm);
result = do_autopm ?
usb_autopm_get_interface(i2400mu->usb_iface) : 0;
if (result < 0) {
dev_err(dev, "TX: can't get autopm: %d\n", result);
do_autopm = 0;
}
epd = usb_get_epd(i2400mu->usb_iface, I2400MU_EP_BULK_OUT);
usb_pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
retry:
result = usb_bulk_msg(i2400mu->usb_dev, usb_pipe,
tx_msg, tx_msg_size, &sent_size, HZ);
usb_mark_last_busy(i2400mu->usb_dev);
switch (result) {
case 0:
if (sent_size != tx_msg_size) { /* Too short? drop it */
dev_err(dev, "TX: short write (%d B vs %zu "
"expected)\n", sent_size, tx_msg_size);
result = -EIO;
}
break;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
result = -ESHUTDOWN;
break;
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "TX: maximum errors in URB "
"exceeded; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
} else {
dev_err(dev, "TX: cannot send URB; retrying. "
"tx_msg @%zu %zu B [%d sent]: %d\n",
(void *) tx_msg - i2400m->tx_buf,
tx_msg_size, sent_size, result);
goto retry;
}
}
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
d_fnend(4, dev, "(i2400mu %p) = result\n", i2400mu);
return result;
}
/*
* Get the next TX message in the TX FIFO and send it to the device
*
* Note we exit the loop if i2400mu_tx() fails; that funtion only
* fails on hard error (failing to tx a buffer not being one of them,
* see its doc).
*
* Return: 0
*/
static
int i2400mu_txd(void *_i2400mu)
{
int result = 0;
struct i2400mu *i2400mu = _i2400mu;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct i2400m_msg_hdr *tx_msg;
size_t tx_msg_size;
d_fnstart(4, dev, "(i2400mu %p)\n", i2400mu);
while (1) {
d_printf(2, dev, "TX: waiting for messages\n");
tx_msg = NULL;
wait_event_interruptible(
i2400mu->tx_wq,
(kthread_should_stop() /* check this first! */
|| (tx_msg = i2400m_tx_msg_get(i2400m, &tx_msg_size)))
);
if (kthread_should_stop())
break;
WARN_ON(tx_msg == NULL); /* should not happen...*/
d_printf(2, dev, "TX: submitting %zu bytes\n", tx_msg_size);
d_dump(5, dev, tx_msg, tx_msg_size);
/* Yeah, we ignore errors ... not much we can do */
i2400mu_tx(i2400mu, tx_msg, tx_msg_size);
i2400m_tx_msg_sent(i2400m); /* ack it, advance the FIFO */
if (result < 0)
break;
}
d_fnend(4, dev, "(i2400mu %p) = %d\n", i2400mu, result);
return result;
}
/*
* i2400m TX engine notifies us that there is data in the FIFO ready
* for TX
*
* If there is a URB in flight, don't do anything; when it finishes,
* it will see there is data in the FIFO and send it. Else, just
* submit a write.
*/
void i2400mu_bus_tx_kick(struct i2400m *i2400m)
{
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p) = void\n", i2400m);
wake_up_all(&i2400mu->tx_wq);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
int i2400mu_tx_setup(struct i2400mu *i2400mu)
{
int result = 0;
struct i2400m *i2400m = &i2400mu->i2400m;
struct device *dev = &i2400mu->usb_iface->dev;
struct wimax_dev *wimax_dev = &i2400m->wimax_dev;
i2400mu->tx_kthread = kthread_run(i2400mu_txd, i2400mu, "%s-tx",
wimax_dev->name);
if (IS_ERR(i2400mu->tx_kthread)) {
result = PTR_ERR(i2400mu->tx_kthread);
dev_err(dev, "TX: cannot start thread: %d\n", result);
}
return result;
}
void i2400mu_tx_release(struct i2400mu *i2400mu)
{
kthread_stop(i2400mu->tx_kthread);
}

View file

@ -0,0 +1,591 @@
/*
* Intel Wireless WiMAX Connection 2400m
* Linux driver model glue for USB device, reset & fw upload
*
*
* Copyright (C) 2007-2008 Intel Corporation <linux-wimax@intel.com>
* Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
* Yanir Lubetkin <yanirx.lubetkin@intel.com>
*
* 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
*
* See i2400m-usb.h for a general description of this driver.
*
* This file implements driver model glue, and hook ups for the
* generic driver to implement the bus-specific functions (device
* communication setup/tear down, firmware upload and resetting).
*
* ROADMAP
*
* i2400mu_probe()
* alloc_netdev()...
* i2400mu_netdev_setup()
* i2400mu_init()
* i2400m_netdev_setup()
* i2400m_setup()...
*
* i2400mu_disconnect
* i2400m_release()
* free_netdev()
*
* i2400mu_suspend()
* i2400m_cmd_enter_powersave()
* i2400mu_notification_release()
*
* i2400mu_resume()
* i2400mu_notification_setup()
*
* i2400mu_bus_dev_start() Called by i2400m_dev_start() [who is
* i2400mu_tx_setup() called by i2400m_setup()]
* i2400mu_rx_setup()
* i2400mu_notification_setup()
*
* i2400mu_bus_dev_stop() Called by i2400m_dev_stop() [who is
* i2400mu_notification_release() called by i2400m_release()]
* i2400mu_rx_release()
* i2400mu_tx_release()
*
* i2400mu_bus_reset() Called by i2400m->bus_reset
* __i2400mu_reset()
* __i2400mu_send_barker()
* usb_reset_device()
*/
#include "i2400m-usb.h"
#include <linux/wimax/i2400m.h>
#include <linux/debugfs.h>
#define D_SUBMODULE usb
#include "usb-debug-levels.h"
/* Our firmware file name */
#define I2400MU_FW_FILE_NAME "i2400m-fw-usb-" I2400M_FW_VERSION ".sbcf"
static
int i2400mu_bus_dev_start(struct i2400m *i2400m)
{
int result;
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
result = i2400mu_tx_setup(i2400mu);
if (result < 0)
goto error_usb_tx_setup;
result = i2400mu_rx_setup(i2400mu);
if (result < 0)
goto error_usb_rx_setup;
result = i2400mu_notification_setup(i2400mu);
if (result < 0)
goto error_notif_setup;
d_fnend(3, dev, "(i2400m %p) = %d\n", i2400m, result);
return result;
error_notif_setup:
i2400mu_rx_release(i2400mu);
error_usb_rx_setup:
i2400mu_tx_release(i2400mu);
error_usb_tx_setup:
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
return result;
}
static
void i2400mu_bus_dev_stop(struct i2400m *i2400m)
{
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = &i2400mu->usb_iface->dev;
d_fnstart(3, dev, "(i2400m %p)\n", i2400m);
i2400mu_notification_release(i2400mu);
i2400mu_rx_release(i2400mu);
i2400mu_tx_release(i2400mu);
d_fnend(3, dev, "(i2400m %p) = void\n", i2400m);
}
/*
* Sends a barker buffer to the device
*
* This helper will allocate a kmalloced buffer and use it to transmit
* (then free it). Reason for this is that other arches cannot use
* stack/vmalloc/text areas for DMA transfers.
*
* Error recovery here is simpler: anything is considered a hard error
* and will move the reset code to use a last-resort bus-based reset.
*/
static
int __i2400mu_send_barker(struct i2400mu *i2400mu,
const __le32 *barker,
size_t barker_size,
unsigned endpoint)
{
struct usb_endpoint_descriptor *epd = NULL;
int pipe, actual_len, ret;
struct device *dev = &i2400mu->usb_iface->dev;
void *buffer;
int do_autopm = 1;
ret = usb_autopm_get_interface(i2400mu->usb_iface);
if (ret < 0) {
dev_err(dev, "RESET: can't get autopm: %d\n", ret);
do_autopm = 0;
}
ret = -ENOMEM;
buffer = kmalloc(barker_size, GFP_KERNEL);
if (buffer == NULL)
goto error_kzalloc;
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
memcpy(buffer, barker, barker_size);
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
&actual_len, HZ);
if (ret < 0) {
if (ret != -EINVAL)
dev_err(dev, "E: barker error: %d\n", ret);
} else if (actual_len != barker_size) {
dev_err(dev, "E: only %d bytes transmitted\n", actual_len);
ret = -EIO;
}
kfree(buffer);
error_kzalloc:
if (do_autopm)
usb_autopm_put_interface(i2400mu->usb_iface);
return ret;
}
/*
* Reset a device at different levels (warm, cold or bus)
*
* @i2400m: device descriptor
* @reset_type: soft, warm or bus reset (I2400M_RT_WARM/SOFT/BUS)
*
* Warm and cold resets get a USB reset if they fail.
*
* Warm reset:
*
* The device will be fully reset internally, but won't be
* disconnected from the USB bus (so no reenumeration will
* happen). Firmware upload will be neccessary.
*
* The device will send a reboot barker in the notification endpoint
* that will trigger the driver to reinitialize the state
* automatically from notif.c:i2400m_notification_grok() into
* i2400m_dev_bootstrap_delayed().
*
* Cold and bus (USB) reset:
*
* The device will be fully reset internally, disconnected from the
* USB bus an a reenumeration will happen. Firmware upload will be
* neccessary. Thus, we don't do any locking or struct
* reinitialization, as we are going to be fully disconnected and
* reenumerated.
*
* Note we need to return -ENODEV if a warm reset was requested and we
* had to resort to a bus reset. See i2400m_op_reset(), wimax_reset()
* and wimax_dev->op_reset.
*
* WARNING: no driver state saved/fixed
*/
static
int i2400mu_bus_reset(struct i2400m *i2400m, enum i2400m_reset_type rt)
{
int result;
struct i2400mu *i2400mu =
container_of(i2400m, struct i2400mu, i2400m);
struct device *dev = i2400m_dev(i2400m);
static const __le32 i2400m_WARM_BOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
__constant_cpu_to_le32(I2400M_WARM_RESET_BARKER),
};
static const __le32 i2400m_COLD_BOOT_BARKER[4] = {
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
__constant_cpu_to_le32(I2400M_COLD_RESET_BARKER),
};
d_fnstart(3, dev, "(i2400m %p rt %u)\n", i2400m, rt);
if (rt == I2400M_RT_WARM)
result = __i2400mu_send_barker(i2400mu, i2400m_WARM_BOOT_BARKER,
sizeof(i2400m_WARM_BOOT_BARKER),
I2400MU_EP_BULK_OUT);
else if (rt == I2400M_RT_COLD)
result = __i2400mu_send_barker(i2400mu, i2400m_COLD_BOOT_BARKER,
sizeof(i2400m_COLD_BOOT_BARKER),
I2400MU_EP_RESET_COLD);
else if (rt == I2400M_RT_BUS) {
do_bus_reset:
result = usb_reset_device(i2400mu->usb_dev);
switch (result) {
case 0:
case -EINVAL: /* device is gone */
case -ENODEV:
case -ENOENT:
case -ESHUTDOWN:
result = rt == I2400M_RT_WARM ? -ENODEV : 0;
break; /* We assume the device is disconnected */
default:
dev_err(dev, "USB reset failed (%d), giving up!\n",
result);
}
} else
BUG();
if (result < 0
&& result != -EINVAL /* device is gone */
&& rt != I2400M_RT_BUS) {
dev_err(dev, "%s reset failed (%d); trying USB reset\n",
rt == I2400M_RT_WARM ? "warm" : "cold", result);
rt = I2400M_RT_BUS;
goto do_bus_reset;
}
d_fnend(3, dev, "(i2400m %p rt %u) = %d\n", i2400m, rt, result);
return result;
}
static
void i2400mu_netdev_setup(struct net_device *net_dev)
{
struct i2400m *i2400m = net_dev_to_i2400m(net_dev);
struct i2400mu *i2400mu = container_of(i2400m, struct i2400mu, i2400m);
i2400mu_init(i2400mu);
i2400m_netdev_setup(net_dev);
}
/*
* Debug levels control; see debug.h
*/
struct d_level D_LEVEL[] = {
D_SUBMODULE_DEFINE(usb),
D_SUBMODULE_DEFINE(fw),
D_SUBMODULE_DEFINE(notif),
D_SUBMODULE_DEFINE(rx),
D_SUBMODULE_DEFINE(tx),
};
size_t D_LEVEL_SIZE = ARRAY_SIZE(D_LEVEL);
#define __debugfs_register(prefix, name, parent) \
do { \
result = d_level_register_debugfs(prefix, name, parent); \
if (result < 0) \
goto error; \
} while (0)
static
int i2400mu_debugfs_add(struct i2400mu *i2400mu)
{
int result;
struct device *dev = &i2400mu->usb_iface->dev;
struct dentry *dentry = i2400mu->i2400m.wimax_dev.debugfs_dentry;
struct dentry *fd;
dentry = debugfs_create_dir("i2400m-usb", dentry);
result = PTR_ERR(dentry);
if (IS_ERR(dentry)) {
if (result == -ENODEV)
result = 0; /* No debugfs support */
goto error;
}
i2400mu->debugfs_dentry = dentry;
__debugfs_register("dl_", usb, dentry);
__debugfs_register("dl_", fw, dentry);
__debugfs_register("dl_", notif, dentry);
__debugfs_register("dl_", rx, dentry);
__debugfs_register("dl_", tx, dentry);
/* Don't touch these if you don't know what you are doing */
fd = debugfs_create_u8("rx_size_auto_shrink", 0600, dentry,
&i2400mu->rx_size_auto_shrink);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"rx_size_auto_shrink: %d\n", result);
goto error;
}
fd = debugfs_create_size_t("rx_size", 0600, dentry,
&i2400mu->rx_size);
result = PTR_ERR(fd);
if (IS_ERR(fd) && result != -ENODEV) {
dev_err(dev, "Can't create debugfs entry "
"rx_size: %d\n", result);
goto error;
}
return 0;
error:
debugfs_remove_recursive(i2400mu->debugfs_dentry);
return result;
}
/*
* Probe a i2400m interface and register it
*
* @iface: USB interface to link to
* @id: USB class/subclass/protocol id
* @returns: 0 if ok, < 0 errno code on error.
*
* Alloc a net device, initialize the bus-specific details and then
* calls the bus-generic initialization routine. That will register
* the wimax and netdev devices, upload the firmware [using
* _bus_bm_*()], call _bus_dev_start() to finalize the setup of the
* communication with the device and then will start to talk to it to
* finnish setting it up.
*/
static
int i2400mu_probe(struct usb_interface *iface,
const struct usb_device_id *id)
{
int result;
struct net_device *net_dev;
struct device *dev = &iface->dev;
struct i2400m *i2400m;
struct i2400mu *i2400mu;
struct usb_device *usb_dev = interface_to_usbdev(iface);
if (usb_dev->speed != USB_SPEED_HIGH)
dev_err(dev, "device not connected as high speed\n");
/* Allocate instance [calls i2400m_netdev_setup() on it]. */
result = -ENOMEM;
net_dev = alloc_netdev(sizeof(*i2400mu), "wmx%d",
i2400mu_netdev_setup);
if (net_dev == NULL) {
dev_err(dev, "no memory for network device instance\n");
goto error_alloc_netdev;
}
SET_NETDEV_DEV(net_dev, dev);
i2400m = net_dev_to_i2400m(net_dev);
i2400mu = container_of(i2400m, struct i2400mu, i2400m);
i2400m->wimax_dev.net_dev = net_dev;
i2400mu->usb_dev = usb_get_dev(usb_dev);
i2400mu->usb_iface = iface;
usb_set_intfdata(iface, i2400mu);
i2400m->bus_tx_block_size = I2400MU_BLK_SIZE;
i2400m->bus_pl_size_max = I2400MU_PL_SIZE_MAX;
i2400m->bus_dev_start = i2400mu_bus_dev_start;
i2400m->bus_dev_stop = i2400mu_bus_dev_stop;
i2400m->bus_tx_kick = i2400mu_bus_tx_kick;
i2400m->bus_reset = i2400mu_bus_reset;
i2400m->bus_bm_cmd_send = i2400mu_bus_bm_cmd_send;
i2400m->bus_bm_wait_for_ack = i2400mu_bus_bm_wait_for_ack;
i2400m->bus_fw_name = I2400MU_FW_FILE_NAME;
i2400m->bus_bm_mac_addr_impaired = 0;
iface->needs_remote_wakeup = 1; /* autosuspend (15s delay) */
device_init_wakeup(dev, 1);
usb_autopm_enable(i2400mu->usb_iface);
usb_dev->autosuspend_delay = 15 * HZ;
usb_dev->autosuspend_disabled = 0;
result = i2400m_setup(i2400m, I2400M_BRI_MAC_REINIT);
if (result < 0) {
dev_err(dev, "cannot setup device: %d\n", result);
goto error_setup;
}
result = i2400mu_debugfs_add(i2400mu);
if (result < 0) {
dev_err(dev, "Can't register i2400mu's debugfs: %d\n", result);
goto error_debugfs_add;
}
return 0;
error_debugfs_add:
i2400m_release(i2400m);
error_setup:
usb_set_intfdata(iface, NULL);
usb_put_dev(i2400mu->usb_dev);
free_netdev(net_dev);
error_alloc_netdev:
return result;
}
/*
* Disconect a i2400m from the system.
*
* i2400m_stop() has been called before, so al the rx and tx contexts
* have been taken down already. Make sure the queue is stopped,
* unregister netdev and i2400m, free and kill.
*/
static
void i2400mu_disconnect(struct usb_interface *iface)
{
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct i2400m *i2400m = &i2400mu->i2400m;
struct net_device *net_dev = i2400m->wimax_dev.net_dev;
struct device *dev = &iface->dev;
d_fnstart(3, dev, "(iface %p i2400m %p)\n", iface, i2400m);
debugfs_remove_recursive(i2400mu->debugfs_dentry);
i2400m_release(i2400m);
usb_set_intfdata(iface, NULL);
usb_put_dev(i2400mu->usb_dev);
free_netdev(net_dev);
d_fnend(3, dev, "(iface %p i2400m %p) = void\n", iface, i2400m);
}
/*
* Get the device ready for USB port or system standby and hibernation
*
* USB port and system standby are handled the same.
*
* When the system hibernates, the USB device is powered down and then
* up, so we don't really have to do much here, as it will be seen as
* a reconnect. Still for simplicity we consider this case the same as
* suspend, so that the device has a chance to do notify the base
* station (if connected).
*
* So at the end, the three cases require common handling.
*
* If at the time of this call the device's firmware is not loaded,
* nothing has to be done.
*
* If the firmware is loaded, we need to:
*
* - tell the device to go into host interface power save mode, wait
* for it to ack
*
* This is quite more interesting than it is; we need to execute a
* command, but this time, we don't want the code in usb-{tx,rx}.c
* to call the usb_autopm_get/put_interface() barriers as it'd
* deadlock, so we need to decrement i2400mu->do_autopm, that acts
* as a poor man's semaphore. Ugly, but it works.
*
* As well, the device might refuse going to sleep for whichever
* reason. In this case we just fail. For system suspend/hibernate,
* we *can't* fail. We look at usb_dev->auto_pm to see if the
* suspend call comes from the USB stack or from the system and act
* in consequence.
*
* - stop the notification endpoint polling
*/
static
int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
{
int result = 0;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct usb_device *usb_dev = i2400mu->usb_dev;
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event);
if (i2400m->updown == 0)
goto no_firmware;
d_printf(1, dev, "fw up, requesting standby\n");
atomic_dec(&i2400mu->do_autopm);
result = i2400m_cmd_enter_powersave(i2400m);
atomic_inc(&i2400mu->do_autopm);
if (result < 0 && usb_dev->auto_pm == 0) {
/* System suspend, can't fail */
dev_err(dev, "failed to suspend, will reset on resume\n");
result = 0;
}
if (result < 0)
goto error_enter_powersave;
i2400mu_notification_release(i2400mu);
d_printf(1, dev, "fw up, got standby\n");
error_enter_powersave:
no_firmware:
d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n",
iface, pm_msg.event, result);
return result;
}
static
int i2400mu_resume(struct usb_interface *iface)
{
int ret = 0;
struct device *dev = &iface->dev;
struct i2400mu *i2400mu = usb_get_intfdata(iface);
struct i2400m *i2400m = &i2400mu->i2400m;
d_fnstart(3, dev, "(iface %p)\n", iface);
if (i2400m->updown == 0) {
d_printf(1, dev, "fw was down, no resume neeed\n");
goto out;
}
d_printf(1, dev, "fw was up, resuming\n");
i2400mu_notification_setup(i2400mu);
/* USB has flow control, so we don't need to give it time to
* come back; otherwise, we'd use something like a get-state
* command... */
out:
d_fnend(3, dev, "(iface %p) = %d\n", iface, ret);
return ret;
}
static
struct usb_device_id i2400mu_id_table[] = {
{ USB_DEVICE(0x8086, 0x0181) },
{ USB_DEVICE(0x8086, 0x1403) },
{ USB_DEVICE(0x8086, 0x1405) },
{ USB_DEVICE(0x8086, 0x0180) },
{ USB_DEVICE(0x8086, 0x0182) },
{ USB_DEVICE(0x8086, 0x1406) },
{ USB_DEVICE(0x8086, 0x1403) },
{ },
};
MODULE_DEVICE_TABLE(usb, i2400mu_id_table);
static
struct usb_driver i2400mu_driver = {
.name = KBUILD_MODNAME,
.suspend = i2400mu_suspend,
.resume = i2400mu_resume,
.probe = i2400mu_probe,
.disconnect = i2400mu_disconnect,
.id_table = i2400mu_id_table,
.supports_autosuspend = 1,
};
static
int __init i2400mu_driver_init(void)
{
return usb_register(&i2400mu_driver);
}
module_init(i2400mu_driver_init);
static
void __exit i2400mu_driver_exit(void)
{
flush_scheduled_work(); /* for the stuff we schedule from sysfs.c */
usb_deregister(&i2400mu_driver);
}
module_exit(i2400mu_driver_exit);
MODULE_AUTHOR("Intel Corporation <linux-wimax@intel.com>");
MODULE_DESCRIPTION("Intel 2400M WiMAX networking for USB");
MODULE_LICENSE("GPL");
MODULE_FIRMWARE(I2400MU_FW_FILE_NAME);

View file

@ -150,4 +150,6 @@ source "drivers/usb/atm/Kconfig"
source "drivers/usb/gadget/Kconfig"
source "drivers/usb/otg/Kconfig"
endif # USB_SUPPORT

View file

@ -1275,7 +1275,7 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
struct acm *acm = usb_get_intfdata(intf);
int cnt;
if (acm->dev->auto_pm) {
if (message.event & PM_EVENT_AUTO) {
int b;
spin_lock_irq(&acm->read_lock);

View file

@ -764,7 +764,8 @@ static int wdm_suspend(struct usb_interface *intf, pm_message_t message)
mutex_lock(&desc->plock);
#ifdef CONFIG_PM
if (interface_to_usbdev(desc->intf)->auto_pm && test_bit(WDM_IN_USE, &desc->flags)) {
if ((message.event & PM_EVENT_AUTO) &&
test_bit(WDM_IN_USE, &desc->flags)) {
rv = -EBUSY;
} else {
#endif

View file

@ -21,6 +21,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kref.h>
@ -482,7 +483,6 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
int retval;
int actual;
unsigned long int n_bytes;
int n;
int remaining;
int done;
int this_part;
@ -526,11 +526,8 @@ static ssize_t usbtmc_write(struct file *filp, const char __user *buf,
goto exit;
}
n_bytes = 12 + this_part;
if (this_part % 4)
n_bytes += 4 - this_part % 4;
for (n = 12 + this_part; n < n_bytes; n++)
buffer[n] = 0;
n_bytes = roundup(12 + this_part, 4);
memset(buffer + 12 + this_part, 0, n_bytes - (12 + this_part));
retval = usb_bulk_msg(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,

View file

@ -981,9 +981,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
return -EINVAL;
if (!uurb->buffer)
return -EINVAL;
if (uurb->signr != 0 && (uurb->signr < SIGRTMIN ||
uurb->signr > SIGRTMAX))
return -EINVAL;
if (!(uurb->type == USBDEVFS_URB_TYPE_CONTROL &&
(uurb->endpoint & ~USB_ENDPOINT_DIR_MASK) == 0)) {
ifnum = findintfep(ps->dev, uurb->endpoint);
@ -1320,7 +1317,7 @@ static int get_urb32(struct usbdevfs_urb *kurb,
if (__get_user(uptr, &uurb->buffer))
return -EFAULT;
kurb->buffer = compat_ptr(uptr);
if (__get_user(uptr, &uurb->buffer))
if (__get_user(uptr, &uurb->usercontext))
return -EFAULT;
kurb->usercontext = compat_ptr(uptr);
@ -1401,8 +1398,6 @@ static int proc_disconnectsignal(struct dev_state *ps, void __user *arg)
if (copy_from_user(&ds, arg, sizeof(ds)))
return -EFAULT;
if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX))
return -EINVAL;
ps->discsignr = ds.signr;
ps->disccontext = ds.context;
return 0;

View file

@ -184,6 +184,20 @@ static int usb_unbind_device(struct device *dev)
return 0;
}
/*
* Cancel any pending scheduled resets
*
* [see usb_queue_reset_device()]
*
* Called after unconfiguring / when releasing interfaces. See
* comments in __usb_queue_reset_device() regarding
* udev->reset_running.
*/
static void usb_cancel_queued_reset(struct usb_interface *iface)
{
if (iface->reset_running == 0)
cancel_work_sync(&iface->reset_ws);
}
/* called from driver core with dev locked */
static int usb_probe_interface(struct device *dev)
@ -242,6 +256,7 @@ static int usb_probe_interface(struct device *dev)
mark_quiesced(intf);
intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
usb_cancel_queued_reset(intf);
} else
intf->condition = USB_INTERFACE_BOUND;
@ -272,6 +287,7 @@ static int usb_unbind_interface(struct device *dev)
usb_disable_interface(udev, intf);
driver->disconnect(intf);
usb_cancel_queued_reset(intf);
/* Reset other interface state.
* We cannot do a Set-Interface if the device is suspended or
@ -279,9 +295,12 @@ static int usb_unbind_interface(struct device *dev)
* altsetting means creating new endpoint device entries).
* When either of these happens, defer the Set-Interface.
*/
if (intf->cur_altsetting->desc.bAlternateSetting == 0)
; /* Already in altsetting 0 so skip Set-Interface */
else if (!error && intf->dev.power.status == DPM_ON)
if (intf->cur_altsetting->desc.bAlternateSetting == 0) {
/* Already in altsetting 0 so skip Set-Interface.
* Just re-enable it without affecting the endpoint toggles.
*/
usb_enable_interface(udev, intf, false);
} else if (!error && intf->dev.power.status == DPM_ON)
usb_set_interface(udev, intf->altsetting[0].
desc.bInterfaceNumber, 0);
else
@ -380,8 +399,10 @@ void usb_driver_release_interface(struct usb_driver *driver,
if (device_is_registered(dev)) {
iface->condition = USB_INTERFACE_UNBINDING;
device_release_driver(dev);
} else {
iface->condition = USB_INTERFACE_UNBOUND;
usb_cancel_queued_reset(iface);
}
dev->driver = NULL;
usb_set_intfdata(iface, NULL);
@ -904,7 +925,7 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
}
/* Caller has locked udev's pm_mutex */
static int usb_resume_device(struct usb_device *udev)
static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_device_driver *udriver;
int status = 0;
@ -922,7 +943,7 @@ static int usb_resume_device(struct usb_device *udev)
udev->reset_resume = 1;
udriver = to_usb_device_driver(udev->dev.driver);
status = udriver->resume(udev);
status = udriver->resume(udev, msg);
done:
dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
@ -942,7 +963,8 @@ static int usb_suspend_interface(struct usb_device *udev,
if (udev->state == USB_STATE_NOTATTACHED || !is_active(intf))
goto done;
if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */
/* This can happen; see usb_driver_release_interface() */
if (intf->condition == USB_INTERFACE_UNBOUND)
goto done;
driver = to_usb_driver(intf->dev.driver);
@ -950,7 +972,7 @@ static int usb_suspend_interface(struct usb_device *udev,
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
else if (!udev->auto_pm)
else if (!(msg.event & PM_EVENT_AUTO))
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
@ -968,7 +990,7 @@ static int usb_suspend_interface(struct usb_device *udev,
/* Caller has locked intf's usb_device's pm_mutex */
static int usb_resume_interface(struct usb_device *udev,
struct usb_interface *intf, int reset_resume)
struct usb_interface *intf, pm_message_t msg, int reset_resume)
{
struct usb_driver *driver;
int status = 0;
@ -1092,7 +1114,7 @@ static int autosuspend_check(struct usb_device *udev, int reschedule)
if (reschedule) {
if (!timer_pending(&udev->autosuspend.timer)) {
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
round_jiffies_relative(suspend_time - j));
round_jiffies_up_relative(suspend_time - j));
}
return -EAGAIN;
}
@ -1119,10 +1141,9 @@ static inline int autosuspend_check(struct usb_device *udev, int reschedule)
* all the interfaces which were suspended are resumed so that they remain
* in the same state as the device.
*
* If an autosuspend is in progress (@udev->auto_pm is set), the routine
* checks first to make sure that neither the device itself or any of its
* active interfaces is in use (pm_usage_cnt is greater than 0). If they
* are, the autosuspend fails.
* If an autosuspend is in progress the routine checks first to make sure
* that neither the device itself or any of its active interfaces is in use
* (pm_usage_cnt is greater than 0). If they are, the autosuspend fails.
*
* If the suspend succeeds, the routine recursively queues an autosuspend
* request for @udev's parent device, thereby propagating the change up
@ -1157,7 +1178,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->auto_pm) {
if (msg.event & PM_EVENT_AUTO) {
status = autosuspend_check(udev, 0);
if (status < 0)
goto done;
@ -1177,13 +1198,16 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* If the suspend failed, resume interfaces that did get suspended */
if (status != 0) {
pm_message_t msg2;
msg2.event = msg.event ^ (PM_EVENT_SUSPEND | PM_EVENT_RESUME);
while (--i >= 0) {
intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, 0);
usb_resume_interface(udev, intf, msg2, 0);
}
/* Try another autosuspend when the interfaces aren't busy */
if (udev->auto_pm)
if (msg.event & PM_EVENT_AUTO)
autosuspend_check(udev, status == -EBUSY);
/* If the suspend succeeded then prevent any more URB submissions,
@ -1213,6 +1237,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/**
* usb_resume_both - resume a USB device and its interfaces
* @udev: the usb_device to resume
* @msg: Power Management message describing this state transition
*
* This is the central routine for resuming USB devices. It calls the
* the resume method for @udev and then calls the resume methods for all
@ -1238,7 +1263,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*
* This routine can run only in process context.
*/
static int usb_resume_both(struct usb_device *udev)
static int usb_resume_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i;
@ -1254,14 +1279,15 @@ static int usb_resume_both(struct usb_device *udev)
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
if (udev->auto_pm && udev->autoresume_disabled) {
if ((msg.event & PM_EVENT_AUTO) &&
udev->autoresume_disabled) {
status = -EPERM;
goto done;
}
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
status = usb_resume_device(udev);
status = usb_resume_device(udev, msg);
if (status || udev->state ==
USB_STATE_NOTATTACHED) {
usb_autosuspend_device(parent);
@ -1284,15 +1310,16 @@ static int usb_resume_both(struct usb_device *udev)
/* We can't progagate beyond the USB subsystem,
* so if a root hub's controller is suspended
* then we're stuck. */
status = usb_resume_device(udev);
status = usb_resume_device(udev, msg);
}
} else if (udev->reset_resume)
status = usb_resume_device(udev);
status = usb_resume_device(udev, msg);
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
usb_resume_interface(udev, intf, udev->reset_resume);
usb_resume_interface(udev, intf, msg,
udev->reset_resume);
}
}
@ -1320,13 +1347,13 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
udev->last_busy = jiffies;
if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
if (udev->state == USB_STATE_SUSPENDED)
status = usb_resume_both(udev);
status = usb_resume_both(udev, PMSG_AUTO_RESUME);
if (status != 0)
udev->pm_usage_cnt -= inc_usage_cnt;
else if (inc_usage_cnt)
udev->last_busy = jiffies;
} else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
status = usb_suspend_both(udev, PMSG_SUSPEND);
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
}
usb_pm_unlock(udev);
return status;
@ -1341,6 +1368,19 @@ void usb_autosuspend_work(struct work_struct *work)
usb_autopm_do_device(udev, 0);
}
/* usb_autoresume_work - callback routine to autoresume a USB device */
void usb_autoresume_work(struct work_struct *work)
{
struct usb_device *udev =
container_of(work, struct usb_device, autoresume);
/* Wake it up, let the drivers do their thing, and then put it
* back to sleep.
*/
if (usb_autopm_do_device(udev, 1) == 0)
usb_autopm_do_device(udev, -1);
}
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
@ -1437,13 +1477,14 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
udev->last_busy = jiffies;
if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
if (udev->state == USB_STATE_SUSPENDED)
status = usb_resume_both(udev);
status = usb_resume_both(udev,
PMSG_AUTO_RESUME);
if (status != 0)
intf->pm_usage_cnt -= inc_usage_cnt;
else
udev->last_busy = jiffies;
} else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
status = usb_suspend_both(udev, PMSG_SUSPEND);
status = usb_suspend_both(udev, PMSG_AUTO_SUSPEND);
}
}
usb_pm_unlock(udev);
@ -1491,6 +1532,45 @@ void usb_autopm_put_interface(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
/**
* usb_autopm_put_interface_async - decrement a USB interface's PM-usage counter
* @intf: the usb_interface whose counter should be decremented
*
* This routine does essentially the same thing as
* usb_autopm_put_interface(): it decrements @intf's usage counter and
* queues a delayed autosuspend request if the counter is <= 0. The
* difference is that it does not acquire the device's pm_mutex;
* callers must handle all synchronization issues themselves.
*
* Typically a driver would call this routine during an URB's completion
* handler, if no more URBs were pending.
*
* This routine can run in atomic context.
*/
void usb_autopm_put_interface_async(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
int status = 0;
if (intf->condition == USB_INTERFACE_UNBOUND) {
status = -ENODEV;
} else {
udev->last_busy = jiffies;
--intf->pm_usage_cnt;
if (udev->autosuspend_disabled || udev->autosuspend_delay < 0)
status = -EPERM;
else if (intf->pm_usage_cnt <= 0 &&
!timer_pending(&udev->autosuspend.timer)) {
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
round_jiffies_up_relative(
udev->autosuspend_delay));
}
}
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
__func__, status, intf->pm_usage_cnt);
}
EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
/**
* usb_autopm_get_interface - increment a USB interface's PM-usage counter
* @intf: the usb_interface whose counter should be incremented
@ -1536,6 +1616,37 @@ int usb_autopm_get_interface(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
/**
* usb_autopm_get_interface_async - increment a USB interface's PM-usage counter
* @intf: the usb_interface whose counter should be incremented
*
* This routine does much the same thing as
* usb_autopm_get_interface(): it increments @intf's usage counter and
* queues an autoresume request if the result is > 0. The differences
* are that it does not acquire the device's pm_mutex (callers must
* handle all synchronization issues themselves), and it does not
* autoresume the device directly (it only queues a request). After a
* successful call, the device will generally not yet be resumed.
*
* This routine can run in atomic context.
*/
int usb_autopm_get_interface_async(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
int status = 0;
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else if (udev->autoresume_disabled)
status = -EPERM;
else if (++intf->pm_usage_cnt > 0 && udev->state == USB_STATE_SUSPENDED)
queue_work(ksuspend_usb_wq, &udev->autoresume);
dev_vdbg(&intf->dev, "%s: status %d cnt %d\n",
__func__, status, intf->pm_usage_cnt);
return status;
}
EXPORT_SYMBOL_GPL(usb_autopm_get_interface_async);
/**
* usb_autopm_set_interface - set a USB interface's autosuspend state
* @intf: the usb_interface whose state should be set
@ -1563,6 +1674,9 @@ EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
void usb_autosuspend_work(struct work_struct *work)
{}
void usb_autoresume_work(struct work_struct *work)
{}
#endif /* CONFIG_USB_SUSPEND */
/**
@ -1595,6 +1709,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
/**
* usb_external_resume_device - external resume of a USB device and its interfaces
* @udev: the usb_device to resume
* @msg: Power Management message describing this state transition
*
* This routine handles external resume requests: ones not generated
* internally by a USB driver (autoresume) but rather coming from the user
@ -1603,13 +1718,13 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
*
* The caller must hold @udev's device lock.
*/
int usb_external_resume_device(struct usb_device *udev)
int usb_external_resume_device(struct usb_device *udev, pm_message_t msg)
{
int status;
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_resume_both(udev);
status = usb_resume_both(udev, msg);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
if (status == 0)
@ -1622,7 +1737,7 @@ int usb_external_resume_device(struct usb_device *udev)
return status;
}
int usb_suspend(struct device *dev, pm_message_t message)
int usb_suspend(struct device *dev, pm_message_t msg)
{
struct usb_device *udev;
@ -1641,10 +1756,10 @@ int usb_suspend(struct device *dev, pm_message_t message)
}
udev->skip_sys_resume = 0;
return usb_external_suspend_device(udev, message);
return usb_external_suspend_device(udev, msg);
}
int usb_resume(struct device *dev)
int usb_resume(struct device *dev, pm_message_t msg)
{
struct usb_device *udev;
@ -1656,7 +1771,7 @@ int usb_resume(struct device *dev)
*/
if (udev->skip_sys_resume)
return 0;
return usb_external_resume_device(udev);
return usb_external_resume_device(udev, msg);
}
#endif /* CONFIG_PM */

View file

@ -276,7 +276,7 @@ static void ep_device_release(struct device *dev)
kfree(ep_dev);
}
int usb_create_ep_files(struct device *parent,
int usb_create_ep_devs(struct device *parent,
struct usb_host_endpoint *endpoint,
struct usb_device *udev)
{
@ -340,7 +340,7 @@ exit:
return retval;
}
void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
void usb_remove_ep_devs(struct usb_host_endpoint *endpoint)
{
struct ep_device *ep_dev = endpoint->ep_dev;

View file

@ -200,18 +200,18 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
* interfaces manually by doing a bus (or "global") suspend.
*/
if (!udev->parent)
rc = hcd_bus_suspend(udev);
rc = hcd_bus_suspend(udev, msg);
/* Non-root devices don't need to do anything for FREEZE or PRETHAW */
else if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_PRETHAW)
rc = 0;
else
rc = usb_port_suspend(udev);
rc = usb_port_suspend(udev, msg);
return rc;
}
static int generic_resume(struct usb_device *udev)
static int generic_resume(struct usb_device *udev, pm_message_t msg)
{
int rc;
@ -221,9 +221,9 @@ static int generic_resume(struct usb_device *udev)
* interfaces manually by doing a bus (or "global") resume.
*/
if (!udev->parent)
rc = hcd_bus_resume(udev);
rc = hcd_bus_resume(udev, msg);
else
rc = usb_port_resume(udev);
rc = usb_port_resume(udev, msg);
return rc;
}

View file

@ -128,6 +128,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
pci_set_master(dev);
device_set_wakeup_enable(&dev->dev, 1);
retval = usb_add_hcd(hcd, dev->irq, IRQF_DISABLED | IRQF_SHARED);
if (retval != 0)
@ -191,17 +192,15 @@ EXPORT_SYMBOL_GPL(usb_hcd_pci_remove);
/**
* usb_hcd_pci_suspend - power management suspend of a PCI-based HCD
* @dev: USB Host Controller being suspended
* @message: semantics in flux
* @message: Power Management message describing this state transition
*
* Store this function in the HCD's struct pci_driver as suspend().
* Store this function in the HCD's struct pci_driver as .suspend.
*/
int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
{
struct usb_hcd *hcd;
struct usb_hcd *hcd = pci_get_drvdata(dev);
int retval = 0;
int has_pci_pm;
hcd = pci_get_drvdata(dev);
int wake, w;
/* Root hub suspend should have stopped all downstream traffic,
* and all bus master traffic. And done so for both the interface
@ -212,8 +211,15 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
* otherwise the swsusp will save (and restore) garbage state.
*/
if (!(hcd->state == HC_STATE_SUSPENDED ||
hcd->state == HC_STATE_HALT))
return -EBUSY;
hcd->state == HC_STATE_HALT)) {
dev_warn(&dev->dev, "Root hub is not suspended\n");
retval = -EBUSY;
goto done;
}
/* We might already be suspended (runtime PM -- not yet written) */
if (dev->current_state != PCI_D0)
goto done;
if (hcd->driver->pci_suspend) {
retval = hcd->driver->pci_suspend(hcd, message);
@ -221,49 +227,60 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
if (retval)
goto done;
}
synchronize_irq(dev->irq);
/* FIXME until the generic PM interfaces change a lot more, this
* can't use PCI D1 and D2 states. For example, the confusion
* between messages and states will need to vanish, and messages
* will need to provide a target system state again.
*
* It'll be important to learn characteristics of the target state,
* especially on embedded hardware where the HCD will often be in
* charge of an external VBUS power supply and one or more clocks.
* Some target system states will leave them active; others won't.
* (With PCI, that's often handled by platform BIOS code.)
/* Don't fail on error to enable wakeup. We rely on pci code
* to reject requests the hardware can't implement, rather
* than coding the same thing.
*/
/* even when the PCI layer rejects some of the PCI calls
* below, HCs can try global suspend and reduce DMA traffic.
* PM-sensitive HCDs may already have done this.
*/
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
wake = (hcd->state == HC_STATE_SUSPENDED &&
device_may_wakeup(&dev->dev));
w = pci_wake_from_d3(dev, wake);
if (w < 0)
wake = w;
dev_dbg(&dev->dev, "wakeup: %d\n", wake);
/* Downstream ports from this root hub should already be quiesced, so
* there will be no DMA activity. Now we can shut down the upstream
* link (except maybe for PME# resume signaling) and enter some PCI
* low power state, if the hardware allows.
*/
if (hcd->state == HC_STATE_SUSPENDED) {
pci_disable_device(dev);
done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
/* no DMA or IRQs except when HC is active */
if (dev->current_state == PCI_D0) {
pci_save_state(dev);
pci_disable_device(dev);
}
/**
* usb_hcd_pci_suspend_late - suspend a PCI-based HCD after IRQs are disabled
* @dev: USB Host Controller being suspended
* @message: Power Management message describing this state transition
*
* Store this function in the HCD's struct pci_driver as .suspend_late.
*/
int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t message)
{
int retval = 0;
int has_pci_pm;
if (message.event == PM_EVENT_FREEZE ||
message.event == PM_EVENT_PRETHAW) {
dev_dbg(hcd->self.controller, "--> no state change\n");
goto done;
}
/* We might already be suspended (runtime PM -- not yet written) */
if (dev->current_state != PCI_D0)
goto done;
if (!has_pci_pm) {
dev_dbg(hcd->self.controller, "--> PCI D0/legacy\n");
goto done;
}
pci_save_state(dev);
/* Don't change state if we don't need to */
if (message.event == PM_EVENT_FREEZE ||
message.event == PM_EVENT_PRETHAW) {
dev_dbg(&dev->dev, "--> no state change\n");
goto done;
}
has_pci_pm = pci_find_capability(dev, PCI_CAP_ID_PM);
if (!has_pci_pm) {
dev_dbg(&dev->dev, "--> PCI D0 legacy\n");
} else {
/* NOTE: dev->current_state becomes nonzero only here, and
* only for devices that support PCI PM. Also, exiting
@ -273,35 +290,16 @@ int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t message)
retval = pci_set_power_state(dev, PCI_D3hot);
suspend_report_result(pci_set_power_state, retval);
if (retval == 0) {
int wake = device_can_wakeup(&hcd->self.root_hub->dev);
wake = wake && device_may_wakeup(hcd->self.controller);
dev_dbg(hcd->self.controller, "--> PCI D3%s\n",
wake ? "/wakeup" : "");
/* Ignore these return values. We rely on pci code to
* reject requests the hardware can't implement, rather
* than coding the same thing.
*/
(void) pci_enable_wake(dev, PCI_D3hot, wake);
(void) pci_enable_wake(dev, PCI_D3cold, wake);
dev_dbg(&dev->dev, "--> PCI D3\n");
} else {
dev_dbg(&dev->dev, "PCI D3 suspend fail, %d\n",
retval);
(void) usb_hcd_pci_resume(dev);
pci_restore_state(dev);
}
} else if (hcd->state != HC_STATE_HALT) {
dev_dbg(hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
WARN_ON(1);
retval = -EINVAL;
}
done:
if (retval == 0) {
#ifdef CONFIG_PPC_PMAC
if (retval == 0) {
/* Disable ASIC clocks for USB */
if (machine_is(powermac)) {
struct device_node *of_node;
@ -311,30 +309,24 @@ done:
pmac_call_feature(PMAC_FTR_USB_ENABLE,
of_node, 0, 0);
}
#endif
}
#endif
done:
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend);
EXPORT_SYMBOL_GPL(usb_hcd_pci_suspend_late);
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* usb_hcd_pci_resume_early - resume a PCI-based HCD before IRQs are enabled
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as resume().
* Store this function in the HCD's struct pci_driver as .resume_early.
*/
int usb_hcd_pci_resume(struct pci_dev *dev)
int usb_hcd_pci_resume_early(struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg(hcd->self.controller,
"can't resume, not suspended!\n");
return 0;
}
int retval = 0;
pci_power_t state = dev->current_state;
#ifdef CONFIG_PPC_PMAC
/* Reenable ASIC clocks for USB */
@ -352,7 +344,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
* calls "standby", "suspend to RAM", and so on). There are also
* dirty cases when swsusp fakes a suspend in "shutdown" mode.
*/
if (dev->current_state != PCI_D0) {
if (state != PCI_D0) {
#ifdef DEBUG
int pci_pm;
u16 pmcr;
@ -364,8 +356,7 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
/* Clean case: power to USB and to HC registers was
* maintained; remote wakeup is easy.
*/
dev_dbg(hcd->self.controller, "resume from PCI D%d\n",
pmcr);
dev_dbg(&dev->dev, "resume from PCI D%d\n", pmcr);
} else {
/* Clean: HC lost Vcc power, D0 uninitialized
* + Vaux may have preserved port and transceiver
@ -376,32 +367,55 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
* + after BIOS init
* + after Linux init (HCD statically linked)
*/
dev_dbg(hcd->self.controller,
"PCI D0, from previous PCI D%d\n",
dev->current_state);
dev_dbg(&dev->dev, "resume from previous PCI D%d\n",
state);
}
#endif
/* yes, ignore these results too... */
(void) pci_enable_wake(dev, dev->current_state, 0);
(void) pci_enable_wake(dev, PCI_D3cold, 0);
retval = pci_set_power_state(dev, PCI_D0);
} else {
/* Same basic cases: clean (powered/not), dirty */
dev_dbg(hcd->self.controller, "PCI legacy resume\n");
dev_dbg(&dev->dev, "PCI legacy resume\n");
}
if (retval < 0)
dev_err(&dev->dev, "can't resume: %d\n", retval);
else
pci_restore_state(dev);
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume_early);
/**
* usb_hcd_pci_resume - power management resume of a PCI-based HCD
* @dev: USB Host Controller being resumed
*
* Store this function in the HCD's struct pci_driver as .resume.
*/
int usb_hcd_pci_resume(struct pci_dev *dev)
{
struct usb_hcd *hcd;
int retval;
hcd = pci_get_drvdata(dev);
if (hcd->state != HC_STATE_SUSPENDED) {
dev_dbg(hcd->self.controller,
"can't resume, not suspended!\n");
return 0;
}
/* NOTE: the PCI API itself is asymmetric here. We don't need to
* pci_set_power_state(PCI_D0) since that's part of re-enabling;
* but that won't re-enable bus mastering. Yet pci_disable_device()
* explicitly disables bus mastering...
*/
retval = pci_enable_device(dev);
if (retval < 0) {
dev_err(hcd->self.controller,
"can't re-enable after resume, %d!\n", retval);
dev_err(&dev->dev, "can't re-enable after resume, %d!\n",
retval);
return retval;
}
pci_set_master(dev);
pci_restore_state(dev);
/* yes, ignore this result too... */
(void) pci_wake_from_d3(dev, 0);
clear_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
@ -413,7 +427,6 @@ int usb_hcd_pci_resume(struct pci_dev *dev)
usb_hc_died(hcd);
}
}
return retval;
}
EXPORT_SYMBOL_GPL(usb_hcd_pci_resume);

View file

@ -1010,7 +1010,7 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
spin_lock(&hcd_urb_list_lock);
/* Check that the URB isn't being killed */
if (unlikely(urb->reject)) {
if (unlikely(atomic_read(&urb->reject))) {
rc = -EPERM;
goto done;
}
@ -1340,7 +1340,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
INIT_LIST_HEAD(&urb->urb_list);
atomic_dec(&urb->use_count);
atomic_dec(&urb->dev->urbnum);
if (urb->reject)
if (atomic_read(&urb->reject))
wake_up(&usb_kill_urb_queue);
usb_put_urb(urb);
}
@ -1444,7 +1444,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
urb->status = status;
urb->complete (urb);
atomic_dec (&urb->use_count);
if (unlikely (urb->reject))
if (unlikely(atomic_read(&urb->reject)))
wake_up (&usb_kill_urb_queue);
usb_put_urb (urb);
}
@ -1573,14 +1573,14 @@ int usb_hcd_get_frame_number (struct usb_device *udev)
#ifdef CONFIG_PM
int hcd_bus_suspend(struct usb_device *rhdev)
int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg)
{
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
int status;
int old_state = hcd->state;
dev_dbg(&rhdev->dev, "bus %s%s\n",
rhdev->auto_pm ? "auto-" : "", "suspend");
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "suspend");
if (!hcd->driver->bus_suspend) {
status = -ENOENT;
} else {
@ -1598,14 +1598,14 @@ int hcd_bus_suspend(struct usb_device *rhdev)
return status;
}
int hcd_bus_resume(struct usb_device *rhdev)
int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg)
{
struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self);
int status;
int old_state = hcd->state;
dev_dbg(&rhdev->dev, "usb %s%s\n",
rhdev->auto_pm ? "auto-" : "", "resume");
(msg.event & PM_EVENT_AUTO ? "auto-" : ""), "resume");
if (!hcd->driver->bus_resume)
return -ENOENT;
if (hcd->state == HC_STATE_RUNNING)
@ -1638,7 +1638,7 @@ static void hcd_resume_work(struct work_struct *work)
usb_lock_device(udev);
usb_mark_last_busy(udev);
usb_external_resume_device(udev);
usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
usb_unlock_device(udev);
}
@ -2028,7 +2028,7 @@ EXPORT_SYMBOL_GPL(usb_hcd_platform_shutdown);
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_MON)
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct usb_mon_operations *mon_ops;
@ -2064,4 +2064,4 @@ void usb_mon_deregister (void)
}
EXPORT_SYMBOL_GPL (usb_mon_deregister);
#endif /* CONFIG_USB_MON */
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */

View file

@ -16,6 +16,8 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __USB_CORE_HCD_H
#define __USB_CORE_HCD_H
#ifdef __KERNEL__
@ -254,7 +256,9 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
extern void usb_hcd_pci_remove(struct pci_dev *dev);
#ifdef CONFIG_PM
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t state);
extern int usb_hcd_pci_suspend(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_suspend_late(struct pci_dev *dev, pm_message_t msg);
extern int usb_hcd_pci_resume_early(struct pci_dev *dev);
extern int usb_hcd_pci_resume(struct pci_dev *dev);
#endif /* CONFIG_PM */
@ -386,8 +390,8 @@ extern int usb_find_interface_driver(struct usb_device *dev,
#ifdef CONFIG_PM
extern void usb_hcd_resume_root_hub(struct usb_hcd *hcd);
extern void usb_root_hub_lost_power(struct usb_device *rhdev);
extern int hcd_bus_suspend(struct usb_device *rhdev);
extern int hcd_bus_resume(struct usb_device *rhdev);
extern int hcd_bus_suspend(struct usb_device *rhdev, pm_message_t msg);
extern int hcd_bus_resume(struct usb_device *rhdev, pm_message_t msg);
#else
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
{
@ -419,7 +423,7 @@ static inline void usbfs_cleanup(void) { }
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_MON)
#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
struct usb_mon_operations {
void (*urb_submit)(struct usb_bus *bus, struct urb *urb);
@ -461,7 +465,7 @@ static inline void usbmon_urb_submit_error(struct usb_bus *bus, struct urb *urb,
static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb,
int status) {}
#endif /* CONFIG_USB_MON */
#endif /* CONFIG_USB_MON || CONFIG_USB_MON_MODULE */
/*-------------------------------------------------------------------------*/
@ -490,3 +494,5 @@ extern struct rw_semaphore ehci_cf_port_reset_rwsem;
extern unsigned long usb_hcds_loaded;
#endif /* __KERNEL__ */
#endif /* __USB_CORE_HCD_H */

View file

@ -107,7 +107,9 @@ MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs");
/* define initial 64-byte descriptor request timeout in milliseconds */
static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT;
module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)");
MODULE_PARM_DESC(initial_descriptor_timeout,
"initial 64-byte descriptor request timeout in milliseconds "
"(default 5000 - 5.0 seconds)");
/*
* As of 2.6.10 we introduce a new USB device initialization scheme which
@ -1136,8 +1138,8 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
hdev = interface_to_usbdev(intf);
if (hdev->level == MAX_TOPO_LEVEL) {
dev_err(&intf->dev, "Unsupported bus topology: "
"hub nested too deep\n");
dev_err(&intf->dev,
"Unsupported bus topology: hub nested too deep\n");
return -E2BIG;
}
@ -1374,8 +1376,9 @@ static void usb_stop_pm(struct usb_device *udev)
usb_autosuspend_device(udev->parent);
usb_pm_unlock(udev);
/* Stop any autosuspend requests already submitted */
cancel_rearming_delayed_work(&udev->autosuspend);
/* Stop any autosuspend or autoresume requests already submitted */
cancel_delayed_work_sync(&udev->autosuspend);
cancel_work_sync(&udev->autoresume);
}
#else
@ -1434,17 +1437,12 @@ void usb_disconnect(struct usb_device **pdev)
usb_disable_device(udev, 0);
usb_hcd_synchronize_unlinks(udev);
usb_remove_ep_devs(&udev->ep0);
usb_unlock_device(udev);
/* Remove the device-specific files from sysfs. This must be
* done with udev unlocked, because some of the attribute
* routines try to acquire the device lock.
*/
usb_remove_sysfs_dev_files(udev);
/* Unregister the device. The device driver is responsible
* for removing the device files from usbfs and sysfs and for
* de-configuring the device.
* for de-configuring the device and invoking the remove-device
* notifier chain (used by usbfs and possibly others).
*/
device_del(&udev->dev);
@ -1476,8 +1474,8 @@ static void announce_device(struct usb_device *udev)
dev_info(&udev->dev, "New USB device found, idVendor=%04x, idProduct=%04x\n",
le16_to_cpu(udev->descriptor.idVendor),
le16_to_cpu(udev->descriptor.idProduct));
dev_info(&udev->dev, "New USB device strings: Mfr=%d, Product=%d, "
"SerialNumber=%d\n",
dev_info(&udev->dev,
"New USB device strings: Mfr=%d, Product=%d, SerialNumber=%d\n",
udev->descriptor.iManufacturer,
udev->descriptor.iProduct,
udev->descriptor.iSerialNumber);
@ -1542,7 +1540,7 @@ static int usb_configure_device_otg(struct usb_device *udev)
* customize to match your product.
*/
dev_info(&udev->dev,
"can't set HNP mode; %d\n",
"can't set HNP mode: %d\n",
err);
bus->b_hnp_enable = 0;
}
@ -1635,6 +1633,10 @@ int usb_new_device(struct usb_device *udev)
{
int err;
/* Increment the parent's count of unsuspended children */
if (udev->parent)
usb_autoresume_device(udev->parent);
usb_detect_quirks(udev); /* Determine quirks */
err = usb_configure_device(udev); /* detect & probe dev/intfs */
if (err < 0)
@ -1643,13 +1645,12 @@ int usb_new_device(struct usb_device *udev)
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
/* Increment the parent's count of unsuspended children */
if (udev->parent)
usb_autoresume_device(udev->parent);
/* Tell the world! */
announce_device(udev);
/* Register the device. The device driver is responsible
* for adding the device files to sysfs and for configuring
* the device.
* for configuring the device and invoking the add-device
* notifier chain (used by usbfs and possibly others).
*/
err = device_add(&udev->dev);
if (err) {
@ -1657,15 +1658,12 @@ int usb_new_device(struct usb_device *udev)
goto fail;
}
/* put device-specific files into sysfs */
usb_create_sysfs_dev_files(udev);
/* Tell the world! */
announce_device(udev);
(void) usb_create_ep_devs(&udev->dev, &udev->ep0, udev);
return err;
fail:
usb_set_device_state(udev, USB_STATE_NOTATTACHED);
usb_stop_pm(udev);
return err;
}
@ -1982,7 +1980,7 @@ static int check_port_resume_type(struct usb_device *udev,
*
* Returns 0 on success, else negative errno.
*/
int usb_port_suspend(struct usb_device *udev)
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
@ -2021,7 +2019,7 @@ int usb_port_suspend(struct usb_device *udev)
} else {
/* device has up to 10 msec to fully suspend */
dev_dbg(&udev->dev, "usb %ssuspend\n",
udev->auto_pm ? "auto-" : "");
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
@ -2045,8 +2043,8 @@ static int finish_port_resume(struct usb_device *udev)
u16 devstatus;
/* caller owns the udev device lock */
dev_dbg(&udev->dev, "finish %sresume\n",
udev->reset_resume ? "reset-" : "");
dev_dbg(&udev->dev, "%s\n",
udev->reset_resume ? "finish reset-resume" : "finish resume");
/* usb ch9 identifies four variants of SUSPENDED, based on what
* state the device resumes to. Linux currently won't see the
@ -2098,8 +2096,9 @@ static int finish_port_resume(struct usb_device *udev)
NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (status)
dev_dbg(&udev->dev, "disable remote "
"wakeup, status %d\n", status);
dev_dbg(&udev->dev,
"disable remote wakeup, status %d\n",
status);
}
status = 0;
}
@ -2140,7 +2139,7 @@ static int finish_port_resume(struct usb_device *udev)
*
* Returns 0 on success, else negative errno.
*/
int usb_port_resume(struct usb_device *udev)
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
@ -2165,7 +2164,7 @@ int usb_port_resume(struct usb_device *udev)
} else {
/* drive resume for at least 20 msec */
dev_dbg(&udev->dev, "usb %sresume\n",
udev->auto_pm ? "auto-" : "");
(msg.event & PM_EVENT_AUTO ? "auto-" : ""));
msleep(25);
/* Virtual root hubs can trigger on GET_PORT_STATUS to
@ -2206,7 +2205,7 @@ static int remote_wakeup(struct usb_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);
status = usb_external_resume_device(udev, PMSG_REMOTE_RESUME);
}
return status;
}
@ -2215,14 +2214,14 @@ static int remote_wakeup(struct usb_device *udev)
/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
int usb_port_suspend(struct usb_device *udev)
int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
return 0;
}
/* However we may need to do a reset-resume */
int usb_port_resume(struct usb_device *udev)
int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
struct usb_hub *hub = hdev_to_hub(udev->parent);
int port1 = udev->portnum;
@ -2262,7 +2261,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
udev = hdev->children [port1-1];
if (udev && udev->can_submit) {
if (!hdev->auto_pm)
if (!(msg.event & PM_EVENT_AUTO))
dev_dbg(&intf->dev, "port %d nyet suspended\n",
port1);
return -EBUSY;
@ -2385,7 +2384,7 @@ void usb_ep0_reinit(struct usb_device *udev)
{
usb_disable_endpoint(udev, 0 + USB_DIR_IN);
usb_disable_endpoint(udev, 0 + USB_DIR_OUT);
usb_enable_endpoint(udev, &udev->ep0);
usb_enable_endpoint(udev, &udev->ep0, true);
}
EXPORT_SYMBOL_GPL(usb_ep0_reinit);
@ -2582,9 +2581,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
if (r) {
dev_err(&udev->dev, "device descriptor "
"read/%s, error %d\n",
"64", r);
dev_err(&udev->dev,
"device descriptor read/64, error %d\n",
r);
retval = -EMSGSIZE;
continue;
}
@ -2621,9 +2620,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
retval = usb_get_device_descriptor(udev, 8);
if (retval < 8) {
dev_err(&udev->dev, "device descriptor "
"read/%s, error %d\n",
"8", retval);
dev_err(&udev->dev,
"device descriptor read/8, error %d\n",
retval);
if (retval >= 0)
retval = -EMSGSIZE;
} else {
@ -2650,8 +2649,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
if (retval < (signed)sizeof(udev->descriptor)) {
dev_err(&udev->dev, "device descriptor read/%s, error %d\n",
"all", retval);
dev_err(&udev->dev, "device descriptor read/all, error %d\n",
retval);
if (retval >= 0)
retval = -ENOMSG;
goto fail;
@ -2719,9 +2718,9 @@ hub_power_remaining (struct usb_hub *hub)
else
delta = 8;
if (delta > hub->mA_per_port)
dev_warn(&udev->dev, "%dmA is over %umA budget "
"for port %d!\n",
delta, hub->mA_per_port, port1);
dev_warn(&udev->dev,
"%dmA is over %umA budget for port %d!\n",
delta, hub->mA_per_port, port1);
remaining -= delta;
}
if (remaining < 0) {
@ -3517,3 +3516,46 @@ int usb_reset_device(struct usb_device *udev)
return ret;
}
EXPORT_SYMBOL_GPL(usb_reset_device);
/**
* usb_queue_reset_device - Reset a USB device from an atomic context
* @iface: USB interface belonging to the device to reset
*
* This function can be used to reset a USB device from an atomic
* context, where usb_reset_device() won't work (as it blocks).
*
* Doing a reset via this method is functionally equivalent to calling
* usb_reset_device(), except for the fact that it is delayed to a
* workqueue. This means that any drivers bound to other interfaces
* might be unbound, as well as users from usbfs in user space.
*
* Corner cases:
*
* - Scheduling two resets at the same time from two different drivers
* attached to two different interfaces of the same device is
* possible; depending on how the driver attached to each interface
* handles ->pre_reset(), the second reset might happen or not.
*
* - If a driver is unbound and it had a pending reset, the reset will
* be cancelled.
*
* - This function can be called during .probe() or .disconnect()
* times. On return from .disconnect(), any pending resets will be
* cancelled.
*
* There is no no need to lock/unlock the @reset_ws as schedule_work()
* does its own.
*
* NOTE: We don't do any reference count tracking because it is not
* needed. The lifecycle of the work_struct is tied to the
* usb_interface. Before destroying the interface we cancel the
* work_struct, so the fact that work_struct is queued and or
* running means the interface (and thus, the device) exist and
* are referenced.
*/
void usb_queue_reset_device(struct usb_interface *iface)
{
schedule_work(&iface->reset_ws);
}
EXPORT_SYMBOL_GPL(usb_queue_reset_device);

View file

@ -18,6 +18,8 @@
#include "hcd.h" /* for usbcore internals */
#include "usb.h"
static void cancel_async_set_config(struct usb_device *udev);
struct api_context {
struct completion done;
int status;
@ -139,9 +141,9 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
dr->bRequestType = requesttype;
dr->bRequest = request;
dr->wValue = cpu_to_le16p(&value);
dr->wIndex = cpu_to_le16p(&index);
dr->wLength = cpu_to_le16p(&size);
dr->wValue = cpu_to_le16(value);
dr->wIndex = cpu_to_le16(index);
dr->wLength = cpu_to_le16(size);
/* dbg("usb_control_msg"); */
@ -1004,6 +1006,34 @@ int usb_clear_halt(struct usb_device *dev, int pipe)
}
EXPORT_SYMBOL_GPL(usb_clear_halt);
static int create_intf_ep_devs(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting;
int i;
if (intf->ep_devs_created || intf->unregistering)
return 0;
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
(void) usb_create_ep_devs(&intf->dev, &alt->endpoint[i], udev);
intf->ep_devs_created = 1;
return 0;
}
static void remove_intf_ep_devs(struct usb_interface *intf)
{
struct usb_host_interface *alt = intf->cur_altsetting;
int i;
if (!intf->ep_devs_created)
return;
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
usb_remove_ep_devs(&alt->endpoint[i]);
intf->ep_devs_created = 0;
}
/**
* usb_disable_endpoint -- Disable an endpoint by address
* @dev: the device whose endpoint is being disabled
@ -1092,7 +1122,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
dev_dbg(&dev->dev, "unregistering interface %s\n",
dev_name(&interface->dev));
interface->unregistering = 1;
usb_remove_sysfs_intf_files(interface);
remove_intf_ep_devs(interface);
device_del(&interface->dev);
}
@ -1113,22 +1143,26 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
* usb_enable_endpoint - Enable an endpoint for USB communications
* @dev: the device whose interface is being enabled
* @ep: the endpoint
* @reset_toggle: flag to set the endpoint's toggle back to 0
*
* Resets the endpoint toggle, and sets dev->ep_{in,out} pointers.
* Resets the endpoint toggle if asked, and sets dev->ep_{in,out} pointers.
* For control endpoints, both the input and output sides are handled.
*/
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep,
bool reset_toggle)
{
int epnum = usb_endpoint_num(&ep->desc);
int is_out = usb_endpoint_dir_out(&ep->desc);
int is_control = usb_endpoint_xfer_control(&ep->desc);
if (is_out || is_control) {
usb_settoggle(dev, epnum, 1, 0);
if (reset_toggle)
usb_settoggle(dev, epnum, 1, 0);
dev->ep_out[epnum] = ep;
}
if (!is_out || is_control) {
usb_settoggle(dev, epnum, 0, 0);
if (reset_toggle)
usb_settoggle(dev, epnum, 0, 0);
dev->ep_in[epnum] = ep;
}
ep->enabled = 1;
@ -1138,17 +1172,18 @@ void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep)
* usb_enable_interface - Enable all the endpoints for an interface
* @dev: the device whose interface is being enabled
* @intf: pointer to the interface descriptor
* @reset_toggles: flag to set the endpoints' toggles back to 0
*
* Enables all the endpoints for the interface's current altsetting.
*/
static void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf)
void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf, bool reset_toggles)
{
struct usb_host_interface *alt = intf->cur_altsetting;
int i;
for (i = 0; i < alt->desc.bNumEndpoints; ++i)
usb_enable_endpoint(dev, &alt->endpoint[i]);
usb_enable_endpoint(dev, &alt->endpoint[i], reset_toggles);
}
/**
@ -1235,8 +1270,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
*/
/* prevent submissions using previous endpoint settings */
if (iface->cur_altsetting != alt)
if (iface->cur_altsetting != alt) {
remove_intf_ep_devs(iface);
usb_remove_sysfs_intf_files(iface);
}
usb_disable_interface(dev, iface);
iface->cur_altsetting = alt;
@ -1271,10 +1308,11 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
* during the SETUP stage - hence EP0 toggles are "don't care" here.
* (Likewise, EP0 never "halts" on well designed devices.)
*/
usb_enable_interface(dev, iface);
if (device_is_registered(&iface->dev))
usb_enable_interface(dev, iface, true);
if (device_is_registered(&iface->dev)) {
usb_create_sysfs_intf_files(iface);
create_intf_ep_devs(iface);
}
return 0;
}
EXPORT_SYMBOL_GPL(usb_set_interface);
@ -1334,7 +1372,6 @@ int usb_reset_configuration(struct usb_device *dev)
struct usb_interface *intf = config->interface[i];
struct usb_host_interface *alt;
usb_remove_sysfs_intf_files(intf);
alt = usb_altnum_to_altsetting(intf, 0);
/* No altsetting 0? We'll assume the first altsetting.
@ -1345,10 +1382,16 @@ int usb_reset_configuration(struct usb_device *dev)
if (!alt)
alt = &intf->altsetting[0];
if (alt != intf->cur_altsetting) {
remove_intf_ep_devs(intf);
usb_remove_sysfs_intf_files(intf);
}
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf);
if (device_is_registered(&intf->dev))
usb_enable_interface(dev, intf, true);
if (device_is_registered(&intf->dev)) {
usb_create_sysfs_intf_files(intf);
create_intf_ep_devs(intf);
}
}
return 0;
}
@ -1441,6 +1484,46 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
return retval;
}
/*
* Internal function to queue a device reset
*
* This is initialized into the workstruct in 'struct
* usb_device->reset_ws' that is launched by
* message.c:usb_set_configuration() when initializing each 'struct
* usb_interface'.
*
* It is safe to get the USB device without reference counts because
* the life cycle of @iface is bound to the life cycle of @udev. Then,
* this function will be ran only if @iface is alive (and before
* freeing it any scheduled instances of it will have been cancelled).
*
* We need to set a flag (usb_dev->reset_running) because when we call
* the reset, the interfaces might be unbound. The current interface
* cannot try to remove the queued work as it would cause a deadlock
* (you cannot remove your work from within your executing
* workqueue). This flag lets it know, so that
* usb_cancel_queued_reset() doesn't try to do it.
*
* See usb_queue_reset_device() for more details
*/
void __usb_queue_reset_device(struct work_struct *ws)
{
int rc;
struct usb_interface *iface =
container_of(ws, struct usb_interface, reset_ws);
struct usb_device *udev = interface_to_usbdev(iface);
rc = usb_lock_device_for_reset(udev, iface);
if (rc >= 0) {
iface->reset_running = 1;
usb_reset_device(udev);
iface->reset_running = 0;
usb_unlock_device(udev);
}
}
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
@ -1560,6 +1643,9 @@ free_interfaces:
if (dev->state != USB_STATE_ADDRESS)
usb_disable_device(dev, 1); /* Skip ep0 */
/* Get rid of pending async Set-Config requests for this device */
cancel_async_set_config(dev);
ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
NULL, 0, USB_CTRL_SET_TIMEOUT);
@ -1604,13 +1690,14 @@ free_interfaces:
alt = &intf->altsetting[0];
intf->cur_altsetting = alt;
usb_enable_interface(dev, intf);
usb_enable_interface(dev, intf, true);
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.groups = usb_interface_groups;
intf->dev.dma_mask = dev->dev.dma_mask;
INIT_WORK(&intf->reset_ws, __usb_queue_reset_device);
device_initialize(&intf->dev);
mark_quiesced(intf);
dev_set_name(&intf->dev, "%d-%s:%d.%d",
@ -1641,17 +1728,21 @@ free_interfaces:
dev_name(&intf->dev), ret);
continue;
}
usb_create_sysfs_intf_files(intf);
create_intf_ep_devs(intf);
}
usb_autosuspend_device(dev);
return 0;
}
static LIST_HEAD(set_config_list);
static DEFINE_SPINLOCK(set_config_lock);
struct set_config_request {
struct usb_device *udev;
int config;
struct work_struct work;
struct list_head node;
};
/* Worker routine for usb_driver_set_configuration() */
@ -1659,14 +1750,35 @@ static void driver_set_config_work(struct work_struct *work)
{
struct set_config_request *req =
container_of(work, struct set_config_request, work);
struct usb_device *udev = req->udev;
usb_lock_device(req->udev);
usb_set_configuration(req->udev, req->config);
usb_unlock_device(req->udev);
usb_put_dev(req->udev);
usb_lock_device(udev);
spin_lock(&set_config_lock);
list_del(&req->node);
spin_unlock(&set_config_lock);
if (req->config >= -1) /* Is req still valid? */
usb_set_configuration(udev, req->config);
usb_unlock_device(udev);
usb_put_dev(udev);
kfree(req);
}
/* Cancel pending Set-Config requests for a device whose configuration
* was just changed
*/
static void cancel_async_set_config(struct usb_device *udev)
{
struct set_config_request *req;
spin_lock(&set_config_lock);
list_for_each_entry(req, &set_config_list, node) {
if (req->udev == udev)
req->config = -999; /* Mark as cancelled */
}
spin_unlock(&set_config_lock);
}
/**
* usb_driver_set_configuration - Provide a way for drivers to change device configurations
* @udev: the device whose configuration is being updated
@ -1698,6 +1810,10 @@ int usb_driver_set_configuration(struct usb_device *udev, int config)
req->config = config;
INIT_WORK(&req->work, driver_set_config_work);
spin_lock(&set_config_lock);
list_add(&req->node, &set_config_list);
spin_unlock(&set_config_lock);
usb_get_dev(udev);
schedule_work(&req->work);
return 0;

View file

@ -359,19 +359,19 @@ set_level(struct device *dev, struct device_attribute *attr,
strncmp(buf, on_string, len) == 0) {
udev->autosuspend_disabled = 1;
udev->autoresume_disabled = 0;
rc = usb_external_resume_device(udev);
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
} else if (len == sizeof auto_string - 1 &&
strncmp(buf, auto_string, len) == 0) {
udev->autosuspend_disabled = 0;
udev->autoresume_disabled = 0;
rc = usb_external_resume_device(udev);
rc = usb_external_resume_device(udev, PMSG_USER_RESUME);
} else if (len == sizeof suspend_string - 1 &&
strncmp(buf, suspend_string, len) == 0) {
udev->autosuspend_disabled = 0;
udev->autoresume_disabled = 1;
rc = usb_external_suspend_device(udev, PMSG_SUSPEND);
rc = usb_external_suspend_device(udev, PMSG_USER_SUSPEND);
} else
rc = -EINVAL;
@ -629,9 +629,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
struct device *dev = &udev->dev;
int retval;
/* Unforunately these attributes cannot be created before
* the uevent is broadcast.
*/
retval = device_create_bin_file(dev, &dev_bin_attr_descriptors);
if (retval)
goto error;
@ -643,11 +640,7 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
retval = add_power_attributes(dev);
if (retval)
goto error;
retval = usb_create_ep_files(dev, &udev->ep0, udev);
if (retval)
goto error;
return 0;
return retval;
error:
usb_remove_sysfs_dev_files(udev);
return retval;
@ -657,7 +650,6 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
{
struct device *dev = &udev->dev;
usb_remove_ep_files(&udev->ep0);
remove_power_attributes(dev);
remove_persist_attributes(dev);
device_remove_bin_file(dev, &dev_bin_attr_descriptors);
@ -812,28 +804,6 @@ struct attribute_group *usb_interface_groups[] = {
NULL
};
static inline void usb_create_intf_ep_files(struct usb_interface *intf,
struct usb_device *udev)
{
struct usb_host_interface *iface_desc;
int i;
iface_desc = intf->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
usb_create_ep_files(&intf->dev, &iface_desc->endpoint[i],
udev);
}
static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
{
struct usb_host_interface *iface_desc;
int i;
iface_desc = intf->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i)
usb_remove_ep_files(&iface_desc->endpoint[i]);
}
int usb_create_sysfs_intf_files(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
@ -843,26 +813,19 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf)
if (intf->sysfs_files_created || intf->unregistering)
return 0;
/* The interface string may be present in some altsettings
* and missing in others. Hence its attribute cannot be created
* before the uevent is broadcast.
*/
if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
retval = device_create_file(&intf->dev, &dev_attr_interface);
usb_create_intf_ep_files(intf, udev);
intf->sysfs_files_created = 1;
return 0;
}
void usb_remove_sysfs_intf_files(struct usb_interface *intf)
{
struct device *dev = &intf->dev;
if (!intf->sysfs_files_created)
return;
usb_remove_intf_ep_files(intf);
device_remove_file(dev, &dev_attr_interface);
device_remove_file(&intf->dev, &dev_attr_interface);
intf->sysfs_files_created = 0;
}

View file

@ -10,7 +10,6 @@
#define to_urb(d) container_of(d, struct urb, kref)
static DEFINE_SPINLOCK(usb_reject_lock);
static void urb_destroy(struct kref *kref)
{
@ -131,9 +130,7 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
urb->anchor = anchor;
if (unlikely(anchor->poisoned)) {
spin_lock(&usb_reject_lock);
urb->reject++;
spin_unlock(&usb_reject_lock);
atomic_inc(&urb->reject);
}
spin_unlock_irqrestore(&anchor->lock, flags);
@ -565,16 +562,12 @@ void usb_kill_urb(struct urb *urb)
might_sleep();
if (!(urb && urb->dev && urb->ep))
return;
spin_lock_irq(&usb_reject_lock);
++urb->reject;
spin_unlock_irq(&usb_reject_lock);
atomic_inc(&urb->reject);
usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
spin_lock_irq(&usb_reject_lock);
--urb->reject;
spin_unlock_irq(&usb_reject_lock);
atomic_dec(&urb->reject);
}
EXPORT_SYMBOL_GPL(usb_kill_urb);
@ -606,9 +599,7 @@ void usb_poison_urb(struct urb *urb)
might_sleep();
if (!(urb && urb->dev && urb->ep))
return;
spin_lock_irq(&usb_reject_lock);
++urb->reject;
spin_unlock_irq(&usb_reject_lock);
atomic_inc(&urb->reject);
usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
@ -617,14 +608,10 @@ EXPORT_SYMBOL_GPL(usb_poison_urb);
void usb_unpoison_urb(struct urb *urb)
{
unsigned long flags;
if (!urb)
return;
spin_lock_irqsave(&usb_reject_lock, flags);
--urb->reject;
spin_unlock_irqrestore(&usb_reject_lock, flags);
atomic_dec(&urb->reject);
}
EXPORT_SYMBOL_GPL(usb_unpoison_urb);
@ -691,6 +678,26 @@ void usb_poison_anchored_urbs(struct usb_anchor *anchor)
}
EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs);
/**
* usb_unpoison_anchored_urbs - let an anchor be used successfully again
* @anchor: anchor the requests are bound to
*
* Reverses the effect of usb_poison_anchored_urbs
* the anchor can be used normally after it returns
*/
void usb_unpoison_anchored_urbs(struct usb_anchor *anchor)
{
unsigned long flags;
struct urb *lazarus;
spin_lock_irqsave(&anchor->lock, flags);
list_for_each_entry(lazarus, &anchor->urb_list, anchor_list) {
usb_unpoison_urb(lazarus);
}
anchor->poisoned = 0;
spin_unlock_irqrestore(&anchor->lock, flags);
}
EXPORT_SYMBOL_GPL(usb_unpoison_anchored_urbs);
/**
* usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse
* @anchor: anchor the requests are bound to

View file

@ -253,7 +253,7 @@ static int usb_dev_prepare(struct device *dev)
static void usb_dev_complete(struct device *dev)
{
/* Currently used only for rebinding interfaces */
usb_resume(dev); /* Implement eventually? */
usb_resume(dev, PMSG_RESUME); /* Message event is meaningless */
}
static int usb_dev_suspend(struct device *dev)
@ -263,7 +263,7 @@ static int usb_dev_suspend(struct device *dev)
static int usb_dev_resume(struct device *dev)
{
return usb_resume(dev);
return usb_resume(dev, PMSG_RESUME);
}
static int usb_dev_freeze(struct device *dev)
@ -273,7 +273,7 @@ static int usb_dev_freeze(struct device *dev)
static int usb_dev_thaw(struct device *dev)
{
return usb_resume(dev);
return usb_resume(dev, PMSG_THAW);
}
static int usb_dev_poweroff(struct device *dev)
@ -283,7 +283,7 @@ static int usb_dev_poweroff(struct device *dev)
static int usb_dev_restore(struct device *dev)
{
return usb_resume(dev);
return usb_resume(dev, PMSG_RESTORE);
}
static struct dev_pm_ops usb_device_pm_ops = {
@ -362,7 +362,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
/* ep0 maxpacket comes later, from device descriptor */
usb_enable_endpoint(dev, &dev->ep0);
usb_enable_endpoint(dev, &dev->ep0, true);
dev->can_submit = 1;
/* Save readable and stable topology id, distinguishing devices
@ -402,6 +402,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
#ifdef CONFIG_PM
mutex_init(&dev->pm_mutex);
INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
INIT_WORK(&dev->autoresume, usb_autoresume_work);
dev->autosuspend_delay = usb_autosuspend_delay * HZ;
dev->connect_time = jiffies;
dev->active_duration = -jiffies;
@ -513,10 +514,7 @@ EXPORT_SYMBOL_GPL(usb_put_intf);
* disconnect; in some drivers (such as usb-storage) the disconnect()
* or suspend() method will block waiting for a device reset to complete.
*
* Returns a negative error code for failure, otherwise 1 or 0 to indicate
* that the device will or will not have to be unlocked. (0 can be
* returned when an interface is given and is BINDING, because in that
* case the driver already owns the device lock.)
* Returns a negative error code for failure, otherwise 0.
*/
int usb_lock_device_for_reset(struct usb_device *udev,
const struct usb_interface *iface)
@ -527,16 +525,9 @@ int usb_lock_device_for_reset(struct usb_device *udev,
return -ENODEV;
if (udev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
if (iface) {
switch (iface->condition) {
case USB_INTERFACE_BINDING:
return 0;
case USB_INTERFACE_BOUND:
break;
default:
return -EINTR;
}
}
if (iface && (iface->condition == USB_INTERFACE_UNBINDING ||
iface->condition == USB_INTERFACE_UNBOUND))
return -EINTR;
while (usb_trylock_device(udev) != 0) {
@ -550,10 +541,11 @@ int usb_lock_device_for_reset(struct usb_device *udev,
return -ENODEV;
if (udev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
if (iface && iface->condition != USB_INTERFACE_BOUND)
if (iface && (iface->condition == USB_INTERFACE_UNBINDING ||
iface->condition == USB_INTERFACE_UNBOUND))
return -EINTR;
}
return 1;
return 0;
}
EXPORT_SYMBOL_GPL(usb_lock_device_for_reset);
@ -962,8 +954,12 @@ void usb_buffer_unmap_sg(const struct usb_device *dev, int is_in,
}
EXPORT_SYMBOL_GPL(usb_buffer_unmap_sg);
/* format to disable USB on kernel command line is: nousb */
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
/* To disable USB, kernel command line is 'nousb' not 'usbcore.nousb' */
#ifdef MODULE
module_param(nousb, bool, 0444);
#else
core_param(nousb, nousb, bool, 0444);
#endif
/*
* for external read access to <nousb>
@ -974,6 +970,37 @@ int usb_disabled(void)
}
EXPORT_SYMBOL_GPL(usb_disabled);
/*
* Notifications of device and interface registration
*/
static int usb_bus_notify(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
switch (action) {
case BUS_NOTIFY_ADD_DEVICE:
if (dev->type == &usb_device_type)
(void) usb_create_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
(void) usb_create_sysfs_intf_files(
to_usb_interface(dev));
break;
case BUS_NOTIFY_DEL_DEVICE:
if (dev->type == &usb_device_type)
usb_remove_sysfs_dev_files(to_usb_device(dev));
else if (dev->type == &usb_if_device_type)
usb_remove_sysfs_intf_files(to_usb_interface(dev));
break;
}
return 0;
}
static struct notifier_block usb_bus_nb = {
.notifier_call = usb_bus_notify,
};
/*
* Init
*/
@ -991,6 +1018,9 @@ static int __init usb_init(void)
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
retval = bus_register_notifier(&usb_bus_type, &usb_bus_nb);
if (retval)
goto bus_notifier_failed;
retval = usb_host_init();
if (retval)
goto host_init_failed;
@ -1025,6 +1055,8 @@ driver_register_failed:
major_init_failed:
usb_host_cleanup();
host_init_failed:
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_notifier_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
ksuspend_usb_cleanup();
@ -1048,6 +1080,7 @@ static void __exit usb_exit(void)
usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
bus_unregister(&usb_bus_type);
ksuspend_usb_cleanup();
}

View file

@ -1,16 +1,20 @@
#include <linux/pm.h>
/* Functions local to drivers/usb/core/ */
extern int usb_create_sysfs_dev_files(struct usb_device *dev);
extern void usb_remove_sysfs_dev_files(struct usb_device *dev);
extern int usb_create_sysfs_intf_files(struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files(struct usb_interface *intf);
extern int usb_create_ep_files(struct device *parent,
extern int usb_create_ep_devs(struct device *parent,
struct usb_host_endpoint *endpoint,
struct usb_device *udev);
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
extern void usb_remove_ep_devs(struct usb_host_endpoint *endpoint);
extern void usb_enable_endpoint(struct usb_device *dev,
struct usb_host_endpoint *ep);
struct usb_host_endpoint *ep, bool reset_toggle);
extern void usb_enable_interface(struct usb_device *dev,
struct usb_interface *intf, bool reset_toggles);
extern void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr);
extern void usb_disable_interface(struct usb_device *dev,
struct usb_interface *intf);
@ -42,14 +46,16 @@ extern void usb_host_cleanup(void);
#ifdef CONFIG_PM
extern int usb_suspend(struct device *dev, pm_message_t msg);
extern int usb_resume(struct device *dev);
extern int usb_resume(struct device *dev, pm_message_t msg);
extern void usb_autosuspend_work(struct work_struct *work);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
extern void usb_autoresume_work(struct work_struct *work);
extern int usb_port_suspend(struct usb_device *dev, pm_message_t msg);
extern int usb_port_resume(struct usb_device *dev, pm_message_t msg);
extern int usb_external_suspend_device(struct usb_device *udev,
pm_message_t msg);
extern int usb_external_resume_device(struct usb_device *udev);
extern int usb_external_resume_device(struct usb_device *udev,
pm_message_t msg);
static inline void usb_pm_lock(struct usb_device *udev)
{
@ -63,12 +69,12 @@ static inline void usb_pm_unlock(struct usb_device *udev)
#else
static inline int usb_port_suspend(struct usb_device *udev)
static inline int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
{
return 0;
}
static inline int usb_port_resume(struct usb_device *udev)
static inline int usb_port_resume(struct usb_device *udev, pm_message_t msg)
{
return 0;
}

View file

@ -297,13 +297,34 @@ config USB_S3C2410_DEBUG
# musb builds in ../musb along with host support
config USB_GADGET_MUSB_HDRC
boolean "Inventra HDRC USB Peripheral (TI, ...)"
boolean "Inventra HDRC USB Peripheral (TI, ADI, ...)"
depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
select USB_GADGET_DUALSPEED
select USB_GADGET_SELECTED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010.
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
config USB_GADGET_IMX
boolean "Freescale IMX USB Peripheral Controller"
depends on ARCH_MX1
help
Freescale's IMX series include an integrated full speed
USB 1.1 device controller. The controller in the IMX series
is register-compatible.
It has Six fixed-function endpoints, as well as endpoint
zero (for control transfers).
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "imx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_IMX
tristate
depends on USB_GADGET_IMX
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_M66592
boolean "Renesas M66592 USB Peripheral Controller"
@ -377,6 +398,24 @@ config USB_FSL_QE
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_CI13XXX
boolean "MIPS USB CI13xxx"
depends on PCI
select USB_GADGET_DUALSPEED
help
MIPS USB IP core family device controller
Currently it only supports IP part number CI13412
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "ci13xxx_udc" and force all
gadget drivers to also be dynamically linked.
config USB_CI13XXX
tristate
depends on USB_GADGET_CI13XXX
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_NET2280
boolean "NetChip 228x"
depends on PCI

View file

@ -10,6 +10,7 @@ obj-$(CONFIG_USB_NET2280) += net2280.o
obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_IMX) += imx_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
@ -19,6 +20,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
obj-$(CONFIG_USB_M66592) += m66592-udc.o
obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
#
# USB gadget drivers

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,195 @@
/*
* ci13xxx_udc.h - structures, registers, and macros MIPS USB IP core
*
* Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
*
* Author: David Lopo
*
* 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.
*
* Description: MIPS USB IP core family device controller
* Structures, registers and logging macros
*/
#ifndef _CI13XXX_h_
#define _CI13XXX_h_
/******************************************************************************
* DEFINE
*****************************************************************************/
#define ENDPT_MAX (16)
#define CTRL_PAYLOAD_MAX (64)
#define RX (0) /* similar to USB_DIR_OUT but can be used as an index */
#define TX (1) /* similar to USB_DIR_IN but can be used as an index */
/******************************************************************************
* STRUCTURES
*****************************************************************************/
/* DMA layout of transfer descriptors */
struct ci13xxx_td {
/* 0 */
u32 next;
#define TD_TERMINATE BIT(0)
/* 1 */
u32 token;
#define TD_STATUS (0x00FFUL << 0)
#define TD_STATUS_TR_ERR BIT(3)
#define TD_STATUS_DT_ERR BIT(5)
#define TD_STATUS_HALTED BIT(6)
#define TD_STATUS_ACTIVE BIT(7)
#define TD_MULTO (0x0003UL << 10)
#define TD_IOC BIT(15)
#define TD_TOTAL_BYTES (0x7FFFUL << 16)
/* 2 */
u32 page[5];
#define TD_CURR_OFFSET (0x0FFFUL << 0)
#define TD_FRAME_NUM (0x07FFUL << 0)
#define TD_RESERVED_MASK (0x0FFFUL << 0)
} __attribute__ ((packed));
/* DMA layout of queue heads */
struct ci13xxx_qh {
/* 0 */
u32 cap;
#define QH_IOS BIT(15)
#define QH_MAX_PKT (0x07FFUL << 16)
#define QH_ZLT BIT(29)
#define QH_MULT (0x0003UL << 30)
/* 1 */
u32 curr;
/* 2 - 8 */
struct ci13xxx_td td;
/* 9 */
u32 RESERVED;
struct usb_ctrlrequest setup;
} __attribute__ ((packed));
/* Extension of usb_request */
struct ci13xxx_req {
struct usb_request req;
unsigned map;
struct list_head queue;
struct ci13xxx_td *ptr;
dma_addr_t dma;
};
/* Extension of usb_ep */
struct ci13xxx_ep {
struct usb_ep ep;
const struct usb_endpoint_descriptor *desc;
u8 dir;
u8 num;
u8 type;
char name[16];
struct {
struct list_head queue;
struct ci13xxx_qh *ptr;
dma_addr_t dma;
} qh[2];
struct usb_request *status;
int wedge;
/* global resources */
spinlock_t *lock;
struct device *device;
struct dma_pool *td_pool;
};
/* CI13XXX UDC descriptor & global resources */
struct ci13xxx {
spinlock_t *lock; /* ctrl register bank access */
struct dma_pool *qh_pool; /* DMA pool for queue heads */
struct dma_pool *td_pool; /* DMA pool for transfer descs */
struct usb_gadget gadget; /* USB slave device */
struct ci13xxx_ep ci13xxx_ep[ENDPT_MAX]; /* extended endpts */
struct usb_gadget_driver *driver; /* 3rd party gadget driver */
};
/******************************************************************************
* REGISTERS
*****************************************************************************/
/* register size */
#define REG_BITS (32)
/* HCCPARAMS */
#define HCCPARAMS_LEN BIT(17)
/* DCCPARAMS */
#define DCCPARAMS_DEN (0x1F << 0)
#define DCCPARAMS_DC BIT(7)
/* TESTMODE */
#define TESTMODE_FORCE BIT(0)
/* USBCMD */
#define USBCMD_RS BIT(0)
#define USBCMD_RST BIT(1)
#define USBCMD_SUTW BIT(13)
/* USBSTS & USBINTR */
#define USBi_UI BIT(0)
#define USBi_UEI BIT(1)
#define USBi_PCI BIT(2)
#define USBi_URI BIT(6)
#define USBi_SLI BIT(8)
/* DEVICEADDR */
#define DEVICEADDR_USBADRA BIT(24)
#define DEVICEADDR_USBADR (0x7FUL << 25)
/* PORTSC */
#define PORTSC_SUSP BIT(7)
#define PORTSC_HSP BIT(9)
#define PORTSC_PTC (0x0FUL << 16)
/* DEVLC */
#define DEVLC_PSPD (0x03UL << 25)
#define DEVLC_PSPD_HS (0x02UL << 25)
/* USBMODE */
#define USBMODE_CM (0x03UL << 0)
#define USBMODE_CM_IDLE (0x00UL << 0)
#define USBMODE_CM_DEVICE (0x02UL << 0)
#define USBMODE_CM_HOST (0x03UL << 0)
#define USBMODE_SLOM BIT(3)
/* ENDPTCTRL */
#define ENDPTCTRL_RXS BIT(0)
#define ENDPTCTRL_RXT (0x03UL << 2)
#define ENDPTCTRL_RXR BIT(6) /* reserved for port 0 */
#define ENDPTCTRL_RXE BIT(7)
#define ENDPTCTRL_TXS BIT(16)
#define ENDPTCTRL_TXT (0x03UL << 18)
#define ENDPTCTRL_TXR BIT(22) /* reserved for port 0 */
#define ENDPTCTRL_TXE BIT(23)
/******************************************************************************
* LOGGING
*****************************************************************************/
#define ci13xxx_printk(level, format, args...) \
do { \
if (_udc == NULL) \
printk(level "[%s] " format "\n", __func__, ## args); \
else \
dev_printk(level, _udc->gadget.dev.parent, \
"[%s] " format "\n", __func__, ## args); \
} while (0)
#define err(format, args...) ci13xxx_printk(KERN_ERR, format, ## args)
#define warn(format, args...) ci13xxx_printk(KERN_WARNING, format, ## args)
#define info(format, args...) ci13xxx_printk(KERN_INFO, format, ## args)
#ifdef TRACE
#define trace(format, args...) ci13xxx_printk(KERN_DEBUG, format, ## args)
#define dbg_trace(format, args...) dev_dbg(dev, format, ##args)
#else
#define trace(format, args...) do {} while (0)
#define dbg_trace(format, args...) do {} while (0)
#endif
#endif /* _CI13XXX_h_ */

View file

@ -161,7 +161,7 @@ ep_matches (
/* report address */
desc->bEndpointAddress &= USB_DIR_IN;
if (isdigit (ep->name [2])) {
u8 num = simple_strtol (&ep->name [2], NULL, 10);
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {

View file

@ -1,7 +1,7 @@
/*
* file_storage.c -- File-backed USB Storage Gadget, for USB development
*
* Copyright (C) 2003-2007 Alan Stern
* Copyright (C) 2003-2008 Alan Stern
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -38,16 +38,17 @@
/*
* The File-backed Storage Gadget acts as a USB Mass Storage device,
* appearing to the host as a disk drive. In addition to providing an
* example of a genuinely useful gadget driver for a USB device, it also
* illustrates a technique of double-buffering for increased throughput.
* Last but not least, it gives an easy way to probe the behavior of the
* Mass Storage drivers in a USB host.
* appearing to the host as a disk drive or as a CD-ROM drive. In addition
* to providing an example of a genuinely useful gadget driver for a USB
* device, it also illustrates a technique of double-buffering for increased
* throughput. Last but not least, it gives an easy way to probe the
* behavior of the Mass Storage drivers in a USB host.
*
* Backing storage is provided by a regular file or a block device, specified
* by the "file" module parameter. Access can be limited to read-only by
* setting the optional "ro" module parameter. The gadget will indicate that
* it has removable media if the optional "removable" module parameter is set.
* setting the optional "ro" module parameter. (For CD-ROM emulation,
* access is always read-only.) The gadget will indicate that it has
* removable media if the optional "removable" module parameter is set.
*
* The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI),
* and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected
@ -64,7 +65,12 @@
* The default number of LUNs is taken from the number of "file" elements;
* it is 1 if "file" is not given. If "removable" is not set then a backing
* file must be specified for each LUN. If it is set, then an unspecified
* or empty backing filename means the LUN's medium is not loaded.
* or empty backing filename means the LUN's medium is not loaded. Ideally
* each LUN would be settable independently as a disk drive or a CD-ROM
* drive, but currently all LUNs have to be the same type. The CD-ROM
* emulation includes a single data track and no audio tracks; hence there
* need be only one backing file per LUN. Note also that the CD-ROM block
* length is set to 512 rather than the more common value 2048.
*
* Requirements are modest; only a bulk-in and a bulk-out endpoint are
* needed (an interrupt-out endpoint is also needed for CBI). The memory
@ -91,6 +97,8 @@
* USB device controller (usually true),
* boolean to permit the driver to halt
* bulk endpoints
* cdrom Default false, boolean for whether to emulate
* a CD-ROM drive
* transport=XXX Default BBB, transport name (CB, CBI, or BBB)
* protocol=YYY Default SCSI, protocol name (RBC, 8020 or
* ATAPI, QIC, UFI, 8070, or SCSI;
@ -103,15 +111,16 @@
* PAGE_CACHE_SIZE)
*
* If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file", "ro",
* "removable", "luns", and "stall" options are available; default values
* are used for everything else.
* "removable", "luns", "stall", and "cdrom" options are available; default
* values are used for everything else.
*
* The pathnames of the backing files and the ro settings are available in
* the attribute files "file" and "ro" in the lun<n> subdirectory of the
* gadget's sysfs directory. If the "removable" option is set, writing to
* these files will simulate ejecting/loading the medium (writing an empty
* line means eject) and adjusting a write-enable tab. Changes to the ro
* setting are not allowed when the medium is loaded.
* setting are not allowed when the medium is loaded or if CD-ROM emulation
* is being used.
*
* This gadget driver is heavily based on "Gadget Zero" by David Brownell.
* The driver's SCSI command interface was based on the "Information
@ -261,7 +270,7 @@
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
#define DRIVER_VERSION "7 August 2007"
#define DRIVER_VERSION "20 November 2008"
static const char longname[] = DRIVER_DESC;
static const char shortname[] = DRIVER_NAME;
@ -341,6 +350,7 @@ static struct {
int removable;
int can_stall;
int cdrom;
char *transport_parm;
char *protocol_parm;
@ -359,6 +369,7 @@ static struct {
.protocol_parm = "SCSI",
.removable = 0,
.can_stall = 1,
.cdrom = 0,
.vendor = DRIVER_VENDOR_ID,
.product = DRIVER_PRODUCT_ID,
.release = 0xffff, // Use controller chip type
@ -382,6 +393,9 @@ MODULE_PARM_DESC(removable, "true to simulate removable media");
module_param_named(stall, mod_data.can_stall, bool, S_IRUGO);
MODULE_PARM_DESC(stall, "false to prevent bulk stalls");
module_param_named(cdrom, mod_data.cdrom, bool, S_IRUGO);
MODULE_PARM_DESC(cdrom, "true to emulate cdrom instead of disk");
/* In the non-TEST version, only the module parameters listed above
* are available. */
@ -411,6 +425,10 @@ MODULE_PARM_DESC(buflen, "I/O buffer size");
/*-------------------------------------------------------------------------*/
/* SCSI device types */
#define TYPE_DISK 0x00
#define TYPE_CDROM 0x05
/* USB protocol value = the transport method */
#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt
#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt
@ -487,6 +505,8 @@ struct interrupt_data {
#define SC_READ_12 0xa8
#define SC_READ_CAPACITY 0x25
#define SC_READ_FORMAT_CAPACITIES 0x23
#define SC_READ_HEADER 0x44
#define SC_READ_TOC 0x43
#define SC_RELEASE 0x17
#define SC_REQUEST_SENSE 0x03
#define SC_RESERVE 0x16
@ -2006,23 +2026,28 @@ static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh)
u8 *buf = (u8 *) bh->buf;
static char vendor_id[] = "Linux ";
static char product_id[] = "File-Stor Gadget";
static char product_disk_id[] = "File-Stor Gadget";
static char product_cdrom_id[] = "File-CD Gadget ";
if (!fsg->curlun) { // Unsupported LUNs are okay
fsg->bad_lun_okay = 1;
memset(buf, 0, 36);
buf[0] = 0x7f; // Unsupported, no device-type
buf[4] = 31; // Additional length
return 36;
}
memset(buf, 0, 8); // Non-removable, direct-access device
memset(buf, 0, 8);
buf[0] = (mod_data.cdrom ? TYPE_CDROM : TYPE_DISK);
if (mod_data.removable)
buf[1] = 0x80;
buf[2] = 2; // ANSI SCSI level 2
buf[3] = 2; // SCSI-2 INQUIRY data format
buf[4] = 31; // Additional length
// No special options
sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id,
sprintf(buf + 8, "%-8s%-16s%04x", vendor_id,
(mod_data.cdrom ? product_cdrom_id :
product_disk_id),
mod_data.release);
return 36;
}
@ -2101,6 +2126,75 @@ static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh)
}
static void store_cdrom_address(u8 *dest, int msf, u32 addr)
{
if (msf) {
/* Convert to Minutes-Seconds-Frames */
addr >>= 2; /* Convert to 2048-byte frames */
addr += 2*75; /* Lead-in occupies 2 seconds */
dest[3] = addr % 75; /* Frames */
addr /= 75;
dest[2] = addr % 60; /* Seconds */
addr /= 60;
dest[1] = addr; /* Minutes */
dest[0] = 0; /* Reserved */
} else {
/* Absolute sector */
put_be32(dest, addr);
}
}
static int do_read_header(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
int msf = fsg->cmnd[1] & 0x02;
u32 lba = get_be32(&fsg->cmnd[2]);
u8 *buf = (u8 *) bh->buf;
if ((fsg->cmnd[1] & ~0x02) != 0) { /* Mask away MSF */
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
if (lba >= curlun->num_sectors) {
curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
return -EINVAL;
}
memset(buf, 0, 8);
buf[0] = 0x01; /* 2048 bytes of user data, rest is EC */
store_cdrom_address(&buf[4], msf, lba);
return 8;
}
static int do_read_toc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
int msf = fsg->cmnd[1] & 0x02;
int start_track = fsg->cmnd[6];
u8 *buf = (u8 *) bh->buf;
if ((fsg->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */
start_track > 1) {
curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
return -EINVAL;
}
memset(buf, 0, 20);
buf[1] = (20-2); /* TOC data length */
buf[2] = 1; /* First track number */
buf[3] = 1; /* Last track number */
buf[5] = 0x16; /* Data track, copying allowed */
buf[6] = 0x01; /* Only track is number 1 */
store_cdrom_address(&buf[8], msf, 0);
buf[13] = 0x16; /* Lead-out track is data */
buf[14] = 0xAA; /* Lead-out track number */
store_cdrom_address(&buf[16], msf, curlun->num_sectors);
return 20;
}
static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
{
struct lun *curlun = fsg->curlun;
@ -2848,6 +2942,26 @@ static int do_scsi_command(struct fsg_dev *fsg)
reply = do_read_capacity(fsg, bh);
break;
case SC_READ_HEADER:
if (!mod_data.cdrom)
goto unknown_cmnd;
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
(3<<7) | (0x1f<<1), 1,
"READ HEADER")) == 0)
reply = do_read_header(fsg, bh);
break;
case SC_READ_TOC:
if (!mod_data.cdrom)
goto unknown_cmnd;
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
(7<<6) | (1<<1), 1,
"READ TOC")) == 0)
reply = do_read_toc(fsg, bh);
break;
case SC_READ_FORMAT_CAPACITIES:
fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]);
if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST,
@ -2933,6 +3047,7 @@ static int do_scsi_command(struct fsg_dev *fsg)
// Fall through
default:
unknown_cmnd:
fsg->data_size_from_cmnd = 0;
sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]);
if ((reply = check_command(fsg, fsg->cmnd_size,
@ -3498,6 +3613,7 @@ static int open_backing_file(struct lun *curlun, const char *filename)
struct inode *inode = NULL;
loff_t size;
loff_t num_sectors;
loff_t min_sectors;
/* R/W if we can, R/O if we must */
ro = curlun->ro;
@ -3541,8 +3657,19 @@ static int open_backing_file(struct lun *curlun, const char *filename)
rc = (int) size;
goto out;
}
num_sectors = size >> 9; // File size in 512-byte sectors
if (num_sectors == 0) {
num_sectors = size >> 9; // File size in 512-byte blocks
min_sectors = 1;
if (mod_data.cdrom) {
num_sectors &= ~3; // Reduce to a multiple of 2048
min_sectors = 300*4; // Smallest track is 300 frames
if (num_sectors >= 256*60*75*4) {
num_sectors = (256*60*75 - 1) * 4;
LINFO(curlun, "file too big: %s\n", filename);
LINFO(curlun, "using only first %d blocks\n",
(int) num_sectors);
}
}
if (num_sectors < min_sectors) {
LINFO(curlun, "file too small: %s\n", filename);
rc = -ETOOSMALL;
goto out;
@ -3845,9 +3972,12 @@ static int __init fsg_bind(struct usb_gadget *gadget)
goto out;
if (mod_data.removable) { // Enable the store_xxx attributes
dev_attr_ro.attr.mode = dev_attr_file.attr.mode = 0644;
dev_attr_ro.store = store_ro;
dev_attr_file.attr.mode = 0644;
dev_attr_file.store = store_file;
if (!mod_data.cdrom) {
dev_attr_ro.attr.mode = 0644;
dev_attr_ro.store = store_ro;
}
}
/* Find out how many LUNs there should be */
@ -3872,6 +4002,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
curlun->ro = mod_data.ro[i];
if (mod_data.cdrom)
curlun->ro = 1;
curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
@ -4031,9 +4163,9 @@ static int __init fsg_bind(struct usb_gadget *gadget)
mod_data.protocol_name, mod_data.protocol_type);
DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n",
mod_data.vendor, mod_data.product, mod_data.release);
DBG(fsg, "removable=%d, stall=%d, buflen=%u\n",
DBG(fsg, "removable=%d, stall=%d, cdrom=%d, buflen=%u\n",
mod_data.removable, mod_data.can_stall,
mod_data.buflen);
mod_data.cdrom, mod_data.buflen);
DBG(fsg, "I/O thread pid: %d\n", task_pid_nr(fsg->thread_task));
set_bit(REGISTERED, &fsg->atomic_bitflags);
@ -4050,6 +4182,7 @@ out:
fsg->state = FSG_STATE_TERMINATED; // The thread is dead
fsg_unbind(gadget);
close_all_backing_files(fsg);
complete(&fsg->thread_notifier);
return rc;
}

View file

@ -26,6 +26,7 @@
#include <linux/ioport.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/interrupt.h>
@ -370,6 +371,9 @@ static int qe_ep_bd_init(struct qe_udc *udc, unsigned char pipe_num)
/* alloc multi-ram for BD rings and set the ep parameters */
tmp_addr = cpm_muram_alloc(sizeof(struct qe_bd) * (bdring_len +
USB_BDRING_LEN_TX), QE_ALIGNMENT_OF_BD);
if (IS_ERR_VALUE(tmp_addr))
return -ENOMEM;
out_be16(&epparam->rbase, (u16)tmp_addr);
out_be16(&epparam->tbase, (u16)(tmp_addr +
(sizeof(struct qe_bd) * bdring_len)));
@ -689,7 +693,7 @@ en_done2:
en_done1:
spin_unlock_irqrestore(&udc->lock, flags);
en_done:
dev_dbg(udc->dev, "failed to initialize %s\n", ep->ep.name);
dev_err(udc->dev, "failed to initialize %s\n", ep->ep.name);
return -ENODEV;
}
@ -2408,6 +2412,8 @@ static struct qe_udc __devinit *qe_udc_config(struct of_device *ofdev)
tmp_addr = cpm_muram_alloc((USB_MAX_ENDPOINTS *
sizeof(struct usb_ep_para)),
USB_EP_PARA_ALIGNMENT);
if (IS_ERR_VALUE(tmp_addr))
goto cleanup;
for (i = 0; i < USB_MAX_ENDPOINTS; i++) {
out_be16(&usbpram->epptr[i], (u16)tmp_addr);
@ -2513,7 +2519,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
/* Initialize the udc structure including QH member and other member */
udc_controller = qe_udc_config(ofdev);
if (!udc_controller) {
dev_dbg(&ofdev->dev, "udc_controll is NULL\n");
dev_err(&ofdev->dev, "failed to initialize\n");
return -ENOMEM;
}
@ -2568,7 +2574,7 @@ static int __devinit qe_udc_probe(struct of_device *ofdev,
/* create a buf for ZLP send, need to remain zeroed */
udc_controller->nullbuf = kzalloc(256, GFP_KERNEL);
if (udc_controller->nullbuf == NULL) {
dev_dbg(udc_controller->dev, "cannot alloc nullbuf\n");
dev_err(udc_controller->dev, "cannot alloc nullbuf\n");
ret = -ENOMEM;
goto err3;
}

View file

@ -110,7 +110,6 @@
#define gadget_is_at91(g) 0
#endif
/* status unclear */
#ifdef CONFIG_USB_GADGET_IMX
#define gadget_is_imx(g) !strcmp("imx_udc", (g)->name)
#else
@ -158,6 +157,11 @@
#define gadget_is_fsl_qe(g) 0
#endif
#ifdef CONFIG_USB_GADGET_CI13XXX
#define gadget_is_ci13xxx(g) (!strcmp("ci13xxx_udc", (g)->name))
#else
#define gadget_is_ci13xxx(g) 0
#endif
// CONFIG_USB_GADGET_SX2
// CONFIG_USB_GADGET_AU1X00
@ -225,6 +229,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x21;
else if (gadget_is_fsl_qe(gadget))
return 0x22;
else if (gadget_is_ci13xxx(gadget))
return 0x23;
return -ENOENT;
}

View file

@ -1349,7 +1349,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
int retval;
if (!driver
|| driver->speed != USB_SPEED_FULL
|| driver->speed < USB_SPEED_FULL
|| !driver->bind
|| !driver->disconnect
|| !driver->setup)

1516
drivers/usb/gadget/imx_udc.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,344 @@
/*
* Copyright (C) 2005 Mike Lee(eemike@gmail.com)
*
* This udc driver is now under testing and code is based on pxa2xx_udc.h
* Please use it with your own risk!
*
* 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.
*/
#ifndef __LINUX_USB_GADGET_IMX_H
#define __LINUX_USB_GADGET_IMX_H
#include <linux/types.h>
/* Helper macros */
#define EP_NO(ep) ((ep->bEndpointAddress) & ~USB_DIR_IN) /* IN:1, OUT:0 */
#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0)
#define irq_to_ep(irq) (((irq) >= USBD_INT0) || ((irq) <= USBD_INT6) ? ((irq) - USBD_INT0) : (USBD_INT6)) /*should not happen*/
#define ep_to_irq(ep) (EP_NO((ep)) + USBD_INT0)
#define IMX_USB_NB_EP 6
/* Driver structures */
struct imx_request {
struct usb_request req;
struct list_head queue;
unsigned int in_use;
};
enum ep0_state {
EP0_IDLE,
EP0_IN_DATA_PHASE,
EP0_OUT_DATA_PHASE,
EP0_CONFIG,
EP0_STALL,
};
struct imx_ep_struct {
struct usb_ep ep;
struct imx_udc_struct *imx_usb;
struct list_head queue;
unsigned char stopped;
unsigned char fifosize;
unsigned char bEndpointAddress;
unsigned char bmAttributes;
};
struct imx_udc_struct {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
struct imx_ep_struct imx_ep[IMX_USB_NB_EP];
struct clk *clk;
enum ep0_state ep0state;
struct resource *res;
void __iomem *base;
unsigned char set_config;
int cfg,
intf,
alt,
usbd_int[7];
};
/* USB registers */
#define USB_FRAME (0x00) /* USB frame */
#define USB_SPEC (0x04) /* USB Spec */
#define USB_STAT (0x08) /* USB Status */
#define USB_CTRL (0x0C) /* USB Control */
#define USB_DADR (0x10) /* USB Desc RAM addr */
#define USB_DDAT (0x14) /* USB Desc RAM/EP buffer data */
#define USB_INTR (0x18) /* USB interrupt */
#define USB_MASK (0x1C) /* USB Mask */
#define USB_ENAB (0x24) /* USB Enable */
#define USB_EP_STAT(x) (0x30 + (x*0x30)) /* USB status/control */
#define USB_EP_INTR(x) (0x34 + (x*0x30)) /* USB interrupt */
#define USB_EP_MASK(x) (0x38 + (x*0x30)) /* USB mask */
#define USB_EP_FDAT(x) (0x3C + (x*0x30)) /* USB FIFO data */
#define USB_EP_FDAT0(x) (0x3C + (x*0x30)) /* USB FIFO data */
#define USB_EP_FDAT1(x) (0x3D + (x*0x30)) /* USB FIFO data */
#define USB_EP_FDAT2(x) (0x3E + (x*0x30)) /* USB FIFO data */
#define USB_EP_FDAT3(x) (0x3F + (x*0x30)) /* USB FIFO data */
#define USB_EP_FSTAT(x) (0x40 + (x*0x30)) /* USB FIFO status */
#define USB_EP_FCTRL(x) (0x44 + (x*0x30)) /* USB FIFO control */
#define USB_EP_LRFP(x) (0x48 + (x*0x30)) /* USB last read frame pointer */
#define USB_EP_LWFP(x) (0x4C + (x*0x30)) /* USB last write frame pointer */
#define USB_EP_FALRM(x) (0x50 + (x*0x30)) /* USB FIFO alarm */
#define USB_EP_FRDP(x) (0x54 + (x*0x30)) /* USB FIFO read pointer */
#define USB_EP_FWRP(x) (0x58 + (x*0x30)) /* USB FIFO write pointer */
/* USB Control Register Bit Fields.*/
#define CTRL_CMDOVER (1<<6) /* UDC status */
#define CTRL_CMDERROR (1<<5) /* UDC status */
#define CTRL_FE_ENA (1<<3) /* Enable Font End logic */
#define CTRL_UDC_RST (1<<2) /* UDC reset */
#define CTRL_AFE_ENA (1<<1) /* Analog Font end enable */
#define CTRL_RESUME (1<<0) /* UDC resume */
/* USB Status Register Bit Fields.*/
#define STAT_RST (1<<8)
#define STAT_SUSP (1<<7)
#define STAT_CFG (3<<5)
#define STAT_INTF (3<<3)
#define STAT_ALTSET (7<<0)
/* USB Interrupt Status/Mask Registers Bit fields */
#define INTR_WAKEUP (1<<31) /* Wake up Interrupt */
#define INTR_MSOF (1<<7) /* Missed Start of Frame */
#define INTR_SOF (1<<6) /* Start of Frame */
#define INTR_RESET_STOP (1<<5) /* Reset Signaling stop */
#define INTR_RESET_START (1<<4) /* Reset Signaling start */
#define INTR_RESUME (1<<3) /* Suspend to resume */
#define INTR_SUSPEND (1<<2) /* Active to suspend */
#define INTR_FRAME_MATCH (1<<1) /* Frame matched */
#define INTR_CFG_CHG (1<<0) /* Configuration change occurred */
/* USB Enable Register Bit Fields.*/
#define ENAB_RST (1<<31) /* Reset USB modules */
#define ENAB_ENAB (1<<30) /* Enable USB modules*/
#define ENAB_SUSPEND (1<<29) /* Suspend USB modules */
#define ENAB_ENDIAN (1<<28) /* Endian of USB modules */
#define ENAB_PWRMD (1<<0) /* Power mode of USB modules */
/* USB Descriptor Ram Address Register bit fields */
#define DADR_CFG (1<<31) /* Configuration */
#define DADR_BSY (1<<30) /* Busy status */
#define DADR_DADR (0x1FF) /* Descriptor Ram Address */
/* USB Descriptor RAM/Endpoint Buffer Data Register bit fields */
#define DDAT_DDAT (0xFF) /* Descriptor Endpoint Buffer */
/* USB Endpoint Status Register bit fields */
#define EPSTAT_BCOUNT (0x7F<<16) /* Endpoint FIFO byte count */
#define EPSTAT_SIP (1<<8) /* Endpoint setup in progress */
#define EPSTAT_DIR (1<<7) /* Endpoint transfer direction */
#define EPSTAT_MAX (3<<5) /* Endpoint Max packet size */
#define EPSTAT_TYP (3<<3) /* Endpoint type */
#define EPSTAT_ZLPS (1<<2) /* Send zero length packet */
#define EPSTAT_FLUSH (1<<1) /* Endpoint FIFO Flush */
#define EPSTAT_STALL (1<<0) /* Force stall */
/* USB Endpoint FIFO Status Register bit fields */
#define FSTAT_FRAME_STAT (0xF<<24) /* Frame status bit [0-3] */
#define FSTAT_ERR (1<<22) /* FIFO error */
#define FSTAT_UF (1<<21) /* FIFO underflow */
#define FSTAT_OF (1<<20) /* FIFO overflow */
#define FSTAT_FR (1<<19) /* FIFO frame ready */
#define FSTAT_FULL (1<<18) /* FIFO full */
#define FSTAT_ALRM (1<<17) /* FIFO alarm */
#define FSTAT_EMPTY (1<<16) /* FIFO empty */
/* USB Endpoint FIFO Control Register bit fields */
#define FCTRL_WFR (1<<29) /* Write frame end */
/* USB Endpoint Interrupt Status Regsiter bit fields */
#define EPINTR_FIFO_FULL (1<<8) /* fifo full */
#define EPINTR_FIFO_EMPTY (1<<7) /* fifo empty */
#define EPINTR_FIFO_ERROR (1<<6) /* fifo error */
#define EPINTR_FIFO_HIGH (1<<5) /* fifo high */
#define EPINTR_FIFO_LOW (1<<4) /* fifo low */
#define EPINTR_MDEVREQ (1<<3) /* multi Device request */
#define EPINTR_EOT (1<<2) /* fifo end of transfer */
#define EPINTR_DEVREQ (1<<1) /* Device request */
#define EPINTR_EOF (1<<0) /* fifo end of frame */
/* Debug macros */
#ifdef DEBUG
/* #define DEBUG_REQ */
/* #define DEBUG_TRX */
/* #define DEBUG_INIT */
/* #define DEBUG_EP0 */
/* #define DEBUG_EPX */
/* #define DEBUG_IRQ */
/* #define DEBUG_EPIRQ */
/* #define DEBUG_DUMP */
#define DEBUG_ERR
#ifdef DEBUG_REQ
#define D_REQ(dev, args...) dev_dbg(dev, ## args)
#else
#define D_REQ(dev, args...) do {} while (0)
#endif /* DEBUG_REQ */
#ifdef DEBUG_TRX
#define D_TRX(dev, args...) dev_dbg(dev, ## args)
#else
#define D_TRX(dev, args...) do {} while (0)
#endif /* DEBUG_TRX */
#ifdef DEBUG_INIT
#define D_INI(dev, args...) dev_dbg(dev, ## args)
#else
#define D_INI(dev, args...) do {} while (0)
#endif /* DEBUG_INIT */
#ifdef DEBUG_EP0
static const char *state_name[] = {
"EP0_IDLE",
"EP0_IN_DATA_PHASE",
"EP0_OUT_DATA_PHASE",
"EP0_CONFIG",
"EP0_STALL"
};
#define D_EP0(dev, args...) dev_dbg(dev, ## args)
#else
#define D_EP0(dev, args...) do {} while (0)
#endif /* DEBUG_EP0 */
#ifdef DEBUG_EPX
#define D_EPX(dev, args...) dev_dbg(dev, ## args)
#else
#define D_EPX(dev, args...) do {} while (0)
#endif /* DEBUG_EP0 */
#ifdef DEBUG_IRQ
static void dump_intr(const char *label, int irqreg, struct device *dev)
{
dev_dbg(dev, "<%s> USB_INTR=[%s%s%s%s%s%s%s%s%s]\n", label,
(irqreg & INTR_WAKEUP) ? " wake" : "",
(irqreg & INTR_MSOF) ? " msof" : "",
(irqreg & INTR_SOF) ? " sof" : "",
(irqreg & INTR_RESUME) ? " resume" : "",
(irqreg & INTR_SUSPEND) ? " suspend" : "",
(irqreg & INTR_RESET_STOP) ? " noreset" : "",
(irqreg & INTR_RESET_START) ? " reset" : "",
(irqreg & INTR_FRAME_MATCH) ? " fmatch" : "",
(irqreg & INTR_CFG_CHG) ? " config" : "");
}
#else
#define dump_intr(x, y, z) do {} while (0)
#endif /* DEBUG_IRQ */
#ifdef DEBUG_EPIRQ
static void dump_ep_intr(const char *label, int nr, int irqreg, struct device *dev)
{
dev_dbg(dev, "<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, nr,
(irqreg & EPINTR_FIFO_FULL) ? " full" : "",
(irqreg & EPINTR_FIFO_EMPTY) ? " fempty" : "",
(irqreg & EPINTR_FIFO_ERROR) ? " ferr" : "",
(irqreg & EPINTR_FIFO_HIGH) ? " fhigh" : "",
(irqreg & EPINTR_FIFO_LOW) ? " flow" : "",
(irqreg & EPINTR_MDEVREQ) ? " mreq" : "",
(irqreg & EPINTR_EOF) ? " eof" : "",
(irqreg & EPINTR_DEVREQ) ? " devreq" : "",
(irqreg & EPINTR_EOT) ? " eot" : "");
}
#else
#define dump_ep_intr(x, y, z, i) do {} while (0)
#endif /* DEBUG_IRQ */
#ifdef DEBUG_DUMP
static void dump_usb_stat(const char *label, struct imx_udc_struct *imx_usb)
{
int temp = __raw_readl(imx_usb->base + USB_STAT);
dev_dbg(imx_usb->dev,
"<%s> USB_STAT=[%s%s CFG=%d, INTF=%d, ALTR=%d]\n", label,
(temp & STAT_RST) ? " reset" : "",
(temp & STAT_SUSP) ? " suspend" : "",
(temp & STAT_CFG) >> 5,
(temp & STAT_INTF) >> 3,
(temp & STAT_ALTSET));
}
static void dump_ep_stat(const char *label, struct imx_ep_struct *imx_ep)
{
int temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_INTR(EP_NO(imx_ep)));
dev_dbg(imx_ep->imx_usb->dev,
"<%s> EP%d_INTR=[%s%s%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
(temp & EPINTR_FIFO_FULL) ? " full" : "",
(temp & EPINTR_FIFO_EMPTY) ? " fempty" : "",
(temp & EPINTR_FIFO_ERROR) ? " ferr" : "",
(temp & EPINTR_FIFO_HIGH) ? " fhigh" : "",
(temp & EPINTR_FIFO_LOW) ? " flow" : "",
(temp & EPINTR_MDEVREQ) ? " mreq" : "",
(temp & EPINTR_EOF) ? " eof" : "",
(temp & EPINTR_DEVREQ) ? " devreq" : "",
(temp & EPINTR_EOT) ? " eot" : "");
temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_STAT(EP_NO(imx_ep)));
dev_dbg(imx_ep->imx_usb->dev,
"<%s> EP%d_STAT=[%s%s bcount=%d]\n", label, EP_NO(imx_ep),
(temp & EPSTAT_SIP) ? " sip" : "",
(temp & EPSTAT_STALL) ? " stall" : "",
(temp & EPSTAT_BCOUNT) >> 16);
temp = __raw_readl(imx_ep->imx_usb->base + USB_EP_FSTAT(EP_NO(imx_ep)));
dev_dbg(imx_ep->imx_usb->dev,
"<%s> EP%d_FSTAT=[%s%s%s%s%s%s%s]\n", label, EP_NO(imx_ep),
(temp & FSTAT_ERR) ? " ferr" : "",
(temp & FSTAT_UF) ? " funder" : "",
(temp & FSTAT_OF) ? " fover" : "",
(temp & FSTAT_FR) ? " fready" : "",
(temp & FSTAT_FULL) ? " ffull" : "",
(temp & FSTAT_ALRM) ? " falarm" : "",
(temp & FSTAT_EMPTY) ? " fempty" : "");
}
static void dump_req(const char *label, struct imx_ep_struct *imx_ep, struct usb_request *req)
{
int i;
if (!req || !req->buf) {
dev_dbg(imx_ep->imx_usb->dev, "<%s> req or req buf is free\n", label);
return;
}
if ((!EP_NO(imx_ep) && imx_ep->imx_usb->ep0state == EP0_IN_DATA_PHASE)
|| (EP_NO(imx_ep) && EP_DIR(imx_ep))) {
dev_dbg(imx_ep->imx_usb->dev, "<%s> request dump <", label);
for (i = 0; i < req->length; i++)
printk("%02x-", *((u8 *)req->buf + i));
printk(">\n");
}
}
#else
#define dump_ep_stat(x, y) do {} while (0)
#define dump_usb_stat(x, y) do {} while (0)
#define dump_req(x, y, z) do {} while (0)
#endif /* DEBUG_DUMP */
#ifdef DEBUG_ERR
#define D_ERR(dev, args...) dev_dbg(dev, ## args)
#else
#define D_ERR(dev, args...) do {} while (0)
#endif
#else
#define D_REQ(dev, args...) do {} while (0)
#define D_TRX(dev, args...) do {} while (0)
#define D_INI(dev, args...) do {} while (0)
#define D_EP0(dev, args...) do {} while (0)
#define D_EPX(dev, args...) do {} while (0)
#define dump_ep_intr(x, y, z, i) do {} while (0)
#define dump_intr(x, y, z) do {} while (0)
#define dump_ep_stat(x, y) do {} while (0)
#define dump_usb_stat(x, y) do {} while (0)
#define dump_req(x, y, z) do {} while (0)
#define D_ERR(dev, args...) do {} while (0)
#endif /* DEBUG */
#endif /* __LINUX_USB_GADGET_IMX_H */

View file

@ -1546,8 +1546,6 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
#define resource_len(r) (((r)->end - (r)->start) + 1)
static int __init m66592_probe(struct platform_device *pdev)
{
struct resource *res;
@ -1560,11 +1558,10 @@ static int __init m66592_probe(struct platform_device *pdev)
int ret = 0;
int i;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
(char *)udc_name);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
pr_err("platform_get_resource_byname error.\n");
pr_err("platform_get_resource error.\n");
goto clean_up;
}
@ -1575,7 +1572,7 @@ static int __init m66592_probe(struct platform_device *pdev)
goto clean_up;
}
reg = ioremap(res->start, resource_len(res));
reg = ioremap(res->start, resource_size(res));
if (reg == NULL) {
ret = -ENOMEM;
pr_err("ioremap error.\n");

View file

@ -669,7 +669,7 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
/* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
wmb ();
td->dmacount = cpu_to_le32p (&dmacount);
td->dmacount = cpu_to_le32(dmacount);
}
static const u32 dmactl_default =

View file

@ -3006,7 +3006,7 @@ cleanup1:
cleanup0:
if (xceiv)
put_device(xceiv->dev);
otg_put_transceiver(xceiv);
if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
clk_disable(hhc_clk);
@ -3034,7 +3034,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
pullup_disable(udc);
if (udc->transceiver) {
put_device(udc->transceiver->dev);
otg_put_transceiver(udc->transceiver);
udc->transceiver = NULL;
}
omap_writew(0, UDC_SYSCON1);

View file

@ -2198,7 +2198,7 @@ static int __init pxa25x_udc_probe(struct platform_device *pdev)
udc_disable(dev);
udc_reinit(dev);
dev->vbus = is_vbus_present();
dev->vbus = !!is_vbus_present();
/* irq setup after old hardware state is cleaned up */
retval = request_irq(irq, pxa25x_udc_irq,

View file

@ -430,7 +430,6 @@ static void pio_irq_enable(struct pxa_ep *ep)
/**
* pio_irq_disable - Disables irq generation for one endpoint
* @ep: udc endpoint
* @index: endpoint number
*/
static void pio_irq_disable(struct pxa_ep *ep)
{
@ -586,7 +585,6 @@ static void inc_ep_stats_reqs(struct pxa_ep *ep, int is_in)
* inc_ep_stats_bytes - Update ep stats counts
* @ep: physical endpoint
* @count: bytes transfered on endpoint
* @req: usb request
* @is_in: ep direction (USB_DIR_IN or 0)
*/
static void inc_ep_stats_bytes(struct pxa_ep *ep, int count, int is_in)

View file

@ -36,6 +36,7 @@
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@ -51,7 +52,6 @@
#include <mach/irqs.h>
#include <mach/hardware.h>
#include <mach/regs-gpio.h>
#include <plat/regs-udc.h>
#include <plat/udc.h>
@ -1510,11 +1510,7 @@ static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev)
dprintk(DEBUG_NORMAL, "%s()\n", __func__);
/* some cpus cannot read from an line configured to IRQ! */
s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_INPUT);
value = s3c2410_gpio_getpin(udc_info->vbus_pin);
s3c2410_gpio_cfgpin(udc_info->vbus_pin, S3C2410_GPIO_SFN2);
value = gpio_get_value(udc_info->vbus_pin) ? 1 : 0;
if (udc_info->vbus_pin_inverted)
value = !value;
@ -1802,7 +1798,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
struct s3c2410_udc *udc = &memory;
struct device *dev = &pdev->dev;
int retval;
unsigned int irq;
int irq;
dev_dbg(dev, "%s()\n", __func__);
@ -1861,7 +1857,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
/* irq setup after old hardware state is cleaned up */
retval = request_irq(IRQ_USBD, s3c2410_udc_irq,
IRQF_DISABLED, gadget_name, udc);
IRQF_DISABLED, gadget_name, udc);
if (retval != 0) {
dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval);
@ -1872,17 +1868,28 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
dev_dbg(dev, "got irq %i\n", IRQ_USBD);
if (udc_info && udc_info->vbus_pin > 0) {
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
retval = gpio_request(udc_info->vbus_pin, "udc vbus");
if (retval < 0) {
dev_err(dev, "cannot claim vbus pin\n");
goto err_int;
}
irq = gpio_to_irq(udc_info->vbus_pin);
if (irq < 0) {
dev_err(dev, "no irq for gpio vbus pin\n");
goto err_gpio_claim;
}
retval = request_irq(irq, s3c2410_udc_vbus_irq,
IRQF_DISABLED | IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
gadget_name, udc);
if (retval != 0) {
dev_err(dev, "can't get vbus irq %i, err %d\n",
dev_err(dev, "can't get vbus irq %d, err %d\n",
irq, retval);
retval = -EBUSY;
goto err_int;
goto err_gpio_claim;
}
dev_dbg(dev, "got irq %i\n", irq);
@ -1902,6 +1909,9 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return 0;
err_gpio_claim:
if (udc_info && udc_info->vbus_pin > 0)
gpio_free(udc_info->vbus_pin);
err_int:
free_irq(IRQ_USBD, udc);
err_map:
@ -1927,7 +1937,7 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
debugfs_remove(udc->regs_info);
if (udc_info && udc_info->vbus_pin > 0) {
irq = s3c2410_gpio_getirq(udc_info->vbus_pin);
irq = gpio_to_irq(udc_info->vbus_pin);
free_irq(irq, udc);
}

View file

@ -96,6 +96,19 @@ config USB_EHCI_HCD_PPC_OF
Enables support for the USB controller present on the PowerPC
OpenFirmware platform bus.
config USB_OXU210HP_HCD
tristate "OXU210HP HCD support"
depends on USB
---help---
The OXU210HP is an USB host/OTG/device controller. Enable this
option if your board has this chip. If unsure, say N.
This driver does not support isochronous transfers and doesn't
implement OTG nor USB device controllers.
To compile this driver as a module, choose M here: the
module will be called oxu210hp-hcd.
config USB_ISP116X_HCD
tristate "ISP116X HCD support"
depends on USB

View file

@ -13,6 +13,7 @@ obj-$(CONFIG_USB_WHCI_HCD) += whci/
obj-$(CONFIG_PCI) += pci-quirks.o
obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o
obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o
obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o

View file

@ -455,9 +455,7 @@ static void qh_lines (
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
if (temp < 0)
temp = 0;
else if (size < temp)
if (size < temp)
temp = size;
size -= temp;
next += temp;
@ -466,9 +464,7 @@ static void qh_lines (
}
temp = snprintf (next, size, "\n");
if (temp < 0)
temp = 0;
else if (size < temp)
if (size < temp)
temp = size;
size -= temp;
next += temp;

View file

@ -194,6 +194,7 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
u32 temp;
u32 power_okay;
int i;
u8 resume_needed = 0;
if (time_before (jiffies, ehci->next_statechange))
msleep(5);
@ -228,7 +229,9 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* Some controller/firmware combinations need a delay during which
* they set up the port statuses. See Bugzilla #8190. */
mdelay(8);
spin_unlock_irq(&ehci->lock);
msleep(8);
spin_lock_irq(&ehci->lock);
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
@ -236,12 +239,21 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
temp &= ~(PORT_RWC_BITS | PORT_WAKE_BITS);
if (test_bit(i, &ehci->bus_suspended) &&
(temp & PORT_SUSPEND))
(temp & PORT_SUSPEND)) {
temp |= PORT_RESUME;
resume_needed = 1;
}
ehci_writel(ehci, temp, &ehci->regs->port_status [i]);
}
/* msleep for 20ms only if code is trying to resume port */
if (resume_needed) {
spin_unlock_irq(&ehci->lock);
msleep(20);
spin_lock_irq(&ehci->lock);
}
i = HCS_N_PORTS (ehci->hcs_params);
mdelay (20);
while (i--) {
temp = ehci_readl(ehci, &ehci->regs->port_status [i]);
if (test_bit(i, &ehci->bus_suspended) &&
@ -422,8 +434,15 @@ static int check_reset_complete (
port_status &= ~PORT_RWC_BITS;
ehci_writel(ehci, port_status, status_reg);
} else
/* ensure 440EPX ohci controller state is operational */
if (ehci->has_amcc_usb23)
set_ohci_hcfs(ehci, 1);
} else {
ehci_dbg (ehci, "port %d high speed\n", index + 1);
/* ensure 440EPx ohci controller state is suspended */
if (ehci->has_amcc_usb23)
set_ohci_hcfs(ehci, 0);
}
return port_status;
}

View file

@ -219,15 +219,19 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
/* Serial Bus Release Number is at PCI 0x60 offset */
pci_read_config_byte(pdev, 0x60, &ehci->sbrn);
/* Workaround current PCI init glitch: wakeup bits aren't
* being set from PCI PM capability.
/* Keep this around for a while just in case some EHCI
* implementation uses legacy PCI PM support. This test
* can be removed on 17 Dec 2009 if the dev_warn() hasn't
* been triggered by then.
*/
if (!device_can_wakeup(&pdev->dev)) {
u16 port_wake;
pci_read_config_word(pdev, 0x62, &port_wake);
if (port_wake & 0x0001)
if (port_wake & 0x0001) {
dev_warn(&pdev->dev, "Enabling legacy PCI PM\n");
device_init_wakeup(&pdev->dev, 1);
}
}
#ifdef CONFIG_USB_SUSPEND
@ -428,6 +432,8 @@ static struct pci_driver ehci_pci_driver = {
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif
.shutdown = usb_hcd_pci_shutdown,

View file

@ -107,11 +107,13 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
{
struct device_node *dn = op->node;
struct usb_hcd *hcd;
struct ehci_hcd *ehci;
struct ehci_hcd *ehci = NULL;
struct resource res;
int irq;
int rv;
struct device_node *np;
if (usb_disabled())
return -ENODEV;
@ -149,6 +151,20 @@ ehci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
}
ehci = hcd_to_ehci(hcd);
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
if (np != NULL) {
/* claim we really affected by usb23 erratum */
if (!of_address_to_resource(np, 0, &res))
ehci->ohci_hcctrl_reg = ioremap(res.start +
OHCI_HCCTRL_OFFSET, OHCI_HCCTRL_LEN);
else
pr_debug(__FILE__ ": no ohci offset in fdt\n");
if (!ehci->ohci_hcctrl_reg) {
pr_debug(__FILE__ ": ioremap for ohci hcctrl failed\n");
} else {
ehci->has_amcc_usb23 = 1;
}
}
if (of_get_property(dn, "big-endian", NULL)) {
ehci->big_endian_mmio = 1;
@ -181,6 +197,9 @@ err_ioremap:
irq_dispose_mapping(irq);
err_irq:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
if (ehci->has_amcc_usb23)
iounmap(ehci->ohci_hcctrl_reg);
err_rmr:
usb_put_hcd(hcd);
@ -191,6 +210,11 @@ err_rmr:
static int ehci_hcd_ppc_of_remove(struct of_device *op)
{
struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
struct device_node *np;
struct resource res;
dev_set_drvdata(&op->dev, NULL);
dev_dbg(&op->dev, "stopping PPC-OF USB Controller\n");
@ -201,6 +225,25 @@ static int ehci_hcd_ppc_of_remove(struct of_device *op)
irq_dispose_mapping(hcd->irq);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
/* use request_mem_region to test if the ohci driver is loaded. if so
* ensure the ohci core is operational.
*/
if (ehci->has_amcc_usb23) {
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ohci-440epx");
if (np != NULL) {
if (!of_address_to_resource(np, 0, &res))
if (!request_mem_region(res.start,
0x4, hcd_name))
set_ohci_hcfs(ehci, 1);
else
release_mem_region(res.start, 0x4);
else
pr_debug(__FILE__ ": no ohci offset in fdt\n");
of_node_put(np);
}
iounmap(ehci->ohci_hcctrl_reg);
}
usb_put_hcd(hcd);
return 0;

View file

@ -120,6 +120,16 @@ struct ehci_hcd { /* one per controller */
unsigned has_fsl_port_bug:1; /* FreeScale */
unsigned big_endian_mmio:1;
unsigned big_endian_desc:1;
unsigned has_amcc_usb23:1;
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
#define OHCI_USB_OPER (2 << 6)
#define OHCI_USB_SUSPEND (3 << 6)
#define OHCI_HCCTRL_OFFSET 0x4
#define OHCI_HCCTRL_LEN 0x4
__hc32 *ohci_hcctrl_reg;
u8 sbrn; /* packed release number */
@ -636,6 +646,30 @@ static inline void ehci_writel(const struct ehci_hcd *ehci,
#endif
}
/*
* On certain ppc-44x SoC there is a HW issue, that could only worked around with
* explicit suspend/operate of OHCI. This function hereby makes sense only on that arch.
* Other common bits are dependant on has_amcc_usb23 quirk flag.
*/
#ifdef CONFIG_44x
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
{
u32 hc_control;
hc_control = (readl_be(ehci->ohci_hcctrl_reg) & ~OHCI_CTRL_HCFS);
if (operational)
hc_control |= OHCI_USB_OPER;
else
hc_control |= OHCI_USB_SUSPEND;
writel_be(hc_control, ehci->ohci_hcctrl_reg);
(void) readl_be(ehci->ohci_hcctrl_reg);
}
#else
static inline void set_ohci_hcfs(struct ehci_hcd *ehci, int operational)
{ }
#endif
/*-------------------------------------------------------------------------*/
/*

View file

@ -435,14 +435,13 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
/*
* PORT 1 Control register of the ISP1760 is the OTG control
* register on ISP1761.
* register on ISP1761. Since there is no OTG or device controller
* support in this driver, we use port 1 as a "normal" USB host port on
* both chips.
*/
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);
}
isp1760_writel(PORT1_POWER | PORT1_INIT2,
hcd->regs + HC_PORT1_CTRL);
mdelay(10);
priv->hcs_params = isp1760_readl(hcd->regs + HC_HCSPARAMS);

View file

@ -135,7 +135,6 @@ typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
* 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 */

View file

@ -60,9 +60,6 @@ static int of_isp1760_probe(struct of_device *dev,
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)
@ -129,23 +126,23 @@ static struct of_platform_driver isp1760_of_driver = {
#endif
#ifdef CONFIG_PCI
static u32 nxp_pci_io_base;
static u32 iolength;
static u32 pci_mem_phy0;
static u32 length;
static u8 __iomem *chip_addr;
static u8 __iomem *iobase;
static int __devinit isp1761_pci_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
u8 latency, limit;
__u32 reg_data;
int retry_count;
int length;
int status = 1;
struct usb_hcd *hcd;
unsigned int devflags = 0;
int ret_status = 0;
resource_size_t pci_mem_phy0;
resource_size_t memlength;
u8 __iomem *chip_addr;
u8 __iomem *iobase;
resource_size_t nxp_pci_io_base;
resource_size_t iolength;
if (usb_disabled())
return -ENODEV;
@ -168,26 +165,30 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
iobase = ioremap_nocache(nxp_pci_io_base, iolength);
if (!iobase) {
printk(KERN_ERR "ioremap #1\n");
release_mem_region(nxp_pci_io_base, iolength);
return -ENOMEM;
ret_status = -ENOMEM;
goto cleanup1;
}
/* Grab the PLX PCI shared memory of the ISP 1761 we need */
pci_mem_phy0 = pci_resource_start(dev, 3);
length = pci_resource_len(dev, 3);
if (length < 0xffff) {
printk(KERN_ERR "memory length for this resource is less than "
"required\n");
release_mem_region(nxp_pci_io_base, iolength);
iounmap(iobase);
return -ENOMEM;
memlength = pci_resource_len(dev, 3);
if (memlength < 0xffff) {
printk(KERN_ERR "memory length for this resource is wrong\n");
ret_status = -ENOMEM;
goto cleanup2;
}
if (!request_mem_region(pci_mem_phy0, length, "ISP-PCI")) {
if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
printk(KERN_ERR "host controller already in use\n");
release_mem_region(nxp_pci_io_base, iolength);
iounmap(iobase);
return -EBUSY;
ret_status = -EBUSY;
goto cleanup2;
}
/* map available memory */
chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
if (!chip_addr) {
printk(KERN_ERR "Error ioremap failed\n");
ret_status = -ENOMEM;
goto cleanup3;
}
/* bad pci latencies can contribute to overruns */
@ -210,39 +211,54 @@ static int __devinit isp1761_pci_probe(struct pci_dev *dev,
* */
writel(0xface, chip_addr + HC_SCRATCH_REG);
udelay(100);
reg_data = readl(chip_addr + HC_SCRATCH_REG);
reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
retry_count--;
}
iounmap(chip_addr);
/* Host Controller presence is detected by writing to scratch register
* and reading back and checking the contents are same or not
*/
if (reg_data != 0xFACE) {
dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
goto clean;
ret_status = -ENOMEM;
goto cleanup3;
}
pci_set_master(dev);
status = readl(iobase + 0x68);
status |= 0x900;
writel(status, iobase + 0x68);
/* configure PLX PCI chip to pass interrupts */
#define PLX_INT_CSR_REG 0x68
reg_data = readl(iobase + PLX_INT_CSR_REG);
reg_data |= 0x900;
writel(reg_data, iobase + PLX_INT_CSR_REG);
dev->dev.dma_mask = NULL;
hcd = isp1760_register(pci_mem_phy0, length, dev->irq,
hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
IRQF_SHARED | IRQF_DISABLED, &dev->dev, dev_name(&dev->dev),
devflags);
if (!IS_ERR(hcd)) {
pci_set_drvdata(dev, hcd);
return 0;
if (IS_ERR(hcd)) {
ret_status = -ENODEV;
goto cleanup3;
}
clean:
status = -ENODEV;
/* done with PLX IO access */
iounmap(iobase);
release_mem_region(pci_mem_phy0, length);
release_mem_region(nxp_pci_io_base, iolength);
return status;
pci_set_drvdata(dev, hcd);
return 0;
cleanup3:
release_mem_region(pci_mem_phy0, memlength);
cleanup2:
iounmap(iobase);
cleanup1:
release_mem_region(nxp_pci_io_base, iolength);
return ret_status;
}
static void isp1761_pci_remove(struct pci_dev *dev)
{
struct usb_hcd *hcd;
@ -255,12 +271,6 @@ static void isp1761_pci_remove(struct pci_dev *dev)
usb_put_hcd(hcd);
pci_disable_device(dev);
iounmap(iobase);
iounmap(chip_addr);
release_mem_region(nxp_pci_io_base, iolength);
release_mem_region(pci_mem_phy0, length);
}
static void isp1761_pci_shutdown(struct pci_dev *dev)
@ -268,12 +278,16 @@ static void isp1761_pci_shutdown(struct pci_dev *dev)
printk(KERN_ERR "ips1761_pci_shutdown\n");
}
static const struct pci_device_id isp1760_plx [] = { {
/* handle any USB 2.0 EHCI controller */
PCI_DEVICE_CLASS(((PCI_CLASS_BRIDGE_OTHER << 8) | (0x06 << 16)), ~0),
.driver_data = 0,
},
{ /* end: all zeroes */ }
static const struct pci_device_id isp1760_plx [] = {
{
.class = PCI_CLASS_BRIDGE_OTHER << 8,
.class_mask = ~0,
.vendor = PCI_VENDOR_ID_PLX,
.device = 0x5406,
.subvendor = PCI_VENDOR_ID_PLX,
.subdevice = 0x9054,
},
{ }
};
MODULE_DEVICE_TABLE(pci, isp1760_plx);

View file

@ -589,13 +589,15 @@ static int ohci_run (struct ohci_hcd *ohci)
/* also: power/overcurrent flags in roothub.a */
}
/* Reset USB nearly "by the book". RemoteWakeupConnected was
* saved if boot firmware (BIOS/SMM/...) told us it's connected,
* or if bus glue did the same (e.g. for PCI add-in cards with
* PCI PM support).
/* Reset USB nearly "by the book". RemoteWakeupConnected has
* to be checked in case boot firmware (BIOS/SMM/...) has set up
* wakeup in a way the bus isn't aware of (e.g., legacy PCI PM).
* If the bus glue detected wakeup capability then it should
* already be enabled. Either way, if wakeup should be enabled
* but isn't, we'll enable it now.
*/
if ((ohci->hc_control & OHCI_CTRL_RWC) != 0
&& !device_may_wakeup(hcd->self.controller))
&& !device_can_wakeup(hcd->self.controller))
device_init_wakeup(hcd->self.controller, 1);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {

View file

@ -355,9 +355,9 @@ static int __devinit ohci_pci_start (struct usb_hcd *hcd)
/* RWC may not be set for add-in PCI cards, since boot
* firmware probably ignored them. This transfers PCI
* PM wakeup capabilities (once the PCI layer is fixed).
* PM wakeup capabilities.
*/
if (device_may_wakeup(&pdev->dev))
if (device_can_wakeup(&pdev->dev))
ohci->hc_control |= OHCI_CTRL_RWC;
}
#endif /* CONFIG_PM */
@ -487,6 +487,8 @@ static struct pci_driver ohci_pci_driver = {
#ifdef CONFIG_PM
.suspend = usb_hcd_pci_suspend,
.suspend_late = usb_hcd_pci_suspend_late,
.resume_early = usb_hcd_pci_resume_early,
.resume = usb_hcd_pci_resume,
#endif

View file

@ -106,65 +106,34 @@ extern int ocpi_enable(void);
static struct clk *usb_clk;
static int isp1301_probe(struct i2c_adapter *adap);
static int isp1301_detach(struct i2c_client *client);
static const unsigned short normal_i2c[] =
{ ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
static struct i2c_client_address_data addr_data = {
.normal_i2c = normal_i2c,
.probe = dummy_i2c_addrlist,
.ignore = dummy_i2c_addrlist,
static int isp1301_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
return 0;
}
static int isp1301_remove(struct i2c_client *client)
{
return 0;
}
const struct i2c_device_id isp1301_id[] = {
{ "isp1301_pnx", 0 },
{ }
};
struct i2c_driver isp1301_driver = {
.driver = {
.name = "isp1301_pnx",
},
.attach_adapter = isp1301_probe,
.detach_client = isp1301_detach,
.probe = isp1301_probe,
.remove = isp1301_remove,
.id_table = isp1301_id,
};
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;
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 0;
}
static int isp1301_probe(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, isp1301_attach);
}
static int isp1301_detach(struct i2c_client *client)
{
i2c_detach_client(client);
kfree(isp1301_i2c_client);
return 0;
}
static void i2c_write(u8 buf, u8 subaddr)
{
char tmpbuf[2];
@ -328,6 +297,8 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
struct usb_hcd *hcd = 0;
struct ohci_hcd *ohci;
const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
struct i2c_adapter *i2c_adap;
struct i2c_board_info i2c_info;
int ret = 0, irq;
@ -351,9 +322,20 @@ static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
ret = i2c_add_driver(&isp1301_driver);
if (ret < 0) {
err("failed to connect I2C to ISP1301 USB Transceiver");
err("failed to add ISP1301 driver");
goto out;
}
i2c_adap = i2c_get_adapter(2);
memset(&i2c_info, 0, sizeof(struct i2c_board_info));
strlcpy(i2c_info.name, "isp1301_pnx", I2C_NAME_SIZE);
isp1301_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,
normal_i2c);
i2c_put_adapter(i2c_adap);
if (!isp1301_i2c_client) {
err("failed to connect I2C to ISP1301 USB Transceiver");
ret = -ENODEV;
goto out_i2c_driver;
}
isp1301_configure();
@ -429,6 +411,9 @@ out3:
out2:
clk_put(usb_clk);
out1:
i2c_unregister_client(isp1301_i2c_client);
isp1301_i2c_client = NULL;
out_i2c_driver:
i2c_del_driver(&isp1301_driver);
out:
return ret;
@ -445,6 +430,8 @@ static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
pnx4008_unset_usb_bits();
clk_disable(usb_clk);
clk_put(usb_clk);
i2c_unregister_client(isp1301_i2c_client);
isp1301_i2c_client = NULL;
i2c_del_driver(&isp1301_driver);
platform_set_drvdata(pdev, NULL);

View file

@ -91,6 +91,7 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
int rv;
int is_bigendian;
struct device_node *np;
if (usb_disabled())
return -ENODEV;
@ -147,6 +148,30 @@ ohci_hcd_ppc_of_probe(struct of_device *op, const struct of_device_id *match)
if (rv == 0)
return 0;
/* by now, 440epx is known to show usb_23 erratum */
np = of_find_compatible_node(NULL, NULL, "ibm,usb-ehci-440epx");
/* Work around - At this point ohci_run has executed, the
* controller is running, everything, the root ports, etc., is
* set up. If the ehci driver is loaded, put the ohci core in
* the suspended state. The ehci driver will bring it out of
* suspended state when / if a non-high speed USB device is
* attached to the USB Host port. If the ehci driver is not
* loaded, do nothing. request_mem_region is used to test if
* the ehci driver is loaded.
*/
if (np != NULL) {
if (!of_address_to_resource(np, 0, &res)) {
if (!request_mem_region(res.start, 0x4, hcd_name)) {
writel_be((readl_be(&ohci->regs->control) |
OHCI_USB_SUSPEND), &ohci->regs->control);
(void) readl_be(&ohci->regs->control);
} else
release_mem_region(res.start, 0x4);
} else
pr_debug(__FILE__ ": cannot get ehci offset from fdt\n");
}
iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);

View file

@ -201,7 +201,7 @@ static int __devinit ohci_hcd_tmio_drv_probe(struct platform_device *dev)
if (!cell)
return -EINVAL;
hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev->dev.bus_id);
hcd = usb_create_hcd(&ohci_tmio_hc_driver, &dev->dev, dev_name(&dev->dev));
if (!hcd) {
ret = -ENOMEM;
goto err_usb_create_hcd;

File diff suppressed because it is too large Load diff

447
drivers/usb/host/oxu210hp.h Normal file
View file

@ -0,0 +1,447 @@
/*
* Host interface registers
*/
#define OXU_DEVICEID 0x00
#define OXU_REV_MASK 0xffff0000
#define OXU_REV_SHIFT 16
#define OXU_REV_2100 0x2100
#define OXU_BO_SHIFT 8
#define OXU_BO_MASK (0x3 << OXU_BO_SHIFT)
#define OXU_MAJ_REV_SHIFT 4
#define OXU_MAJ_REV_MASK (0xf << OXU_MAJ_REV_SHIFT)
#define OXU_MIN_REV_SHIFT 0
#define OXU_MIN_REV_MASK (0xf << OXU_MIN_REV_SHIFT)
#define OXU_HOSTIFCONFIG 0x04
#define OXU_SOFTRESET 0x08
#define OXU_SRESET (1 << 0)
#define OXU_PIOBURSTREADCTRL 0x0C
#define OXU_CHIPIRQSTATUS 0x10
#define OXU_CHIPIRQEN_SET 0x14
#define OXU_CHIPIRQEN_CLR 0x18
#define OXU_USBSPHLPWUI 0x00000080
#define OXU_USBOTGLPWUI 0x00000040
#define OXU_USBSPHI 0x00000002
#define OXU_USBOTGI 0x00000001
#define OXU_CLKCTRL_SET 0x1C
#define OXU_SYSCLKEN 0x00000008
#define OXU_USBSPHCLKEN 0x00000002
#define OXU_USBOTGCLKEN 0x00000001
#define OXU_ASO 0x68
#define OXU_SPHPOEN 0x00000100
#define OXU_OVRCCURPUPDEN 0x00000800
#define OXU_ASO_OP (1 << 10)
#define OXU_COMPARATOR 0x000004000
#define OXU_USBMODE 0x1A8
#define OXU_VBPS 0x00000020
#define OXU_ES_LITTLE 0x00000000
#define OXU_CM_HOST_ONLY 0x00000003
/*
* Proper EHCI structs & defines
*/
/* Magic numbers that can affect system performance */
#define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */
#define EHCI_TUNE_RL_HS 4 /* nak throttle; see 4.9 */
#define EHCI_TUNE_RL_TT 0
#define EHCI_TUNE_MULT_HS 1 /* 1-3 transactions/uframe; 4.10.3 */
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
struct oxu_hcd;
/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
/* Section 2.2 Host Controller Capability Registers */
struct ehci_caps {
/* these fields are specified as 8 and 16 bit registers,
* but some hosts can't perform 8 or 16 bit PCI accesses.
*/
u32 hc_capbase;
#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
u32 hcs_params; /* HCSPARAMS - offset 0x4 */
#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
u8 portroute[8]; /* nibbles for routing - offset 0xC */
} __attribute__ ((packed));
/* Section 2.3 Host Controller Operational Registers */
struct ehci_regs {
/* USBCMD: offset 0x00 */
u32 command;
/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
#define CMD_PARK (1<<11) /* enable "park" on async qh */
#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
/* USBSTS: offset 0x04 */
u32 status;
#define STS_ASS (1<<15) /* Async Schedule Status */
#define STS_PSS (1<<14) /* Periodic Schedule Status */
#define STS_RECL (1<<13) /* Reclamation */
#define STS_HALT (1<<12) /* Not running (any reason) */
/* some bits reserved */
/* these STS_* flags are also intr_enable bits (USBINTR) */
#define STS_IAA (1<<5) /* Interrupted on async advance */
#define STS_FATAL (1<<4) /* such as some PCI access errors */
#define STS_FLR (1<<3) /* frame list rolled over */
#define STS_PCD (1<<2) /* port change detect */
#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
#define STS_INT (1<<0) /* "normal" completion (short, ...) */
#define INTR_MASK (STS_IAA | STS_FATAL | STS_PCD | STS_ERR | STS_INT)
/* USBINTR: offset 0x08 */
u32 intr_enable;
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
u32 reserved[9];
/* CONFIGFLAG: offset 0x40 */
u32 configured_flag;
#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
/* PORTSC: offset 0x44 */
u32 port_status[0]; /* up to N_PORTS */
/* 31:23 reserved */
#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
/* 19:16 for port testing */
#define PORT_LED_OFF (0<<14)
#define PORT_LED_AMBER (1<<14)
#define PORT_LED_GREEN (2<<14)
#define PORT_LED_MASK (3<<14)
#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
#define PORT_POWER (1<<12) /* true: has power (see PPC) */
#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
/* 9 reserved */
#define PORT_RESET (1<<8) /* reset port */
#define PORT_SUSPEND (1<<7) /* suspend port */
#define PORT_RESUME (1<<6) /* resume it */
#define PORT_OCC (1<<5) /* over current change */
#define PORT_OC (1<<4) /* over current active */
#define PORT_PEC (1<<3) /* port enable change */
#define PORT_PE (1<<2) /* port enable */
#define PORT_CSC (1<<1) /* connect status change */
#define PORT_CONNECT (1<<0) /* device connected */
#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
} __attribute__ ((packed));
/* Appendix C, Debug port ... intended for use with special "debug devices"
* that can help if there's no serial console. (nonstandard enumeration.)
*/
struct ehci_dbg_port {
u32 control;
#define DBGP_OWNER (1<<30)
#define DBGP_ENABLED (1<<28)
#define DBGP_DONE (1<<16)
#define DBGP_INUSE (1<<10)
#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
# define DBGP_ERR_BAD 1
# define DBGP_ERR_SIGNAL 2
#define DBGP_ERROR (1<<6)
#define DBGP_GO (1<<5)
#define DBGP_OUT (1<<4)
#define DBGP_LEN(x) (((x)>>0)&0x0f)
u32 pids;
#define DBGP_PID_GET(x) (((x)>>16)&0xff)
#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
u32 data03;
u32 data47;
u32 address;
#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
} __attribute__ ((packed));
#define QTD_NEXT(dma) cpu_to_le32((u32)dma)
/*
* EHCI Specification 0.95 Section 3.5
* QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
* used with control, bulk, and interrupt transfers.
*/
struct ehci_qtd {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.5.1 */
__le32 hw_alt_next; /* see EHCI 3.5.2 */
__le32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
#define QTD_CERR(tok) (((tok)>>10) & 0x3)
#define QTD_PID(tok) (((tok)>>8) & 0x3)
#define QTD_STS_ACTIVE (1 << 7) /* HC may execute this */
#define QTD_STS_HALT (1 << 6) /* halted on error */
#define QTD_STS_DBE (1 << 5) /* data buffer error (in HC) */
#define QTD_STS_BABBLE (1 << 4) /* device was babbling (qtd halted) */
#define QTD_STS_XACT (1 << 3) /* device gave illegal response */
#define QTD_STS_MMF (1 << 2) /* incomplete split transaction */
#define QTD_STS_STS (1 << 1) /* split transaction state */
#define QTD_STS_PING (1 << 0) /* issue PING? */
__le32 hw_buf[5]; /* see EHCI 3.5.4 */
__le32 hw_buf_hi[5]; /* Appendix B */
/* the rest is HCD-private */
dma_addr_t qtd_dma; /* qtd address */
struct list_head qtd_list; /* sw qtd list */
struct urb *urb; /* qtd's urb */
size_t length; /* length of buffer */
u32 qtd_buffer_len;
void *buffer;
dma_addr_t buffer_dma;
void *transfer_buffer;
void *transfer_dma;
} __attribute__ ((aligned(32)));
/* mask NakCnt+T in qh->hw_alt_next */
#define QTD_MASK __constant_cpu_to_le32 (~0x1f)
#define IS_SHORT_READ(token) (QTD_LENGTH(token) != 0 && QTD_PID(token) == 1)
/* Type tag from {qh, itd, sitd, fstn}->hw_next */
#define Q_NEXT_TYPE(dma) ((dma) & __constant_cpu_to_le32 (3 << 1))
/* values for that type tag */
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
/* for periodic/async schedules and qtd lists, mark end of list */
#define EHCI_LIST_END __constant_cpu_to_le32(1) /* "null pointer" to hw */
/*
* Entries in periodic shadow table are pointers to one of four kinds
* of data structure. That's dictated by the hardware; a type tag is
* encoded in the low bits of the hardware's periodic schedule. Use
* Q_NEXT_TYPE to get the tag.
*
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
struct ehci_qh *qh; /* Q_TYPE_QH */
__le32 *hw_next; /* (all types) */
void *ptr;
};
/*
* EHCI Specification 0.95 Section 3.6
* QH: describes control/bulk/interrupt endpoints
* See Fig 3-7 "Queue Head Structure Layout".
*
* These appear in both the async and (for interrupt) periodic schedules.
*/
struct ehci_qh {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.6.1 */
__le32 hw_info1; /* see EHCI 3.6.2 */
#define QH_HEAD 0x00008000
__le32 hw_info2; /* see EHCI 3.6.2 */
#define QH_SMASK 0x000000ff
#define QH_CMASK 0x0000ff00
#define QH_HUBADDR 0x007f0000
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__le32 hw_qtd_next;
__le32 hw_alt_next;
__le32 hw_token;
__le32 hw_buf[5];
__le32 hw_buf_hi[5];
/* the rest is HCD-private */
dma_addr_t qh_dma; /* address of qh */
union ehci_shadow qh_next; /* ptr to qh; or periodic */
struct list_head qtd_list; /* sw qtd list */
struct ehci_qtd *dummy;
struct ehci_qh *reclaim; /* next to reclaim */
struct oxu_hcd *oxu;
struct kref kref;
unsigned stamp;
u8 qh_state;
#define QH_STATE_LINKED 1 /* HC sees this */
#define QH_STATE_UNLINK 2 /* HC may still see this */
#define QH_STATE_IDLE 3 /* HC doesn't see this */
#define QH_STATE_UNLINK_WAIT 4 /* LINKED and on reclaim q */
#define QH_STATE_COMPLETING 5 /* don't touch token.HALT */
/* periodic schedule info */
u8 usecs; /* intr bandwidth */
u8 gap_uf; /* uframes split/csplit gap */
u8 c_usecs; /* ... split completion bw */
u16 tt_usecs; /* tt downstream bandwidth */
unsigned short period; /* polling interval */
unsigned short start; /* where polling starts */
#define NO_FRAME ((unsigned short)~0) /* pick new start */
struct usb_device *dev; /* access to TT */
} __attribute__ ((aligned(32)));
/*
* Proper OXU210HP structs
*/
#define OXU_OTG_CORE_OFFSET 0x00400
#define OXU_OTG_CAP_OFFSET (OXU_OTG_CORE_OFFSET + 0x100)
#define OXU_SPH_CORE_OFFSET 0x00800
#define OXU_SPH_CAP_OFFSET (OXU_SPH_CORE_OFFSET + 0x100)
#define OXU_OTG_MEM 0xE000
#define OXU_SPH_MEM 0x16000
/* Only how many elements & element structure are specifies here. */
/* 2 host controllers are enabled - total size <= 28 kbytes */
#define DEFAULT_I_TDPS 1024
#define QHEAD_NUM 16
#define QTD_NUM 32
#define SITD_NUM 8
#define MURB_NUM 8
#define BUFFER_NUM 8
#define BUFFER_SIZE 512
struct oxu_info {
struct usb_hcd *hcd[2];
};
struct oxu_buf {
u8 buffer[BUFFER_SIZE];
} __attribute__ ((aligned(BUFFER_SIZE)));
struct oxu_onchip_mem {
struct oxu_buf db_pool[BUFFER_NUM];
u32 frame_list[DEFAULT_I_TDPS];
struct ehci_qh qh_pool[QHEAD_NUM];
struct ehci_qtd qtd_pool[QTD_NUM];
} __attribute__ ((aligned(4 << 10)));
#define EHCI_MAX_ROOT_PORTS 15 /* see HCS_N_PORTS */
struct oxu_murb {
struct urb urb;
struct urb *main;
u8 last;
};
struct oxu_hcd { /* one per controller */
unsigned int is_otg:1;
u8 qh_used[QHEAD_NUM];
u8 qtd_used[QTD_NUM];
u8 db_used[BUFFER_NUM];
u8 murb_used[MURB_NUM];
struct oxu_onchip_mem __iomem *mem;
spinlock_t mem_lock;
struct timer_list urb_timer;
struct ehci_caps __iomem *caps;
struct ehci_regs __iomem *regs;
__u32 hcs_params; /* cached register copy */
spinlock_t lock;
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
unsigned reclaim_ready:1;
unsigned scanning:1;
/* periodic schedule support */
unsigned periodic_size;
__le32 *periodic; /* hw periodic table */
dma_addr_t periodic_dma;
unsigned i_thresh; /* uframes HC might cache */
union ehci_shadow *pshadow; /* mirror hw periodic table */
int next_uframe; /* scan periodic, start here */
unsigned periodic_sched; /* periodic activity count */
/* per root hub port */
unsigned long reset_done[EHCI_MAX_ROOT_PORTS];
/* bit vectors (one bit per port) */
unsigned long bus_suspended; /* which ports were
* already suspended at the
* start of a bus suspend
*/
unsigned long companion_ports;/* which ports are dedicated
* to the companion controller
*/
struct timer_list watchdog;
unsigned long actions;
unsigned stamp;
unsigned long next_statechange;
u32 command;
/* SILICON QUIRKS */
struct list_head urb_list; /* this is the head to urb
* queue that didn't get enough
* resources
*/
struct oxu_murb *murb_pool; /* murb per split big urb */
unsigned urb_len;
u8 sbrn; /* packed release number */
};
#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
#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 */
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
#include <linux/oxu210hp.h>

View file

@ -172,9 +172,9 @@ static void __devinit quirk_usb_handoff_ohci(struct pci_dev *pdev)
if (!mmio_resource_enabled(pdev, 0))
return;
base = ioremap_nocache(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (base == NULL) return;
base = pci_ioremap_bar(pdev, 0);
if (base == NULL)
return;
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
#ifndef __hppa__
@ -221,9 +221,9 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
if (!mmio_resource_enabled(pdev, 0))
return;
base = ioremap_nocache(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (base == NULL) return;
base = pci_ioremap_bar(pdev, 0);
if (base == NULL)
return;
cap_length = readb(base);
op_reg_base = base + cap_length;
@ -271,7 +271,7 @@ static void __devinit quirk_usb_disable_ehci(struct pci_dev *pdev)
/* if boot firmware now owns EHCI, spin till
* it hands it over.
*/
msec = 5000;
msec = 1000;
while ((cap & EHCI_USBLEGSUP_BIOS) && (msec > 0)) {
tried_handoff = 1;
msleep(10);

View file

@ -2275,7 +2275,6 @@ static int __init_or_module r8a66597_remove(struct platform_device *pdev)
return 0;
}
#define resource_len(r) (((r)->end - (r)->start) + 1)
static int __init r8a66597_probe(struct platform_device *pdev)
{
#if defined(CONFIG_SUPERH_ON_CHIP_R8A66597) && defined(CONFIG_HAVE_CLK)
@ -2296,11 +2295,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
goto clean_up;
}
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
(char *)hcd_name);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
ret = -ENODEV;
dev_err(&pdev->dev, "platform_get_resource_byname error.\n");
dev_err(&pdev->dev, "platform_get_resource error.\n");
goto clean_up;
}
@ -2315,7 +2313,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
irq = ires->start;
irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
reg = ioremap(res->start, resource_len(res));
reg = ioremap(res->start, resource_size(res));
if (reg == NULL) {
ret = -ENOMEM;
dev_err(&pdev->dev, "ioremap error.\n");

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