提交 50f732ee 编写于 作者: L Linus Torvalds

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

* master.kernel.org:/pub/scm/linux/kernel/git/gregkh/usb-2.6: (78 commits)
  USB: update MAINAINERS and CREDITS for Freescale USB driver
  USB: update gadget files for fsl_usb2_udc driver
  USB: add Freescale high-speed USB SOC device controller driver
  USB: quirk for broken suspend of IT8152F/G
  USB: iowarrior.c: timeouts too small in usb_control_msg calls
  USB: dell device id for option.c
  USB: Remove Huawei unusual_devs entry
  USB: CP2101 New Device IDs
  USB: add picdem device to ldusb
  usbfs micro optimitation
  USB: remove ancient/broken CRIS hcd
  usb ethernet gadget, workaround network stack API glitch
  USB: add "busnum" attribute for USB devices
  USB: cxacru: ADSL state management
  usbatm: Detect usb device shutdown and ignore failed urbs
  USB: Remove duplicate define of OHCI_QUIRK_ZFMICRO
  USB: BandRich BandLuxe HSDPA Data Card Driver
  USB gadget rndis: fix struct rndis_packet_msg_type unaligned bug
  USB Elan FTDI: check for driver registration status
  USB: sierra: add more checks on shutdown
  ...
......@@ -3301,6 +3301,14 @@ S: 12725 SW Millikan Way, Suite 400
S: Beaverton, Oregon 97005
S: USA
N: Li Yang
E: leoli@freescale.com
D: Freescale Highspeed USB device driver
D: Freescale QE SoC support and Ethernet driver
S: B-1206 Jingmao Guojigongyu
S: 16 Baliqiao Nanjie, Beijing 101100
S: People's Repulic of China
N: Marcelo Tosatti
E: marcelo@kvack.org
D: v2.4 kernel maintainer
......
What: /sys/bus/usb/devices/.../power/autosuspend
Date: March 2007
KernelVersion: 2.6.21
Contact: Alan Stern <stern@rowland.harvard.edu>
Description:
Each USB device directory will contain a file named
power/autosuspend. This file holds the time (in seconds)
the device must be idle before it will be autosuspended.
0 means the device will be autosuspended as soon as
possible. Negative values will prevent the device from
being autosuspended at all, and writing a negative value
will resume the device if it is already suspended.
The autosuspend delay for newly-created devices is set to
the value of the usbcore.autosuspend module parameter.
What: /sys/bus/usb/devices/.../power/level
Date: March 2007
KernelVersion: 2.6.21
Contact: Alan Stern <stern@rowland.harvard.edu>
Description:
Each USB device directory will contain a file named
power/level. This file holds a power-level setting for
the device, one of "on", "auto", or "suspend".
"on" means that the device is not allowed to autosuspend,
although normal suspends for system sleep will still
be honored. "auto" means the device will autosuspend
and autoresume in the usual manner, according to the
capabilities of its driver. "suspend" means the device
is forced into a suspended state and it will not autoresume
in response to I/O requests. However remote-wakeup requests
from the device may still be enabled (the remote-wakeup
setting is controlled separately by the power/wakeup
attribute).
During normal use, devices should be left in the "auto"
level. The other levels are meant for administrative uses.
If you want to suspend a device immediately but leave it
free to wake up in response to I/O requests, you should
write "0" to power/autosuspend.
......@@ -1792,7 +1792,7 @@ and is between 256 and 4096 characters. It is defined in the file
for newly-detected USB devices (default 2). This
is the time required before an idle device will be
autosuspended. Devices for which the delay is set
to 0 won't be autosuspended at all.
to a negative value won't be autosuspended at all.
usbhid.mousepoll=
[USBHID] The interval which mice are to be polled at.
......
......@@ -16,7 +16,7 @@ situation as with tcpdump.
Unlike the packet socket, usbmon has an interface which provides traces
in a text format. This is used for two purposes. First, it serves as a
common trace exchange format for tools while most sophisticated formats
common trace exchange format for tools while more sophisticated formats
are finalized. Second, humans can read it in case tools are not available.
To collect a raw text trace, execute following steps.
......@@ -34,7 +34,7 @@ if usbmon is built into the kernel.
Verify that bus sockets are present.
# ls /sys/kernel/debug/usbmon
1s 1t 2s 2t 3s 3t 4s 4t
1s 1t 1u 2s 2t 2u 3s 3t 3u 4s 4t 4u
#
2. Find which bus connects to the desired device
......@@ -54,7 +54,7 @@ Bus=03 means it's bus 3.
3. Start 'cat'
# cat /sys/kernel/debug/usbmon/3t > /tmp/1.mon.out
# cat /sys/kernel/debug/usbmon/3u > /tmp/1.mon.out
This process will be reading until killed. Naturally, the output can be
redirected to a desirable location. This is preferred, because it is going
......@@ -75,46 +75,80 @@ that the file size is not excessive for your favourite editor.
* Raw text data format
The '1t' type data consists of a stream of events, such as URB submission,
Two formats are supported currently: the original, or '1t' format, and
the '1u' format. The '1t' format is deprecated in kernel 2.6.21. The '1u'
format adds a few fields, such as ISO frame descriptors, interval, etc.
It produces slightly longer lines, but otherwise is a perfect superset
of '1t' format.
If it is desired to recognize one from the other in a program, look at the
"address" word (see below), where '1u' format adds a bus number. If 2 colons
are present, it's the '1t' format, otherwise '1u'.
Any text format data consists of a stream of events, such as URB submission,
URB callback, submission error. Every event is a text line, which consists
of whitespace separated words. The number or position of words may depend
on the event type, but there is a set of words, common for all types.
Here is the list of words, from left to right:
- URB Tag. This is used to identify URBs is normally a kernel mode address
of the URB structure in hexadecimal.
- Timestamp in microseconds, a decimal number. The timestamp's resolution
depends on available clock, and so it can be much worse than a microsecond
(if the implementation uses jiffies, for example).
- Event Type. This type refers to the format of the event, not URB type.
Available types are: S - submission, C - callback, E - submission error.
- "Pipe". The pipe concept is deprecated. This is a composite word, used to
be derived from information in pipes. It consists of three fields, separated
by colons: URB type and direction, Device address, Endpoint number.
- "Address" word (formerly a "pipe"). It consists of four fields, separated by
colons: URB type and direction, Bus number, Device address, Endpoint number.
Type and direction are encoded with two bytes in the following manner:
Ci Co Control input and output
Zi Zo Isochronous input and output
Ii Io Interrupt input and output
Bi Bo Bulk input and output
Device address and Endpoint number are 3-digit and 2-digit (respectively)
decimal numbers, with leading zeroes.
- URB Status. In most cases, this field contains a number, sometimes negative,
which represents a "status" field of the URB. This field makes no sense for
submissions, but is present anyway to help scripts with parsing. When an
error occurs, the field contains the error code. In case of a submission of
a Control packet, this field contains a Setup Tag instead of an error code.
It is easy to tell whether the Setup Tag is present because it is never a
number. Thus if scripts find a number in this field, they proceed to read
Data Length. If they find something else, like a letter, they read the setup
packet before reading the Data Length.
Bus number, Device address, and Endpoint are decimal numbers, but they may
have leading zeros, for the sake of human readers.
- URB Status word. This is either a letter, or several numbers separated
by colons: URB status, interval, start frame, and error count. Unlike the
"address" word, all fields save the status are optional. Interval is printed
only for interrupt and isochronous URBs. Start frame is printed only for
isochronous URBs. Error count is printed only for isochronous callback
events.
The status field is a decimal number, sometimes negative, which represents
a "status" field of the URB. This field makes no sense for submissions, but
is present anyway to help scripts with parsing. When an error occurs, the
field contains the error code.
In case of a submission of a Control packet, this field contains a Setup Tag
instead of an group of numbers. It is easy to tell whether the Setup Tag is
present because it is never a number. Thus if scripts find a set of numbers
in this word, they proceed to read Data Length (except for isochronous URBs).
If they find something else, like a letter, they read the setup packet before
reading the Data Length or isochronous descriptors.
- Setup packet, if present, consists of 5 words: one of each for bmRequestType,
bRequest, wValue, wIndex, wLength, as specified by the USB Specification 2.0.
These words are safe to decode if Setup Tag was 's'. Otherwise, the setup
packet was present, but not captured, and the fields contain filler.
- Number of isochronous frame descriptors and descriptors themselves.
If an Isochronous transfer event has a set of descriptors, a total number
of them in an URB is printed first, then a word per descriptor, up to a
total of 5. The word consists of 3 colon-separated decimal numbers for
status, offset, and length respectively. For submissions, initial length
is reported. For callbacks, actual length is reported.
- Data Length. For submissions, this is the requested length. For callbacks,
this is the actual length.
- Data tag. The usbmon may not always capture data, even if length is nonzero.
The data words are present only if this tag is '='.
- Data words follow, in big endian hexadecimal format. Notice that they are
not machine words, but really just a byte stream split into words to make
it easier to read. Thus, the last word may contain from one to four bytes.
......@@ -153,20 +187,18 @@ class ParsedLine {
}
}
This format may be changed in the future.
Examples:
An input control transfer to get a port status.
d5ea89a0 3575914555 S Ci:001:00 s a3 00 0000 0003 0004 4 <
d5ea89a0 3575914560 C Ci:001:00 0 4 = 01050000
d5ea89a0 3575914555 S Ci:1:001:0 s a3 00 0000 0003 0004 4 <
d5ea89a0 3575914560 C Ci:1:001:0 0 4 = 01050000
An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper
to a storage device at address 5:
dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
dd65f0e8 4128379808 C Bo:005:02 0 31 >
dd65f0e8 4128379752 S Bo:1:005:2 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
dd65f0e8 4128379808 C Bo:1:005:2 0 31 >
* Raw binary format and API
......
......@@ -981,6 +981,11 @@ M: mhw@wittsend.com
W: http://www.wittsend.com/computone.html
S: Maintained
CONEXANT ACCESSRUNNER USB DRIVER
P: Simon Arlott
M: cxacru@fire.lp0.eu
S: Maintained
COSA/SRP SYNC SERIAL DRIVER
P: Jan "Yenya" Kasprzak
M: kas@fi.muni.cz
......@@ -1389,6 +1394,13 @@ L: linuxppc-embedded@ozlabs.org
L: netdev@vger.kernel.org
S: Maintained
FREESCALE HIGHSPEED USB DEVICE DRIVER
P: Li Yang
M: leoli@freescale.com
L: linux-usb-devel@lists.sourceforge.net
L: linuxppc-embedded@ozlabs.org
S: Maintained
FILE LOCKING (flock() and fcntl()/lockf())
P: Matthew Wilcox
M: matthew@wil.cx
......
......@@ -2132,10 +2132,13 @@ static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
if ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_BULK) {
/* BULK in or out? */
if (ep->bEndpointAddress & USB_DIR_IN)
ep_in = ep;
else
ep_out = ep;
if (ep->bEndpointAddress & USB_DIR_IN) {
if (ep_in == NULL)
ep_in = ep;
} else {
if (ep_out == NULL)
ep_out = ep;
}
}
}
......
......@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_ETRAX_USB_HOST) += host/
obj-$(CONFIG_USB_OHCI_AT91) += host/
obj-$(CONFIG_USB_ACM) += class/
......
......@@ -4,6 +4,7 @@
*
* Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
* Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
* Copyright (C) 2007 Simon Arlott
*
* 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
......@@ -34,14 +35,14 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/device.h> /* FIXME: linux/firmware.h should include it itself */
#include <linux/device.h>
#include <linux/firmware.h>
#include <linux/mutex.h>
#include "usbatm.h"
#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands"
#define DRIVER_VERSION "0.2"
#define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
#define DRIVER_VERSION "0.3"
#define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver"
static const char cxacru_driver_name[] = "cxacru";
......@@ -64,7 +65,7 @@ static const char cxacru_driver_name[] = "cxacru";
#define SDRAM_ENA 0x1
#define CMD_TIMEOUT 2000 /* msecs */
#define POLL_INTERVAL 5000 /* msecs */
#define POLL_INTERVAL 1 /* secs */
/* commands for interaction with the modem through the control channel before
* firmware is loaded */
......@@ -146,6 +147,13 @@ enum cxacru_info_idx {
CXINF_MAX = 0x1c,
};
enum cxacru_poll_state {
CXPOLL_STOPPING,
CXPOLL_STOPPED,
CXPOLL_POLLING,
CXPOLL_SHUTDOWN
};
struct cxacru_modem_type {
u32 pll_f_clk;
u32 pll_b_clk;
......@@ -158,7 +166,12 @@ struct cxacru_data {
const struct cxacru_modem_type *modem_type;
int line_status;
struct mutex adsl_state_serialize;
int adsl_status;
struct delayed_work poll_work;
u32 card_info[CXINF_MAX];
struct mutex poll_state_serialize;
int poll_state;
/* contol handles */
struct mutex cm_serialize;
......@@ -170,6 +183,275 @@ struct cxacru_data {
struct completion snd_done;
};
static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
u8 *wdata, int wsize, u8 *rdata, int rsize);
static void cxacru_poll_status(struct work_struct *work);
/* Card info exported through sysfs */
#define CXACRU__ATTR_INIT(_name) \
static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
#define CXACRU_CMD_INIT(_name) \
static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
#define CXACRU_ATTR_INIT(_value, _type, _name) \
static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
struct device_attribute *attr, char *buf) \
{ \
struct usb_interface *intf = to_usb_interface(dev); \
struct usbatm_data *usbatm_instance = usb_get_intfdata(intf); \
struct cxacru_data *instance = usbatm_instance->driver_data; \
return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
} \
CXACRU__ATTR_INIT(_name)
#define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
#define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
#define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%u\n", value);
}
static ssize_t cxacru_sysfs_showattr_s8(s8 value, char *buf)
{
return snprintf(buf, PAGE_SIZE, "%d\n", value);
}
static ssize_t cxacru_sysfs_showattr_dB(s16 value, char *buf)
{
if (unlikely(value < 0)) {
return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
value / 100, -value % 100);
} else {
return snprintf(buf, PAGE_SIZE, "%d.%02u\n",
value / 100, value % 100);
}
}
static ssize_t cxacru_sysfs_showattr_bool(u32 value, char *buf)
{
switch (value) {
case 0: return snprintf(buf, PAGE_SIZE, "no\n");
case 1: return snprintf(buf, PAGE_SIZE, "yes\n");
default: return 0;
}
}
static ssize_t cxacru_sysfs_showattr_LINK(u32 value, char *buf)
{
switch (value) {
case 1: return snprintf(buf, PAGE_SIZE, "not connected\n");
case 2: return snprintf(buf, PAGE_SIZE, "connected\n");
case 3: return snprintf(buf, PAGE_SIZE, "lost\n");
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
}
}
static ssize_t cxacru_sysfs_showattr_LINE(u32 value, char *buf)
{
switch (value) {
case 0: return snprintf(buf, PAGE_SIZE, "down\n");
case 1: return snprintf(buf, PAGE_SIZE, "attempting to activate\n");
case 2: return snprintf(buf, PAGE_SIZE, "training\n");
case 3: return snprintf(buf, PAGE_SIZE, "channel analysis\n");
case 4: return snprintf(buf, PAGE_SIZE, "exchange\n");
case 5: return snprintf(buf, PAGE_SIZE, "up\n");
case 6: return snprintf(buf, PAGE_SIZE, "waiting\n");
case 7: return snprintf(buf, PAGE_SIZE, "initialising\n");
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
}
}
static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
{
switch (value) {
case 0: return 0;
case 1: return snprintf(buf, PAGE_SIZE, "ANSI T1.413\n");
case 2: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.1 (G.DMT)\n");
case 3: return snprintf(buf, PAGE_SIZE, "ITU-T G.992.2 (G.LITE)\n");
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
}
}
/*
* This could use MAC_ADDRESS_HIGH and MAC_ADDRESS_LOW, but since
* this data is already in atm_dev there's no point.
*
* MAC_ADDRESS_HIGH = 0x????5544
* MAC_ADDRESS_LOW = 0x33221100
* Where 00-55 are bytes 0-5 of the MAC.
*/
static ssize_t cxacru_sysfs_show_mac_address(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
struct atm_dev *atm_dev = usbatm_instance->atm_dev;
return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
atm_dev->esi[0], atm_dev->esi[1], atm_dev->esi[2],
atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
}
static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
struct cxacru_data *instance = usbatm_instance->driver_data;
u32 value = instance->card_info[CXINF_LINE_STARTABLE];
switch (value) {
case 0: return snprintf(buf, PAGE_SIZE, "running\n");
case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
}
}
static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
struct cxacru_data *instance = usbatm_instance->driver_data;
int ret;
int poll = -1;
char str_cmd[8];
int len = strlen(buf);
if (!capable(CAP_NET_ADMIN))
return -EACCES;
ret = sscanf(buf, "%7s", str_cmd);
if (ret != 1)
return -EINVAL;
ret = 0;
if (mutex_lock_interruptible(&instance->adsl_state_serialize))
return -ERESTARTSYS;
if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
if (ret < 0) {
atm_err(usbatm_instance, "change adsl state:"
" CHIP_ADSL_LINE_STOP returned %d\n", ret);
ret = -EIO;
} else {
ret = len;
poll = CXPOLL_STOPPED;
}
}
/* Line status is only updated every second
* and the device appears to only react to
* START/STOP every second too. Wait 1.5s to
* be sure that restart will have an effect. */
if (!strcmp(str_cmd, "restart"))
msleep(1500);
if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
if (ret < 0) {
atm_err(usbatm_instance, "change adsl state:"
" CHIP_ADSL_LINE_START returned %d\n", ret);
ret = -EIO;
} else {
ret = len;
poll = CXPOLL_POLLING;
}
}
if (!strcmp(str_cmd, "poll")) {
ret = len;
poll = CXPOLL_POLLING;
}
if (ret == 0) {
ret = -EINVAL;
poll = -1;
}
if (poll == CXPOLL_POLLING) {
mutex_lock(&instance->poll_state_serialize);
switch (instance->poll_state) {
case CXPOLL_STOPPED:
/* start polling */
instance->poll_state = CXPOLL_POLLING;
break;
case CXPOLL_STOPPING:
/* abort stop request */
instance->poll_state = CXPOLL_POLLING;
case CXPOLL_POLLING:
case CXPOLL_SHUTDOWN:
/* don't start polling */
poll = -1;
}
mutex_unlock(&instance->poll_state_serialize);
} else if (poll == CXPOLL_STOPPED) {
mutex_lock(&instance->poll_state_serialize);
/* request stop */
if (instance->poll_state == CXPOLL_POLLING)
instance->poll_state = CXPOLL_STOPPING;
mutex_unlock(&instance->poll_state_serialize);
}
mutex_unlock(&instance->adsl_state_serialize);
if (poll == CXPOLL_POLLING)
cxacru_poll_status(&instance->poll_work.work);
return ret;
}
/*
* All device attributes are included in CXACRU_ALL_FILES
* so that the same list can be used multiple times:
* INIT (define the device attributes)
* CREATE (create all the device files)
* REMOVE (remove all the device files)
*
* With the last two being defined as needed in the functions
* they are used in before calling CXACRU_ALL_FILES()
*/
#define CXACRU_ALL_FILES(_action) \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_RATE, u32, downstream_rate); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_RATE, u32, upstream_rate); \
CXACRU_ATTR_##_action(CXINF_LINK_STATUS, LINK, link_status); \
CXACRU_ATTR_##_action(CXINF_LINE_STATUS, LINE, line_status); \
CXACRU__ATTR_##_action( mac_address); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_SNR_MARGIN, dB, upstream_snr_margin); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_SNR_MARGIN, dB, downstream_snr_margin); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_ATTENUATION, dB, upstream_attenuation); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_ATTENUATION, dB, downstream_attenuation); \
CXACRU_ATTR_##_action(CXINF_TRANSMITTER_POWER, s8, transmitter_power); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_BITS_PER_FRAME, u32, upstream_bits_per_frame); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_BITS_PER_FRAME, u32, downstream_bits_per_frame); \
CXACRU_ATTR_##_action(CXINF_STARTUP_ATTEMPTS, u32, startup_attempts); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_CRC_ERRORS, u32, upstream_crc_errors); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_CRC_ERRORS, u32, downstream_crc_errors); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_FEC_ERRORS, u32, upstream_fec_errors); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_FEC_ERRORS, u32, downstream_fec_errors); \
CXACRU_ATTR_##_action(CXINF_UPSTREAM_HEC_ERRORS, u32, upstream_hec_errors); \
CXACRU_ATTR_##_action(CXINF_DOWNSTREAM_HEC_ERRORS, u32, downstream_hec_errors); \
CXACRU_ATTR_##_action(CXINF_LINE_STARTABLE, bool, line_startable); \
CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
CXACRU_CMD_##_action( adsl_state);
CXACRU_ALL_FILES(INIT);
/* the following three functions are stolen from drivers/usb/core/message.c */
static void cxacru_blocking_completion(struct urb *urb)
{
......@@ -347,8 +629,6 @@ static int cxacru_card_status(struct cxacru_data *instance)
return 0;
}
static void cxacru_poll_status(struct work_struct *work);
static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev)
{
......@@ -357,6 +637,7 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
struct atm_dev *atm_dev = usbatm_instance->atm_dev;
*/
int ret;
int start_polling = 1;
dbg("cxacru_atm_start");
......@@ -369,14 +650,35 @@ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
}
/* start ADSL */
mutex_lock(&instance->adsl_state_serialize);
ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
if (ret < 0) {
atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
mutex_unlock(&instance->adsl_state_serialize);
return ret;
}
/* Start status polling */
cxacru_poll_status(&instance->poll_work.work);
mutex_lock(&instance->poll_state_serialize);
switch (instance->poll_state) {
case CXPOLL_STOPPED:
/* start polling */
instance->poll_state = CXPOLL_POLLING;
break;
case CXPOLL_STOPPING:
/* abort stop request */
instance->poll_state = CXPOLL_POLLING;
case CXPOLL_POLLING:
case CXPOLL_SHUTDOWN:
/* don't start polling */
start_polling = 0;
}
mutex_unlock(&instance->poll_state_serialize);
mutex_unlock(&instance->adsl_state_serialize);
if (start_polling)
cxacru_poll_status(&instance->poll_work.work);
return 0;
}
......@@ -387,14 +689,46 @@ static void cxacru_poll_status(struct work_struct *work)
u32 buf[CXINF_MAX] = {};
struct usbatm_data *usbatm = instance->usbatm;
struct atm_dev *atm_dev = usbatm->atm_dev;
int keep_polling = 1;
int ret;
ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
if (ret < 0) {
atm_warn(usbatm, "poll status: error %d\n", ret);
if (ret != -ESHUTDOWN)
atm_warn(usbatm, "poll status: error %d\n", ret);
mutex_lock(&instance->poll_state_serialize);
if (instance->poll_state != CXPOLL_SHUTDOWN) {
instance->poll_state = CXPOLL_STOPPED;
if (ret != -ESHUTDOWN)
atm_warn(usbatm, "polling disabled, set adsl_state"
" to 'start' or 'poll' to resume\n");
}
mutex_unlock(&instance->poll_state_serialize);
goto reschedule;
}
memcpy(instance->card_info, buf, sizeof(instance->card_info));
if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
instance->adsl_status = buf[CXINF_LINE_STARTABLE];
switch (instance->adsl_status) {
case 0:
atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
break;
case 1:
atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
break;
default:
atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
break;
}
}
if (instance->line_status == buf[CXINF_LINE_STATUS])
goto reschedule;
......@@ -449,7 +783,20 @@ static void cxacru_poll_status(struct work_struct *work)
break;
}
reschedule:
schedule_delayed_work(&instance->poll_work, msecs_to_jiffies(POLL_INTERVAL));
mutex_lock(&instance->poll_state_serialize);
if (instance->poll_state == CXPOLL_STOPPING &&
instance->adsl_status == 1 && /* stopped */
instance->line_status == 0) /* down */
instance->poll_state = CXPOLL_STOPPED;
if (instance->poll_state == CXPOLL_STOPPED)
keep_polling = 0;
mutex_unlock(&instance->poll_state_serialize);
if (keep_polling)
schedule_delayed_work(&instance->poll_work,
round_jiffies_relative(POLL_INTERVAL*HZ));
}
static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
......@@ -684,6 +1031,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
instance->usbatm = usbatm_instance;
instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
memset(instance->card_info, 0, sizeof(instance->card_info));
mutex_init(&instance->poll_state_serialize);
instance->poll_state = CXPOLL_STOPPED;
instance->line_status = -1;
instance->adsl_status = -1;
mutex_init(&instance->adsl_state_serialize);
instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
if (!instance->rcv_buf) {
......@@ -710,6 +1065,13 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
goto fail;
}
#define CXACRU_DEVICE_CREATE_FILE(_name) \
ret = device_create_file(&intf->dev, &dev_attr_##_name); \
if (unlikely(ret)) \
goto fail_sysfs;
CXACRU_ALL_FILES(CREATE);
#undef CXACRU_DEVICE_CREATE_FILE
usb_fill_int_urb(instance->rcv_urb,
usb_dev, usb_rcvintpipe(usb_dev, CXACRU_EP_CMD),
instance->rcv_buf, PAGE_SIZE,
......@@ -730,6 +1092,14 @@ static int cxacru_bind(struct usbatm_data *usbatm_instance,
return 0;
fail_sysfs:
dbg("cxacru_bind: device_create_file failed (%d)\n", ret);
#define CXACRU_DEVICE_REMOVE_FILE(_name) \
device_remove_file(&intf->dev, &dev_attr_##_name);
CXACRU_ALL_FILES(REMOVE);
#undef CXACRU_DEVICE_REVOVE_FILE
fail:
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
......@@ -744,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
struct usb_interface *intf)
{
struct cxacru_data *instance = usbatm_instance->driver_data;
int is_polling = 1;
dbg("cxacru_unbind entered");
......@@ -752,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
return;
}
while (!cancel_delayed_work(&instance->poll_work))
flush_scheduled_work();
mutex_lock(&instance->poll_state_serialize);
BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
/* ensure that status polling continues unless
* it has already stopped */
if (instance->poll_state == CXPOLL_STOPPED)
is_polling = 0;
/* stop polling from being stopped or started */
instance->poll_state = CXPOLL_SHUTDOWN;
mutex_unlock(&instance->poll_state_serialize);
if (is_polling)
cancel_rearming_delayed_work(&instance->poll_work);
usb_kill_urb(instance->snd_urb);
usb_kill_urb(instance->rcv_urb);
......@@ -762,6 +1145,12 @@ static void cxacru_unbind(struct usbatm_data *usbatm_instance,
free_page((unsigned long) instance->snd_buf);
free_page((unsigned long) instance->rcv_buf);
#define CXACRU_DEVICE_REMOVE_FILE(_name) \
device_remove_file(&intf->dev, &dev_attr_##_name);
CXACRU_ALL_FILES(REMOVE);
#undef CXACRU_DEVICE_REVOVE_FILE
kfree(instance);
usbatm_instance->driver_data = NULL;
......
......@@ -274,6 +274,9 @@ static void usbatm_complete(struct urb *urb)
(!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
urb->status != -EILSEQ ))
{
if (urb->status == -ESHUTDOWN)
return;
if (printk_ratelimit())
atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
__func__, urb, urb->status);
......@@ -968,6 +971,14 @@ static int usbatm_atm_init(struct usbatm_data *instance)
/* temp init ATM device, set to 128kbit */
atm_dev->link_rate = 128 * 1000 / 424;
ret = sysfs_create_link(&atm_dev->class_dev.kobj,
&instance->usb_intf->dev.kobj, "device");
if (ret) {
atm_err(instance, "%s: sysfs_create_link failed: %d\n",
__func__, ret);
goto fail_sysfs;
}
if (instance->driver->atm_start && ((ret = instance->driver->atm_start(instance, atm_dev)) < 0)) {
atm_err(instance, "%s: atm_start failed: %d!\n", __func__, ret);
goto fail;
......@@ -986,6 +997,8 @@ static int usbatm_atm_init(struct usbatm_data *instance)
return 0;
fail:
sysfs_remove_link(&atm_dev->class_dev.kobj, "device");
fail_sysfs:
instance->atm_dev = NULL;
atm_dev_deregister(atm_dev); /* usbatm_atm_dev_close will eventually be called */
return ret;
......@@ -1318,8 +1331,10 @@ void usbatm_usb_disconnect(struct usb_interface *intf)
kfree(instance->cell_buf);
/* ATM finalize */
if (instance->atm_dev)
if (instance->atm_dev) {
sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device");
atm_dev_deregister(instance->atm_dev);
}
usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
}
......
......@@ -212,7 +212,41 @@ static int acm_write_start(struct acm *acm)
}
return rc;
}
/*
* attributes exported through sysfs
*/
static ssize_t show_caps
(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct acm *acm = usb_get_intfdata(intf);
return sprintf(buf, "%d", acm->ctrl_caps);
}
static DEVICE_ATTR(bmCapabilities, S_IRUGO, show_caps, NULL);
static ssize_t show_country_codes
(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct acm *acm = usb_get_intfdata(intf);
memcpy(buf, acm->country_codes, acm->country_code_size);
return acm->country_code_size;
}
static DEVICE_ATTR(wCountryCodes, S_IRUGO, show_country_codes, NULL);
static ssize_t show_country_rel_date
(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_interface *intf = to_usb_interface(dev);
struct acm *acm = usb_get_intfdata(intf);
return sprintf(buf, "%d", acm->country_rel_date);
}
static DEVICE_ATTR(iCountryCodeRelDate, S_IRUGO, show_country_rel_date, NULL);
/*
* Interrupt handlers for various ACM device responses
*/
......@@ -514,6 +548,7 @@ static void acm_tty_unregister(struct acm *acm)
usb_free_urb(acm->writeurb);
for (i = 0; i < nr; i++)
usb_free_urb(acm->ru[i].urb);
kfree(acm->country_codes);
kfree(acm);
}
......@@ -761,6 +796,7 @@ static int acm_probe (struct usb_interface *intf,
const struct usb_device_id *id)
{
struct usb_cdc_union_desc *union_header = NULL;
struct usb_cdc_country_functional_desc *cfd = NULL;
char *buffer = intf->altsetting->extra;
int buflen = intf->altsetting->extralen;
struct usb_interface *control_interface;
......@@ -824,8 +860,9 @@ static int acm_probe (struct usb_interface *intf,
union_header = (struct usb_cdc_union_desc *)
buffer;
break;
case USB_CDC_COUNTRY_TYPE: /* maybe somehow export */
break; /* for now we ignore it */
case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
cfd = (struct usb_cdc_country_functional_desc *)buffer;
break;
case USB_CDC_HEADER_TYPE: /* maybe check version */
break; /* for now we ignore it */
case USB_CDC_ACM_TYPE:
......@@ -983,6 +1020,34 @@ static int acm_probe (struct usb_interface *intf,
goto alloc_fail7;
}
usb_set_intfdata (intf, acm);
i = device_create_file(&intf->dev, &dev_attr_bmCapabilities);
if (i < 0)
goto alloc_fail8;
if (cfd) { /* export the country data */
acm->country_codes = kmalloc(cfd->bLength - 4, GFP_KERNEL);
if (!acm->country_codes)
goto skip_countries;
acm->country_code_size = cfd->bLength - 4;
memcpy(acm->country_codes, (u8 *)&cfd->wCountyCode0, cfd->bLength - 4);
acm->country_rel_date = cfd->iCountryCodeRelDate;
i = device_create_file(&intf->dev, &dev_attr_wCountryCodes);
if (i < 0) {
kfree(acm->country_codes);
goto skip_countries;
}
i = device_create_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
if (i < 0) {
kfree(acm->country_codes);
goto skip_countries;
}
}
skip_countries:
usb_fill_int_urb(acm->ctrlurb, usb_dev, usb_rcvintpipe(usb_dev, epctrl->bEndpointAddress),
acm->ctrl_buffer, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval);
acm->ctrlurb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
......@@ -1006,9 +1071,10 @@ static int acm_probe (struct usb_interface *intf,
tty_register_device(acm_tty_driver, minor, &control_interface->dev);
acm_table[minor] = acm;
usb_set_intfdata (intf, acm);
return 0;
return 0;
alloc_fail8:
usb_free_urb(acm->writeurb);
alloc_fail7:
for (i = 0; i < num_rx_buf; i++)
usb_buffer_free(usb_dev, acm->readsize, acm->rb[i].base, acm->rb[i].dma);
......@@ -1027,7 +1093,7 @@ static int acm_probe (struct usb_interface *intf,
static void acm_disconnect(struct usb_interface *intf)
{
struct acm *acm = usb_get_intfdata (intf);
struct acm *acm = usb_get_intfdata(intf);
struct usb_device *usb_dev = interface_to_usbdev(intf);
int i;
......@@ -1041,6 +1107,11 @@ static void acm_disconnect(struct usb_interface *intf)
mutex_unlock(&open_mutex);
return;
}
if (acm->country_codes){
device_remove_file(&intf->dev, &dev_attr_wCountryCodes);
device_remove_file(&intf->dev, &dev_attr_iCountryCodeRelDate);
}
device_remove_file(&intf->dev, &dev_attr_bmCapabilities);
acm->dev = NULL;
usb_set_intfdata(acm->control, NULL);
usb_set_intfdata(acm->data, NULL);
......
......@@ -91,6 +91,9 @@ struct acm {
struct urb *ctrlurb, *writeurb; /* urbs */
u8 *ctrl_buffer; /* buffers of urbs */
dma_addr_t ctrl_dma; /* dma handles of buffers */
u8 *country_codes; /* country codes from device */
unsigned int country_code_size; /* size of this buffer */
unsigned int country_rel_date; /* release date of version */
struct acm_wb wb[ACM_NW];
struct acm_ru ru[ACM_NR];
struct acm_rb rb[ACM_NR];
......
......@@ -31,7 +31,30 @@ config USB_DEVICEFS
For the format of the various /proc/bus/usb/ files, please read
<file:Documentation/usb/proc_usb_info.txt>.
Most users want to say Y here.
Usbfs files can't handle Access Control Lists (ACL), which are the
default way to grant access to USB devices for untrusted users of a
desktop system. The usbfs functionality is replaced by real
device-nodes managed by udev. These nodes live in /dev/bus/usb and
are used by libusb.
config USB_DEVICE_CLASS
bool "USB device class-devices (DEPRECATED)"
depends on USB
default n
---help---
Userspace access to USB devices is granted by device-nodes exported
directly from the usbdev in sysfs. Old versions of the driver
core and udev needed additional class devices to export device nodes.
These additional devices are difficult to handle in userspace, if
information about USB interfaces must be available. One device contains
the device node, the other device contains the interface data. Both
devices are at the same level in sysfs (siblings) and one can't access
the other. The device node created directly by the usbdev is the parent
device of the interface and therefore easily accessible from the interface
event.
This option provides backward compatibility if needed.
config USB_DYNAMIC_MINORS
bool "Dynamic USB minor allocation (EXPERIMENTAL)"
......
......@@ -57,7 +57,6 @@
#define USB_MAXBUS 64
#define USB_DEVICE_MAX USB_MAXBUS * 128
static struct class *usb_device_class;
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
......@@ -514,22 +513,25 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype, unsig
return ret;
}
static struct usb_device *usbdev_lookup_minor(int minor)
static int __match_minor(struct device *dev, void *data)
{
struct device *device;
struct usb_device *udev = NULL;
int minor = *((int *)data);
down(&usb_device_class->sem);
list_for_each_entry(device, &usb_device_class->devices, node) {
if (device->devt == MKDEV(USB_DEVICE_MAJOR, minor)) {
udev = device->platform_data;
break;
}
}
up(&usb_device_class->sem);
if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
return 1;
return 0;
}
return udev;
};
static struct usb_device *usbdev_lookup_by_minor(int minor)
{
struct device *dev;
dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
if (!dev)
return NULL;
put_device(dev);
return container_of(dev, struct usb_device, dev);
}
/*
* file operations
......@@ -548,11 +550,14 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
/* check if we are called from a real node or usbfs */
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_minor(iminor(inode));
dev = usbdev_lookup_by_minor(iminor(inode));
#ifdef CONFIG_USB_DEVICEFS
/* procfs file */
if (!dev)
dev = inode->i_private;
#endif
if (!dev)
goto out;
ret = usb_autoresume_device(dev);
......@@ -575,7 +580,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
ps->disccontext = NULL;
ps->ifclaimed = 0;
security_task_getsecid(current, &ps->secid);
wmb();
smp_wmb();
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
......@@ -1570,7 +1575,7 @@ static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wai
return mask;
}
const struct file_operations usbfs_device_file_operations = {
const struct file_operations usbdev_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
......@@ -1579,50 +1584,53 @@ const struct file_operations usbfs_device_file_operations = {
.release = usbdev_release,
};
static int usbdev_add(struct usb_device *dev)
#ifdef CONFIG_USB_DEVICE_CLASS
static struct class *usb_classdev_class;
static int usb_classdev_add(struct usb_device *dev)
{
int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
dev->usbfs_dev = device_create(usb_device_class, &dev->dev,
dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
MKDEV(USB_DEVICE_MAJOR, minor),
"usbdev%d.%d", dev->bus->busnum, dev->devnum);
if (IS_ERR(dev->usbfs_dev))
return PTR_ERR(dev->usbfs_dev);
if (IS_ERR(dev->usb_classdev))
return PTR_ERR(dev->usb_classdev);
dev->usbfs_dev->platform_data = dev;
return 0;
}
static void usbdev_remove(struct usb_device *dev)
static void usb_classdev_remove(struct usb_device *dev)
{
device_unregister(dev->usbfs_dev);
device_unregister(dev->usb_classdev);
}
static int usbdev_notify(struct notifier_block *self, unsigned long action,
void *dev)
static int usb_classdev_notify(struct notifier_block *self,
unsigned long action, void *dev)
{
switch (action) {
case USB_DEVICE_ADD:
if (usbdev_add(dev))
if (usb_classdev_add(dev))
return NOTIFY_BAD;
break;
case USB_DEVICE_REMOVE:
usbdev_remove(dev);
usb_classdev_remove(dev);
break;
}
return NOTIFY_OK;
}
static struct notifier_block usbdev_nb = {
.notifier_call = usbdev_notify,
.notifier_call = usb_classdev_notify,
};
#endif
static struct cdev usb_device_cdev = {
.kobj = {.name = "usb_device", },
.owner = THIS_MODULE,
};
int __init usbdev_init(void)
int __init usb_devio_init(void)
{
int retval;
......@@ -1632,38 +1640,38 @@ int __init usbdev_init(void)
err("unable to register minors for usb_device");
goto out;
}
cdev_init(&usb_device_cdev, &usbfs_device_file_operations);
cdev_init(&usb_device_cdev, &usbdev_file_operations);
retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX);
if (retval) {
err("unable to get usb_device major %d", USB_DEVICE_MAJOR);
goto error_cdev;
}
usb_device_class = class_create(THIS_MODULE, "usb_device");
if (IS_ERR(usb_device_class)) {
#ifdef CONFIG_USB_DEVICE_CLASS
usb_classdev_class = class_create(THIS_MODULE, "usb_device");
if (IS_ERR(usb_classdev_class)) {
err("unable to register usb_device class");
retval = PTR_ERR(usb_device_class);
goto error_class;
retval = PTR_ERR(usb_classdev_class);
cdev_del(&usb_device_cdev);
usb_classdev_class = NULL;
goto out;
}
usb_register_notify(&usbdev_nb);
#endif
out:
return retval;
error_class:
usb_device_class = NULL;
cdev_del(&usb_device_cdev);
error_cdev:
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
goto out;
}
void usbdev_cleanup(void)
void usb_devio_cleanup(void)
{
#ifdef CONFIG_USB_DEVICE_CLASS
usb_unregister_notify(&usbdev_nb);
class_destroy(usb_device_class);
class_destroy(usb_classdev_class);
#endif
cdev_del(&usb_device_cdev);
unregister_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX);
}
......@@ -574,23 +574,10 @@ static int usb_device_match(struct device *dev, struct device_driver *drv)
}
#ifdef CONFIG_HOTPLUG
/*
* This sends an uevent to userspace, typically helping to load driver
* or other modules, configure the device, and more. Drivers can provide
* a MODULE_DEVICE_TABLE to help with module loading subtasks.
*
* We're called either from khubd (the typical case) or from root hub
* (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
* delays in event delivery. Use sysfs (and DEVPATH) to make sure the
* device (and this configuration!) are still present.
*/
static int usb_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct usb_interface *intf;
struct usb_device *usb_dev;
struct usb_host_interface *alt;
int i = 0;
int length = 0;
......@@ -600,13 +587,11 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
if (is_usb_device(dev)) {
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
alt = NULL;
} else {
intf = to_usb_interface(dev);
else {
struct usb_interface *intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
alt = intf->cur_altsetting;
}
if (usb_dev->devnum < 0) {
......@@ -621,9 +606,7 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
#ifdef CONFIG_USB_DEVICEFS
/* If this is available, userspace programs can directly read
* all the device descriptors we don't tell them about. Or
* even act as usermode drivers.
*
* FIXME reduce hardwired intelligence here
* act as usermode drivers.
*/
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
......@@ -650,44 +633,29 @@ static int usb_uevent(struct device *dev, char **envp, int num_envp,
usb_dev->descriptor.bDeviceProtocol))
return -ENOMEM;
if (!is_usb_device(dev)) {
if (add_uevent_var(envp, num_envp, &i,
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
"BUSNUM=%03d",
usb_dev->bus->busnum))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
}
"DEVNUM=%03d",
usb_dev->devnum))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif /* CONFIG_HOTPLUG */
/**
......@@ -872,8 +840,10 @@ static int usb_resume_device(struct usb_device *udev)
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
if (status == 0)
if (status == 0) {
udev->autoresume_disabled = 0;
udev->dev.power.power_state.event = PM_EVENT_ON;
}
return status;
}
......@@ -962,6 +932,7 @@ static int autosuspend_check(struct usb_device *udev)
{
int i;
struct usb_interface *intf;
unsigned long suspend_time;
/* For autosuspend, fail fast if anything is in use or autosuspend
* is disabled. Also fail if any interfaces require remote wakeup
......@@ -970,9 +941,10 @@ static int autosuspend_check(struct usb_device *udev)
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->pm_usage_cnt > 0)
return -EBUSY;
if (!udev->autosuspend_delay)
if (udev->autosuspend_delay < 0 || udev->autosuspend_disabled)
return -EPERM;
suspend_time = udev->last_busy + udev->autosuspend_delay;
if (udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
......@@ -988,6 +960,24 @@ static int autosuspend_check(struct usb_device *udev)
}
}
}
/* If everything is okay but the device hasn't been idle for long
* enough, queue a delayed autosuspend request.
*/
if (time_after(suspend_time, jiffies)) {
if (!timer_pending(&udev->autosuspend.timer)) {
/* The value of jiffies may change between the
* time_after() comparison above and the subtraction
* below. That's okay; the system behaves sanely
* when a timer is registered for the present moment
* or for the past.
*/
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
suspend_time - jiffies);
}
return -EAGAIN;
}
return 0;
}
......@@ -1033,26 +1023,25 @@ static int autosuspend_check(struct usb_device *udev)
*
* This routine can run only in process context.
*/
int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
static int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
{
int status = 0;
int i = 0;
struct usb_interface *intf;
struct usb_device *parent = udev->parent;
cancel_delayed_work(&udev->autosuspend);
if (udev->state == USB_STATE_NOTATTACHED)
return 0;
if (udev->state == USB_STATE_SUSPENDED)
return 0;
if (udev->state == USB_STATE_NOTATTACHED ||
udev->state == USB_STATE_SUSPENDED)
goto done;
udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
if (udev->auto_pm) {
status = autosuspend_check(udev);
if (status < 0)
return status;
goto done;
}
cancel_delayed_work(&udev->autosuspend);
/* Suspend all the interfaces and then udev itself */
if (udev->actconfig) {
......@@ -1077,6 +1066,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
} else if (parent)
usb_autosuspend_device(parent);
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
......@@ -1109,7 +1099,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
*
* This routine can run only in process context.
*/
int usb_resume_both(struct usb_device *udev)
static int usb_resume_both(struct usb_device *udev)
{
int status = 0;
int i;
......@@ -1117,11 +1107,17 @@ int usb_resume_both(struct usb_device *udev)
struct usb_device *parent = udev->parent;
cancel_delayed_work(&udev->autosuspend);
if (udev->state == USB_STATE_NOTATTACHED)
return -ENODEV;
if (udev->state == USB_STATE_NOTATTACHED) {
status = -ENODEV;
goto done;
}
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
if (udev->auto_pm && udev->autoresume_disabled) {
status = -EPERM;
goto done;
}
if (parent) {
status = usb_autoresume_device(parent);
if (status == 0) {
......@@ -1167,6 +1163,7 @@ int usb_resume_both(struct usb_device *udev)
}
}
done:
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
}
......@@ -1181,20 +1178,34 @@ static int usb_autopm_do_device(struct usb_device *udev, int inc_usage_cnt)
int status = 0;
usb_pm_lock(udev);
udev->auto_pm = 1;
udev->pm_usage_cnt += inc_usage_cnt;
WARN_ON(udev->pm_usage_cnt < 0);
if (inc_usage_cnt >= 0 && udev->pm_usage_cnt > 0) {
udev->auto_pm = 1;
status = usb_resume_both(udev);
if (udev->state == USB_STATE_SUSPENDED)
status = usb_resume_both(udev);
if (status != 0)
udev->pm_usage_cnt -= inc_usage_cnt;
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
udev->autosuspend_delay);
else if (inc_usage_cnt)
udev->last_busy = jiffies;
} else if (inc_usage_cnt <= 0 && udev->pm_usage_cnt <= 0) {
if (inc_usage_cnt)
udev->last_busy = jiffies;
status = usb_suspend_both(udev, PMSG_SUSPEND);
}
usb_pm_unlock(udev);
return status;
}
/* usb_autosuspend_work - callback routine to autosuspend a USB device */
void usb_autosuspend_work(struct work_struct *work)
{
struct usb_device *udev =
container_of(work, struct usb_device, autosuspend.work);
usb_autopm_do_device(udev, 0);
}
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
* @udev: the usb_device to autosuspend
......@@ -1286,15 +1297,20 @@ static int usb_autopm_do_interface(struct usb_interface *intf,
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
udev->auto_pm = 1;
intf->pm_usage_cnt += inc_usage_cnt;
if (inc_usage_cnt >= 0 && intf->pm_usage_cnt > 0) {
udev->auto_pm = 1;
status = usb_resume_both(udev);
if (udev->state == USB_STATE_SUSPENDED)
status = usb_resume_both(udev);
if (status != 0)
intf->pm_usage_cnt -= inc_usage_cnt;
} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
udev->autosuspend_delay);
else if (inc_usage_cnt)
udev->last_busy = jiffies;
} else if (inc_usage_cnt <= 0 && intf->pm_usage_cnt <= 0) {
if (inc_usage_cnt)
udev->last_busy = jiffies;
status = usb_suspend_both(udev, PMSG_SUSPEND);
}
}
usb_pm_unlock(udev);
return status;
......@@ -1353,11 +1369,14 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
* or @intf is unbound. A typical example would be a character-device
* driver when its device file is opened.
*
* The routine increments @intf's usage counter. So long as the counter
* is greater than 0, autosuspend will not be allowed for @intf or its
* usb_device. When the driver is finished using @intf it should call
* usb_autopm_put_interface() to decrement the usage counter and queue
* a delayed autosuspend request (if the counter is <= 0).
*
* The routine increments @intf's usage counter. (However if the
* autoresume fails then the counter is re-decremented.) So long as the
* counter is greater than 0, autosuspend will not be allowed for @intf
* or its usb_device. When the driver is finished using @intf it should
* call usb_autopm_put_interface() to decrement the usage counter and
* queue a delayed autosuspend request (if the counter is <= 0).
*
*
* Note that @intf->pm_usage_cnt is owned by the interface driver. The
* core will not change its value other than the increment and decrement
......@@ -1405,50 +1424,96 @@ int usb_autopm_set_interface(struct usb_interface *intf)
}
EXPORT_SYMBOL_GPL(usb_autopm_set_interface);
#else
void usb_autosuspend_work(struct work_struct *work)
{}
#endif /* CONFIG_USB_SUSPEND */
static int usb_suspend(struct device *dev, pm_message_t message)
/**
* usb_external_suspend_device - external suspend of a USB device and its interfaces
* @udev: the usb_device to suspend
* @msg: Power Management message describing this state transition
*
* This routine handles external suspend requests: ones not generated
* internally by a USB driver (autosuspend) but rather coming from the user
* (via sysfs) or the PM core (system sleep). The suspend will be carried
* out regardless of @udev's usage counter or those of its interfaces,
* and regardless of whether or not remote wakeup is enabled. Of course,
* interface drivers still have the option of failing the suspend (if
* there are unsuspended children, for example).
*
* The caller must hold @udev's device lock.
*/
int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
int status;
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, message);
usb_pm_unlock(udev);
} else
status = 0;
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
usb_pm_unlock(udev);
return status;
}
static int usb_resume(struct device *dev)
/**
* usb_external_resume_device - external resume of a USB device and its interfaces
* @udev: the usb_device to resume
*
* This routine handles external resume requests: ones not generated
* internally by a USB driver (autoresume) but rather coming from the user
* (via sysfs), the PM core (system resume), or the device itself (remote
* wakeup). @udev's usage counter is unaffected.
*
* The caller must hold @udev's device lock.
*/
int usb_external_resume_device(struct usb_device *udev)
{
int status;
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_resume_both(udev);
usb_pm_unlock(udev);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_resume_both(udev);
usb_pm_unlock(udev);
/* Rebind drivers that had no suspend method? */
} else
status = 0;
/* Now that the device is awake, we can start trying to autosuspend
* it again. */
if (status == 0)
usb_try_autosuspend_device(udev);
return status;
}
static int usb_suspend(struct device *dev, pm_message_t message)
{
if (!is_usb_device(dev)) /* Ignore PM for interfaces */
return 0;
return usb_external_suspend_device(to_usb_device(dev), message);
}
static int usb_resume(struct device *dev)
{
struct usb_device *udev;
if (!is_usb_device(dev)) /* Ignore PM for interfaces */
return 0;
udev = to_usb_device(dev);
if (udev->autoresume_disabled)
return -EPERM;
return usb_external_resume_device(udev);
}
#else
#define usb_suspend NULL
#define usb_resume NULL
#endif /* CONFIG_PM */
struct bus_type usb_bus_type = {
.name = "usb",
.match = usb_device_match,
.uevent = usb_uevent,
#ifdef CONFIG_PM
.suspend = usb_suspend,
.resume = usb_resume,
#endif
};
......@@ -37,6 +37,7 @@
#include <asm/irq.h>
#include <asm/byteorder.h>
#include <linux/platform_device.h>
#include <linux/workqueue.h>
#include <linux/usb.h>
......@@ -544,6 +545,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
unsigned long flags;
char buffer[4]; /* Any root hubs with > 31 ports? */
if (unlikely(!hcd->rh_registered))
return;
if (!hcd->uses_new_polling && !hcd->status_urb)
return;
......@@ -1296,14 +1299,26 @@ int hcd_bus_resume (struct usb_bus *bus)
return status;
}
/* Workqueue routine for root-hub remote wakeup */
static void hcd_resume_work(struct work_struct *work)
{
struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work);
struct usb_device *udev = hcd->self.root_hub;
usb_lock_device(udev);
usb_mark_last_busy(udev);
usb_external_resume_device(udev);
usb_unlock_device(udev);
}
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
*
* The USB host controller calls this function when its root hub is
* suspended (with the remote wakeup feature enabled) and a remote
* wakeup request is received. It queues a request for khubd to
* resume the root hub (that is, manage its downstream ports again).
* wakeup request is received. The routine submits a workqueue request
* to resume the root hub (that is, manage its downstream ports again).
*/
void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
{
......@@ -1311,7 +1326,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd)
spin_lock_irqsave (&hcd_root_hub_lock, flags);
if (hcd->rh_registered)
usb_resume_root_hub (hcd->self.root_hub);
queue_work(ksuspend_usb_wq, &hcd->wakeup_work);
spin_unlock_irqrestore (&hcd_root_hub_lock, flags);
}
EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub);
......@@ -1500,6 +1515,9 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
hcd->rh_timer.data = (unsigned long) hcd;
#ifdef CONFIG_PM
INIT_WORK(&hcd->wakeup_work, hcd_resume_work);
#endif
hcd->driver = driver;
hcd->product_desc = (driver->product_desc) ? driver->product_desc :
......@@ -1666,16 +1684,20 @@ void usb_remove_hcd(struct usb_hcd *hcd)
hcd->rh_registered = 0;
spin_unlock_irq (&hcd_root_hub_lock);
#ifdef CONFIG_PM
flush_workqueue(ksuspend_usb_wq);
#endif
mutex_lock(&usb_bus_list_lock);
usb_disconnect(&hcd->self.root_hub);
mutex_unlock(&usb_bus_list_lock);
hcd->poll_rh = 0;
del_timer_sync(&hcd->rh_timer);
hcd->driver->stop(hcd);
hcd->state = HC_STATE_HALT;
hcd->poll_rh = 0;
del_timer_sync(&hcd->rh_timer);
if (hcd->irq >= 0)
free_irq(hcd->irq, hcd);
usb_deregister_bus(&hcd->self);
......
......@@ -68,6 +68,9 @@ struct usb_hcd {
struct timer_list rh_timer; /* drives root-hub polling */
struct urb *status_urb; /* the current status urb */
#ifdef CONFIG_PM
struct work_struct wakeup_work; /* for remote wakeup */
#endif
/*
* hardware info/state
......
......@@ -1367,11 +1367,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
/* export the usbdev device-node for libusb */
udev->dev.devt = MKDEV(USB_DEVICE_MAJOR,
(((udev->bus->busnum-1) * 128) + (udev->devnum-1)));
/* Register the device. The device driver is responsible
* for adding the device files to usbfs and sysfs and for
* configuring the device.
* for adding the device files to sysfs and for configuring
* the device.
*/
err = device_add (&udev->dev);
err = device_add(&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
......@@ -1855,12 +1859,8 @@ static int remote_wakeup(struct usb_device *udev)
usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
status = usb_autoresume_device(udev);
/* Give the interface drivers a chance to do something,
* then autosuspend the device again. */
if (status == 0)
usb_autosuspend_device(udev);
usb_mark_last_busy(udev);
status = usb_external_resume_device(udev);
}
usb_unlock_device(udev);
return status;
......@@ -1984,13 +1984,6 @@ static inline int remote_wakeup(struct usb_device *udev)
#define hub_resume NULL
#endif
void usb_resume_root_hub(struct usb_device *hdev)
{
struct usb_hub *hub = hdev_to_hub(hdev);
kick_khubd(hub);
}
/* USB 2.0 spec, 7.1.7.3 / fig 7-29:
*
......
......@@ -662,7 +662,7 @@ static void usbfs_add_device(struct usb_device *dev)
sprintf (name, "%03d", dev->devnum);
dev->usbfs_dentry = fs_create_file (name, devmode | S_IFREG,
dev->bus->usbfs_dentry, dev,
&usbfs_device_file_operations,
&usbdev_file_operations,
devuid, devgid);
if (dev->usbfs_dentry == NULL) {
err ("error creating usbfs device entry");
......
......@@ -412,10 +412,24 @@ int usb_sg_init (
io->urbs [i]->status = -EINPROGRESS;
io->urbs [i]->actual_length = 0;
/*
* Some systems need to revert to PIO when DMA is temporarily
* unavailable. For their sakes, both transfer_buffer and
* transfer_dma are set when possible. However this can only
* work on systems without HIGHMEM, since DMA buffers located
* in high memory are not directly addressable by the CPU for
* PIO ... so when HIGHMEM is in use, transfer_buffer is NULL
* to prevent stale pointers and to help spot bugs.
*/
if (dma) {
/* hc may use _only_ transfer_dma */
io->urbs [i]->transfer_dma = sg_dma_address (sg + i);
len = sg_dma_len (sg + i);
#ifdef CONFIG_HIGHMEM
io->urbs[i]->transfer_buffer = NULL;
#else
io->urbs[i]->transfer_buffer =
page_address(sg[i].page) + sg[i].offset;
#endif
} else {
/* hc may use _only_ transfer_buffer */
io->urbs [i]->transfer_buffer =
......@@ -1305,7 +1319,7 @@ int usb_reset_configuration(struct usb_device *dev)
return 0;
}
static void release_interface(struct device *dev)
void usb_release_interface(struct device *dev)
{
struct usb_interface *intf = to_usb_interface(dev);
struct usb_interface_cache *intfc =
......@@ -1315,6 +1329,67 @@ static void release_interface(struct device *dev)
kfree(intf);
}
#ifdef CONFIG_HOTPLUG
static int usb_if_uevent(struct device *dev, char **envp, int num_envp,
char *buffer, int buffer_size)
{
struct usb_device *usb_dev;
struct usb_interface *intf;
struct usb_host_interface *alt;
int i = 0;
int length = 0;
if (!dev)
return -ENODEV;
/* driver is often null here; dev_dbg() would oops */
pr_debug ("usb %s: uevent\n", dev->bus_id);
intf = to_usb_interface(dev);
usb_dev = interface_to_usbdev(intf);
alt = intf->cur_altsetting;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"INTERFACE=%d/%d/%d",
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
if (add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
le16_to_cpu(usb_dev->descriptor.idVendor),
le16_to_cpu(usb_dev->descriptor.idProduct),
le16_to_cpu(usb_dev->descriptor.bcdDevice),
usb_dev->descriptor.bDeviceClass,
usb_dev->descriptor.bDeviceSubClass,
usb_dev->descriptor.bDeviceProtocol,
alt->desc.bInterfaceClass,
alt->desc.bInterfaceSubClass,
alt->desc.bInterfaceProtocol))
return -ENOMEM;
envp[i] = NULL;
return 0;
}
#else
static int usb_if_uevent(struct device *dev, char **envp,
int num_envp, char *buffer, int buffer_size)
{
return -ENODEV;
}
#endif /* CONFIG_HOTPLUG */
struct device_type usb_if_device_type = {
.name = "usb_interface",
.release = usb_release_interface,
.uevent = usb_if_uevent,
};
/*
* usb_set_configuration - Makes a particular device setting be current
* @dev: the device whose configuration is being updated
......@@ -1478,8 +1553,8 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
intf->dev.parent = &dev->dev;
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
intf->dev.dma_mask = dev->dev.dma_mask;
intf->dev.release = release_interface;
device_initialize (&intf->dev);
mark_quiesced(intf);
sprintf (&intf->dev.bus_id[0], "%d-%s:%d.%d",
......
......@@ -42,7 +42,7 @@ static void usb_autosuspend_quirk(struct usb_device *udev)
{
#ifdef CONFIG_USB_SUSPEND
/* disable autosuspend, but allow the user to re-enable it via sysfs */
udev->autosuspend_delay = 0;
udev->autosuspend_disabled = 1;
#endif
}
......
......@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/usb.h>
#include "usb.h"
......@@ -116,6 +117,16 @@ show_speed(struct device *dev, struct device_attribute *attr, char *buf)
}
static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
static ssize_t
show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev;
udev = to_usb_device(dev);
return sprintf(buf, "%d\n", udev->bus->busnum);
}
static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
static ssize_t
show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
{
......@@ -165,7 +176,7 @@ show_autosuspend(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
return sprintf(buf, "%u\n", udev->autosuspend_delay / HZ);
return sprintf(buf, "%d\n", udev->autosuspend_delay / HZ);
}
static ssize_t
......@@ -173,38 +184,114 @@ set_autosuspend(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
unsigned value, old;
int value;
if (sscanf(buf, "%u", &value) != 1 || value >= INT_MAX/HZ)
if (sscanf(buf, "%d", &value) != 1 || value >= INT_MAX/HZ ||
value <= - INT_MAX/HZ)
return -EINVAL;
value *= HZ;
old = udev->autosuspend_delay;
udev->autosuspend_delay = value;
if (value > 0 && old == 0)
if (value >= 0)
usb_try_autosuspend_device(udev);
else {
if (usb_autoresume_device(udev) == 0)
usb_autosuspend_device(udev);
}
return count;
}
static DEVICE_ATTR(autosuspend, S_IRUGO | S_IWUSR,
show_autosuspend, set_autosuspend);
static const char on_string[] = "on";
static const char auto_string[] = "auto";
static const char suspend_string[] = "suspend";
static ssize_t
show_level(struct device *dev, struct device_attribute *attr, char *buf)
{
struct usb_device *udev = to_usb_device(dev);
const char *p = auto_string;
if (udev->state == USB_STATE_SUSPENDED) {
if (udev->autoresume_disabled)
p = suspend_string;
} else {
if (udev->autosuspend_disabled)
p = on_string;
}
return sprintf(buf, "%s\n", p);
}
static ssize_t
set_level(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
int len = count;
char *cp;
int rc = 0;
cp = memchr(buf, '\n', count);
if (cp)
len = cp - buf;
usb_lock_device(udev);
/* Setting the flags without calling usb_pm_lock is a subject to
* races, but who cares...
*/
if (len == sizeof on_string - 1 &&
strncmp(buf, on_string, len) == 0) {
udev->autosuspend_disabled = 1;
udev->autoresume_disabled = 0;
rc = usb_external_resume_device(udev);
} 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);
} 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);
} else
rc = -EINVAL;
usb_unlock_device(udev);
return (rc < 0 ? rc : count);
}
static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level);
static char power_group[] = "power";
static int add_power_attributes(struct device *dev)
{
int rc = 0;
if (is_usb_device(dev))
if (is_usb_device(dev)) {
rc = sysfs_add_file_to_group(&dev->kobj,
&dev_attr_autosuspend.attr,
power_group);
if (rc == 0)
rc = sysfs_add_file_to_group(&dev->kobj,
&dev_attr_level.attr,
power_group);
}
return rc;
}
static void remove_power_attributes(struct device *dev)
{
sysfs_remove_file_from_group(&dev->kobj,
&dev_attr_level.attr,
power_group);
sysfs_remove_file_from_group(&dev->kobj,
&dev_attr_autosuspend.attr,
power_group);
......@@ -270,6 +357,7 @@ static struct attribute *dev_attrs[] = {
&dev_attr_bNumConfigurations.attr,
&dev_attr_bMaxPacketSize0.attr,
&dev_attr_speed.attr,
&dev_attr_busnum.attr,
&dev_attr_devnum.attr,
&dev_attr_version.attr,
&dev_attr_maxchild.attr,
......
......@@ -49,12 +49,13 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
/* Workqueue for autosuspend and for remote wakeup of root hubs */
struct workqueue_struct *ksuspend_usb_wq;
#ifdef CONFIG_USB_SUSPEND
static int usb_autosuspend_delay = 2; /* Default delay value,
* in seconds */
module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
module_param_named(autosuspend, usb_autosuspend_delay, int, 0644);
MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
#else
......@@ -196,6 +197,11 @@ static void usb_release_dev(struct device *dev)
kfree(udev);
}
struct device_type usb_device_type = {
.name = "usb_device",
.release = usb_release_dev,
};
#ifdef CONFIG_PM
static int ksuspend_usb_init(void)
......@@ -211,27 +217,6 @@ static void ksuspend_usb_cleanup(void)
destroy_workqueue(ksuspend_usb_wq);
}
#ifdef CONFIG_USB_SUSPEND
/* usb_autosuspend_work - callback routine to autosuspend a USB device */
static void usb_autosuspend_work(struct work_struct *work)
{
struct usb_device *udev =
container_of(work, struct usb_device, autosuspend.work);
usb_pm_lock(udev);
udev->auto_pm = 1;
usb_suspend_both(udev, PMSG_SUSPEND);
usb_pm_unlock(udev);
}
#else
static void usb_autosuspend_work(struct work_struct *work)
{}
#endif /* CONFIG_USB_SUSPEND */
#else
#define ksuspend_usb_init() 0
......@@ -267,13 +252,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
dev->dev.dma_mask = bus->controller->dma_mask;
dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
/* This magic assignment distinguishes devices from interfaces */
dev->dev.platform_data = &usb_generic_driver;
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
......@@ -902,9 +884,9 @@ static int __init usb_init(void)
retval = usb_register(&usbfs_driver);
if (retval)
goto driver_register_failed;
retval = usbdev_init();
retval = usb_devio_init();
if (retval)
goto usbdevice_init_failed;
goto usb_devio_init_failed;
retval = usbfs_init();
if (retval)
goto fs_init_failed;
......@@ -919,8 +901,8 @@ static int __init usb_init(void)
hub_init_failed:
usbfs_cleanup();
fs_init_failed:
usbdev_cleanup();
usbdevice_init_failed:
usb_devio_cleanup();
usb_devio_init_failed:
usb_deregister(&usbfs_driver);
driver_register_failed:
usb_major_cleanup();
......@@ -947,7 +929,7 @@ static void __exit usb_exit(void)
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
usbdev_cleanup();
usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
......
......@@ -21,7 +21,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_kick_khubd(struct usb_device *dev);
extern void usb_resume_root_hub(struct usb_device *dev);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
......@@ -34,10 +33,12 @@ extern void usb_host_cleanup(void);
#ifdef CONFIG_PM
extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
extern int usb_resume_both(struct usb_device *udev);
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 int usb_external_suspend_device(struct usb_device *udev,
pm_message_t msg);
extern int usb_external_resume_device(struct usb_device *udev);
static inline void usb_pm_lock(struct usb_device *udev)
{
......@@ -51,11 +52,6 @@ static inline void usb_pm_unlock(struct usb_device *udev)
#else
#define usb_suspend_both(udev, msg) 0
static inline int usb_resume_both(struct usb_device *udev)
{
return 0;
}
#define usb_port_suspend(dev) 0
#define usb_port_resume(dev) 0
static inline void usb_pm_lock(struct usb_device *udev) {}
......@@ -82,15 +78,13 @@ static inline int usb_autoresume_device(struct usb_device *udev)
extern struct workqueue_struct *ksuspend_usb_wq;
extern struct bus_type usb_bus_type;
extern struct device_type usb_device_type;
extern struct device_type usb_if_device_type;
extern struct usb_device_driver usb_generic_driver;
/* Here's how we tell apart devices and interfaces. Luckily there's
* no such thing as a platform USB device, so we can steal the use
* of the platform_data field. */
static inline int is_usb_device(const struct device *dev)
{
return dev->platform_data == &usb_generic_driver;
return dev->type == &usb_device_type;
}
/* Do the same for device drivers and interface drivers. */
......@@ -126,11 +120,11 @@ extern const char *usbcore_name;
extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbfs_device_file_operations;
extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
extern int usbdev_init(void);
extern void usbdev_cleanup(void);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
struct dev_state {
struct list_head list; /* state list */
......
......@@ -68,6 +68,27 @@ choice
Many controller drivers are platform-specific; these
often need board-specific hooks.
config USB_GADGET_FSL_USB2
boolean "Freescale Highspeed USB DR Peripheral Controller"
depends on MPC834x || PPC_MPC831x
select USB_GADGET_DUALSPEED
help
Some of Freescale PowerPC processors have a High Speed
Dual-Role(DR) USB controller, which supports device mode.
The number of programmable endpoints is different through
SOC revisions.
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "fsl_usb2_udc" and force
all gadget drivers to also be dynamically linked.
config USB_FSL_USB2
tristate
depends on USB_GADGET_FSL_USB2
default USB_GADGET
select USB_GADGET_SELECTED
config USB_GADGET_NET2280
boolean "NetChip 228x"
depends on PCI
......@@ -370,6 +391,7 @@ config USB_GADGETFS
config USB_FILE_STORAGE
tristate "File-backed Storage Gadget"
depends on BLOCK
help
The File-backed Storage Gadget acts as a USB Mass Storage
disk drive. As its storage repository it can use a regular
......
......@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
#
# USB gadget drivers
......
......@@ -282,6 +282,9 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
#ifdef CONFIG_USB_GADGET_FSL_USB2
#define DEV_CONFIG_CDC
#endif
/* For CDC-incapable hardware, choose the simple cdc subset.
* Anything that talks bulk (without notable bugs) can do this.
......@@ -1735,7 +1738,8 @@ rx_submit (struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
defer_kevent (dev, WORK_RX_MEMORY);
if (retval) {
DEBUG (dev, "rx submit --> %d\n", retval);
dev_kfree_skb_any (skb);
if (skb)
dev_kfree_skb_any(skb);
spin_lock(&dev->req_lock);
list_add (&req->list, &dev->rx_reqs);
spin_unlock(&dev->req_lock);
......
此差异已折叠。
此差异已折叠。
......@@ -99,6 +99,12 @@
#define gadget_is_imx(g) 0
#endif
#ifdef CONFIG_USB_GADGET_FSL_USB2
#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name)
#else
#define gadget_is_fsl_usb2(g) 0
#endif
/* Mentor high speed function controller */
#ifdef CONFIG_USB_GADGET_MUSBHSFC
#define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
......@@ -177,5 +183,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
return 0x17;
else if (gadget_is_husb2dev(gadget))
return 0x18;
else if (gadget_is_fsl_usb2(gadget))
return 0x19;
return -ENOENT;
}
此差异已折叠。
......@@ -195,7 +195,7 @@ struct rndis_packet_msg_type
__le32 PerPacketInfoLength;
__le32 VcHandle;
__le32 Reserved;
};
} __attribute__ ((packed));
struct rndis_config_parameter
{
......
......@@ -15,4 +15,3 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
......@@ -31,7 +31,7 @@
#define FSL_SOC_USB_SNOOP1 0x400 /* NOTE: big-endian */
#define FSL_SOC_USB_SNOOP2 0x404 /* NOTE: big-endian */
#define FSL_SOC_USB_AGECNTTHRSH 0x408 /* NOTE: big-endian */
#define FSL_SOC_USB_SICTRL 0x40c /* NOTE: big-endian */
#define FSL_SOC_USB_PRICTRL 0x410 /* NOTE: big-endian */
#define FSL_SOC_USB_PRICTRL 0x40c /* NOTE: big-endian */
#define FSL_SOC_USB_SICTRL 0x410 /* NOTE: big-endian */
#define FSL_SOC_USB_CTRL 0x500 /* NOTE: big-endian */
#endif /* _EHCI_FSL_H */
......@@ -136,6 +136,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* restore CMD_RUN, framelist size, and irq threshold */
ehci_writel(ehci, ehci->command, &ehci->regs->command);
/* Some controller/firmware combinations need a delay during which
* they set up the port statuses. See Bugzilla #8190. */
mdelay(8);
/* manually resume the ports we suspended during bus_suspend() */
i = HCS_N_PORTS (ehci->hcs_params);
while (i--) {
......
此差异已折叠。
此差异已折叠。
......@@ -20,10 +20,16 @@
/*-------------------------------------------------------------------------*/
static int broken_suspend(struct usb_hcd *hcd)
{
device_init_wakeup(&hcd->self.root_hub->dev, 0);
return 0;
}
/* AMD 756, for most chips (early revs), corrupts register
* values on read ... so enable the vendor workaround.
*/
static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
static int ohci_quirk_amd756(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
......@@ -31,16 +37,14 @@ static int __devinit ohci_quirk_amd756(struct usb_hcd *hcd)
ohci_dbg (ohci, "AMD756 erratum 4 workaround\n");
/* also erratum 10 (suspend/resume issues) */
device_init_wakeup(&hcd->self.root_hub->dev, 0);
return 0;
return broken_suspend(hcd);
}
/* Apple's OHCI driver has a lot of bizarre workarounds
* for this chip. Evidently control and bulk lists
* can get confused. (B&W G3 models, and ...)
*/
static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
static int ohci_quirk_opti(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
......@@ -53,7 +57,7 @@ static int __devinit ohci_quirk_opti(struct usb_hcd *hcd)
* identify the USB (fn2). This quirk might apply to more or
* even all NSC stuff.
*/
static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
static int ohci_quirk_ns(struct usb_hcd *hcd)
{
struct pci_dev *pdev = to_pci_dev(hcd->self.controller);
struct pci_dev *b;
......@@ -75,7 +79,7 @@ static int __devinit ohci_quirk_ns(struct usb_hcd *hcd)
* delays before control or bulk queues get re-activated
* in finish_unlinks()
*/
static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
static int ohci_quirk_zfmicro(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
......@@ -88,7 +92,7 @@ static int __devinit ohci_quirk_zfmicro(struct usb_hcd *hcd)
/* Check for Toshiba SCC OHCI which has big endian registers
* and little endian in memory data structures
*/
static int __devinit ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
static int ohci_quirk_toshiba_scc(struct usb_hcd *hcd)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
......@@ -129,6 +133,18 @@ static const struct pci_device_id ohci_pci_quirks[] = {
PCI_DEVICE(PCI_VENDOR_ID_TOSHIBA_2, 0x01b6),
.driver_data = (unsigned long)ohci_quirk_toshiba_scc,
},
{
/* Toshiba portege 4000 */
.vendor = PCI_VENDOR_ID_AL,
.device = 0x5237,
.subvendor = PCI_VENDOR_ID_TOSHIBA_2,
.subdevice = 0x0004,
.driver_data = (unsigned long) broken_suspend,
},
{
PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
.driver_data = (unsigned long) broken_suspend,
},
/* FIXME for some of the early AMD 760 southbridges, OHCI
* won't work at all. blacklist them.
*/
......
......@@ -123,10 +123,14 @@ static struct uhci_td *uhci_alloc_td(struct uhci_hcd *uhci)
static void uhci_free_td(struct uhci_hcd *uhci, struct uhci_td *td)
{
if (!list_empty(&td->list))
if (!list_empty(&td->list)) {
dev_warn(uhci_dev(uhci), "td %p still in list!\n", td);
if (!list_empty(&td->fl_list))
WARN_ON(1);
}
if (!list_empty(&td->fl_list)) {
dev_warn(uhci_dev(uhci), "td %p still in fl_list!\n", td);
WARN_ON(1);
}
dma_pool_free(uhci->td_pool, td, td->dma_handle);
}
......@@ -291,8 +295,10 @@ static struct uhci_qh *uhci_alloc_qh(struct uhci_hcd *uhci,
static void uhci_free_qh(struct uhci_hcd *uhci, struct uhci_qh *qh)
{
WARN_ON(qh->state != QH_STATE_IDLE && qh->udev);
if (!list_empty(&qh->queue))
if (!list_empty(&qh->queue)) {
dev_warn(uhci_dev(uhci), "qh %p list not empty!\n", qh);
WARN_ON(1);
}
list_del(&qh->node);
if (qh->udev) {
......@@ -740,9 +746,11 @@ static void uhci_free_urb_priv(struct uhci_hcd *uhci,
{
struct uhci_td *td, *tmp;
if (!list_empty(&urbp->node))
if (!list_empty(&urbp->node)) {
dev_warn(uhci_dev(uhci), "urb %p still on QH's list!\n",
urbp->urb);
WARN_ON(1);
}
list_for_each_entry_safe(td, tmp, &urbp->td_list, list) {
uhci_remove_td_from_urbp(td);
......
此差异已折叠。
......@@ -1047,13 +1047,10 @@ static void gtco_disconnect(struct usb_interface *interface)
/* Grab private device ptr */
struct gtco *device = usb_get_intfdata (interface);
struct input_dev *inputdev;
inputdev = device->inputdevice;
/* Now reverse all the registration stuff */
if (device) {
input_unregister_device(inputdev);
input_unregister_device(device->inputdevice);
usb_kill_urb(device->urbinfo);
usb_free_urb(device->urbinfo);
usb_buffer_free(device->usbdev, REPORT_MAX_SIZE,
......
此差异已折叠。
......@@ -246,11 +246,13 @@ static void cypress_disconnect(struct usb_interface *interface)
struct cypress *dev;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
/* remove device attribute files */
device_remove_file(&interface->dev, &dev_attr_port0);
device_remove_file(&interface->dev, &dev_attr_port1);
/* the intfdata can be set to NULL only after the
* device files have been removed */
usb_set_intfdata(interface, NULL);
usb_put_dev(dev->udev);
......
......@@ -2304,7 +2304,6 @@ static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
#define OHCI_QUIRK_SUPERIO 0x02
#define OHCI_QUIRK_INITRESET 0x04
#define OHCI_BIG_ENDIAN 0x08
#define OHCI_QUIRK_ZFMICRO 0x10
#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
OHCI_INTR_WDH)
......@@ -2910,24 +2909,28 @@ static int __init ftdi_elan_init(void)
INIT_LIST_HEAD(&ftdi_static_list);
status_queue = create_singlethread_workqueue("ftdi-status-control");
if (!status_queue)
goto err1;
goto err_status_queue;
command_queue = create_singlethread_workqueue("ftdi-command-engine");
if (!command_queue)
goto err2;
goto err_command_queue;
respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
if (!respond_queue)
goto err3;
goto err_respond_queue;
result = usb_register(&ftdi_elan_driver);
if (result)
if (result) {
destroy_workqueue(status_queue);
destroy_workqueue(command_queue);
destroy_workqueue(respond_queue);
printk(KERN_ERR "usb_register failed. Error number %d\n",
result);
}
return result;
err3:
err_respond_queue:
destroy_workqueue(command_queue);
err2:
err_command_queue:
destroy_workqueue(status_queue);
err1:
err_status_queue:
printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name);
return -ENOMEM;
}
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册