Merge tag 'xceiv-for-v3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
usb: xceiv: patches for v3.7 merge window
nop xceiv got its own header to avoid polluting otg.h. It has also
learned to work as USB2 and USB3 phys so we can use it on USB3
controllers.
Together with those two changes to nop xceiv, we're adding basic
PHY support to dwc3 driver, this is to allow platforms which actually
have a SW-controllable PHY talk to them through dwc3 driver.
We're adding a new phy driver for the OMAP architecture. This driver
is for the PHY found in OMAP4 SoCs, and a new phy driver for the
marvell architecture. An extra phy driver - for Tegra SoCs - is now
moving from arch/arm/mach-tegra* to drivers/usb/phy.
Also here, there's the creation of <linux/usb/phy.h> which should be
used from now on for PHY drivers, even those which don't support
OTG.
diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 5f75f8f..13f3eee 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -220,3 +220,34 @@
If the device doesn't support LTM, the file will read "no".
The file will be present for all speeds of USB devices, and will
always read "no" for USB 1.1 and USB 2.0 devices.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX
+Date: August 2012
+Contact: Lan Tianyu <tianyu.lan@intel.com>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX
+ is usb port device's sysfs directory.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX/control
+Date: August 2012
+Contact: Lan Tianyu <tianyu.lan@intel.com>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX/control
+ attribute allows user space to control the power policy on
+ the usb port.
+
+ All ports have one of the following two values for control
+ "on" - port power must be on.
+ "off" - port power must be off.
+
+What: /sys/bus/usb/devices/.../(hub interface)/portX/state
+Date: August 2012
+Contact: Lan Tianyu <tianyu.lan@intel.com>
+Description:
+ The /sys/bus/usb/devices/.../(hub interface)/portX/state
+ attribute allows user space to check hub port's power state.
+
+ All ports have three following states
+ "on" - port power on
+ "off" - port power off
+ "error" - can't get the hub port's power state
diff --git a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
index 814b013..b31e782 100644
--- a/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
+++ b/Documentation/ABI/testing/sysfs-platform-ideapad-laptop
@@ -5,4 +5,15 @@
Description:
Control the power of camera module. 1 means on, 0 means off.
+What: /sys/devices/platform/ideapad/fan_mode
+Date: June 2012
+KernelVersion: 3.6
+Contact: "Maxim Mikityanskiy <maxtram95@gmail.com>"
+Description:
+ Change fan mode
+ There are four available modes:
+ * 0 -> Super Silent Mode
+ * 1 -> Standard Mode
+ * 2 -> Dust Cleaning
+ * 4 -> Efficient Thermal Dissipation Mode
diff --git a/Documentation/DocBook/filesystems.tmpl b/Documentation/DocBook/filesystems.tmpl
index 3fca32c..25b58ef 100644
--- a/Documentation/DocBook/filesystems.tmpl
+++ b/Documentation/DocBook/filesystems.tmpl
@@ -224,8 +224,8 @@
</para>
<para>
-Then at umount time , in your put_super() (2.4) or write_super() (2.5)
-you can then call journal_destroy() to clean up your in-core journal object.
+Then at umount time , in your put_super() you can then call journal_destroy()
+to clean up your in-core journal object.
</para>
<para>
diff --git a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
index 7203951..701138f 100644
--- a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
+++ b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml
@@ -125,7 +125,7 @@
<constant>V4L2_TUNER_CAP_NORM</constant> flags can't be used.</para>
<para>If multiple frequency bands are supported, then
<structfield>capability</structfield> is the union of all
-<structfield>capability></structfield> fields of each &v4l2-frequency-band;.
+<structfield>capability</structfield> fields of each &v4l2-frequency-band;.
</para></entry>
</row>
<row>
diff --git a/Documentation/devicetree/bindings/regulator/tps6586x.txt b/Documentation/devicetree/bindings/regulator/tps6586x.txt
index d156e1b..da80c2a 100644
--- a/Documentation/devicetree/bindings/regulator/tps6586x.txt
+++ b/Documentation/devicetree/bindings/regulator/tps6586x.txt
@@ -9,9 +9,9 @@
- regulators: list of regulators provided by this controller, must have
property "regulator-compatible" to match their hardware counterparts:
sm[0-2], ldo[0-9] and ldo_rtc
-- sm0-supply: The input supply for the SM0.
-- sm1-supply: The input supply for the SM1.
-- sm2-supply: The input supply for the SM2.
+- vin-sm0-supply: The input supply for the SM0.
+- vin-sm1-supply: The input supply for the SM1.
+- vin-sm2-supply: The input supply for the SM2.
- vinldo01-supply: The input supply for the LDO1 and LDO2
- vinldo23-supply: The input supply for the LDO2 and LDO3
- vinldo4-supply: The input supply for the LDO4
@@ -30,9 +30,9 @@
#gpio-cells = <2>;
gpio-controller;
- sm0-supply = <&some_reg>;
- sm1-supply = <&some_reg>;
- sm2-supply = <&some_reg>;
+ vin-sm0-supply = <&some_reg>;
+ vin-sm1-supply = <&some_reg>;
+ vin-sm2-supply = <&some_reg>;
vinldo01-supply = <...>;
vinldo23-supply = <...>;
vinldo4-supply = <...>;
diff --git a/Documentation/devicetree/bindings/usb/platform-uhci.txt b/Documentation/devicetree/bindings/usb/platform-uhci.txt
new file mode 100644
index 0000000..91477d6
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/platform-uhci.txt
@@ -0,0 +1,12 @@
+Generic Platform UHCI controllers.
+
+Required properties:
+ - compatible: Should be "platform-uhci".
+ - reg: Address range of the uhci registers
+ - interrupts: Should contain the uhci interrupt.
+
+usb: uhci@D8007301 {
+ compatible = "platform-uhci", "usb-uhci";
+ reg = <0xD8007301 0x200>;
+ interrupts = <0>;
+};
diff --git a/Documentation/devicetree/bindings/usb/pxa-usb.txt b/Documentation/devicetree/bindings/usb/pxa-usb.txt
new file mode 100644
index 0000000..79729a9
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/pxa-usb.txt
@@ -0,0 +1,31 @@
+PXA USB controllers
+
+OHCI
+
+Required properties:
+ - compatible: Should be "marvell,pxa-ohci" for USB controllers
+ used in host mode.
+
+Optional properties:
+ - "marvell,enable-port1", "marvell,enable-port2", "marvell,enable-port3"
+ If present, enables the appropriate USB port of the controller.
+ - "marvell,port-mode" selects the mode of the ports:
+ 1 = PMM_NPS_MODE
+ 2 = PMM_GLOBAL_MODE
+ 3 = PMM_PERPORT_MODE
+ - "marvell,power-sense-low" - power sense pin is low-active.
+ - "marvell,power-control-low" - power control pin is low-active.
+ - "marvell,no-oc-protection" - disable over-current protection.
+ - "marvell,oc-mode-perport" - enable per-port over-current protection.
+ - "marvell,power_on_delay" Power On to Power Good time - in ms.
+
+Example:
+
+ usb0: ohci@4c000000 {
+ compatible = "marvell,pxa-ohci", "usb-ohci";
+ reg = <0x4c000000 0x100000>;
+ interrupts = <18>;
+ marvell,enable-port1;
+ marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
+ };
+
diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
new file mode 100644
index 0000000..5fb8fd6
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
@@ -0,0 +1,12 @@
+VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
+
+Required properties:
+ - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
+ - reg: Address range of the ehci registers. size should be 0x200
+ - interrupts: Should contain the ehci interrupt.
+
+usb: ehci@D8007100 {
+ compatible = "wm,prizm-ehci", "usb-ehci";
+ reg = <0xD8007100 0x200>;
+ interrupts = <1>;
+};
diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
index afaff31..b9aa38b 100644
--- a/Documentation/feature-removal-schedule.txt
+++ b/Documentation/feature-removal-schedule.txt
@@ -495,17 +495,6 @@
----------------------------
-What: Low Performance USB Block driver ("CONFIG_BLK_DEV_UB")
-When: 3.6
-Why: This driver provides support for USB storage devices like "USB
- sticks". As of now, it is deactivated in Debian, Fedora and
- Ubuntu. All current users can switch over to usb-storage
- (CONFIG_USB_STORAGE) which only drawback is the additional SCSI
- stack.
-Who: Sebastian Andrzej Siewior <sebastian@breakpoint.cc>
-
-----------------------------
-
What: get_robust_list syscall
When: 2013
Why: There appear to be no production users of the get_robust_list syscall,
diff --git a/Documentation/filesystems/Locking b/Documentation/filesystems/Locking
index 0f103e3..e540a24 100644
--- a/Documentation/filesystems/Locking
+++ b/Documentation/filesystems/Locking
@@ -114,7 +114,6 @@
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -136,7 +135,6 @@
drop_inode: !!!inode->i_lock!!!
evict_inode:
put_super: write
-write_super: read
sync_fs: read
freeze_fs: write
unfreeze_fs: write
diff --git a/Documentation/filesystems/porting b/Documentation/filesystems/porting
index 2bef2b3..0742fee 100644
--- a/Documentation/filesystems/porting
+++ b/Documentation/filesystems/porting
@@ -94,9 +94,8 @@
---
[mandatory]
-BKL is also moved from around sb operations. ->write_super() Is now called
-without BKL held. BKL should have been shifted into individual fs sb_op
-functions. If you don't need it, remove it.
+BKL is also moved from around sb operations. BKL should have been shifted into
+individual fs sb_op functions. If you don't need it, remove it.
---
[informational]
diff --git a/Documentation/filesystems/vfat.txt b/Documentation/filesystems/vfat.txt
index ead764b..de1e6c4 100644
--- a/Documentation/filesystems/vfat.txt
+++ b/Documentation/filesystems/vfat.txt
@@ -137,6 +137,17 @@
without doing anything or remount the partition in
read-only mode (default behavior).
+discard -- If set, issues discard/TRIM commands to the block
+ device when blocks are freed. This is useful for SSD devices
+ and sparse/thinly-provisoned LUNs.
+
+nfs -- This option maintains an index (cache) of directory
+ inodes by i_logstart which is used by the nfs-related code to
+ improve look-ups.
+
+ Enable this only if you want to export the FAT filesystem
+ over NFS
+
<bool>: 0,1,yes,no,true,false
TODO
diff --git a/Documentation/filesystems/vfs.txt b/Documentation/filesystems/vfs.txt
index 065aa2d..2ee133e 100644
--- a/Documentation/filesystems/vfs.txt
+++ b/Documentation/filesystems/vfs.txt
@@ -216,7 +216,6 @@
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -273,9 +272,6 @@
put_super: called when the VFS wishes to free the superblock
(i.e. unmount). This is called with the superblock lock held
- write_super: called when the VFS superblock needs to be written to
- disc. This method is optional
-
sync_fs: called when VFS is writing out all dirty data associated with
a superblock. The second parameter indicates whether the method
should wait until the write out has been completed. Optional.
diff --git a/Documentation/laptops/laptop-mode.txt b/Documentation/laptops/laptop-mode.txt
index 0bf25ee..4ebbfc3 100644
--- a/Documentation/laptops/laptop-mode.txt
+++ b/Documentation/laptops/laptop-mode.txt
@@ -262,9 +262,9 @@
#
# Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
-# exceeded, the kernel will wake pdflush which will then reduce the amount
-# of dirty memory to dirty_background_ratio. Set this nice and low, so once
-# some writeout has commenced, we do a lot of it.
+# exceeded, the kernel will wake flusher threads which will then reduce the
+# amount of dirty memory to dirty_background_ratio. Set this nice and low,
+# so once some writeout has commenced, we do a lot of it.
#
#DIRTY_BACKGROUND_RATIO=5
@@ -384,9 +384,9 @@
#
# Allowed dirty background ratio, in percent. Once DIRTY_RATIO has been
-# exceeded, the kernel will wake pdflush which will then reduce the amount
-# of dirty memory to dirty_background_ratio. Set this nice and low, so once
-# some writeout has commenced, we do a lot of it.
+# exceeded, the kernel will wake flusher threads which will then reduce the
+# amount of dirty memory to dirty_background_ratio. Set this nice and low,
+# so once some writeout has commenced, we do a lot of it.
#
DIRTY_BACKGROUND_RATIO=${DIRTY_BACKGROUND_RATIO:-'5'}
diff --git a/Documentation/networking/netconsole.txt b/Documentation/networking/netconsole.txt
index 8d02207..2e9e0ae2 100644
--- a/Documentation/networking/netconsole.txt
+++ b/Documentation/networking/netconsole.txt
@@ -51,8 +51,23 @@
initialized and attempts to bring up the supplied dev at the supplied
address.
-The remote host can run either 'netcat -u -l -p <port>',
-'nc -l -u <port>' or syslogd.
+The remote host has several options to receive the kernel messages,
+for example:
+
+1) syslogd
+
+2) netcat
+
+ On distributions using a BSD-based netcat version (e.g. Fedora,
+ openSUSE and Ubuntu) the listening port must be specified without
+ the -p switch:
+
+ 'nc -u -l -p <port>' / 'nc -u -l <port>' or
+ 'netcat -u -l -p <port>' / 'netcat -u -l <port>'
+
+3) socat
+
+ 'socat udp-recv:<port> -'
Dynamic reconfiguration:
========================
diff --git a/Documentation/pinctrl.txt b/Documentation/pinctrl.txt
index e40f4b4..1479aca 100644
--- a/Documentation/pinctrl.txt
+++ b/Documentation/pinctrl.txt
@@ -840,9 +840,9 @@
static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", "i2c0"),
- PIN_MAP_MUX_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),
- PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),
- PIN_MAP_MUX_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),
+ PIN_MAP_CONFIGS_GROUP("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0", i2c_grp_configs),
+ PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0scl", i2c_pin_configs),
+ PIN_MAP_CONFIGS_PIN("foo-i2c.0", PINCTRL_STATE_DEFAULT, "pinctrl-foo", "i2c0sda", i2c_pin_configs),
};
Finally, some devices expect the mapping table to contain certain specific
diff --git a/Documentation/security/Yama.txt b/Documentation/security/Yama.txt
index e369de2..dd908cf 100644
--- a/Documentation/security/Yama.txt
+++ b/Documentation/security/Yama.txt
@@ -46,14 +46,13 @@
so that any otherwise allowed process (even those in external pid namespaces)
may attach.
-These restrictions do not change how ptrace via PTRACE_TRACEME operates.
-
-The sysctl settings are:
+The sysctl settings (writable only with CAP_SYS_PTRACE) are:
0 - classic ptrace permissions: a process can PTRACE_ATTACH to any other
process running under the same uid, as long as it is dumpable (i.e.
did not transition uids, start privileged, or have called
- prctl(PR_SET_DUMPABLE...) already).
+ prctl(PR_SET_DUMPABLE...) already). Similarly, PTRACE_TRACEME is
+ unchanged.
1 - restricted ptrace: a process must have a predefined relationship
with the inferior it wants to call PTRACE_ATTACH on. By default,
@@ -61,12 +60,13 @@
classic criteria is also met. To change the relationship, an
inferior can call prctl(PR_SET_PTRACER, debugger, ...) to declare
an allowed debugger PID to call PTRACE_ATTACH on the inferior.
+ Using PTRACE_TRACEME is unchanged.
2 - admin-only attach: only processes with CAP_SYS_PTRACE may use ptrace
- with PTRACE_ATTACH.
+ with PTRACE_ATTACH, or through children calling PTRACE_TRACEME.
-3 - no attach: no processes may use ptrace with PTRACE_ATTACH. Once set,
- this sysctl cannot be changed to a lower value.
+3 - no attach: no processes may use ptrace with PTRACE_ATTACH nor via
+ PTRACE_TRACEME. Once set, this sysctl value cannot be changed.
The original children-only logic was based on the restrictions in grsecurity.
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index dcc2a94..078701f 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -76,8 +76,8 @@
dirty_background_bytes
-Contains the amount of dirty memory at which the pdflush background writeback
-daemon will start writeback.
+Contains the amount of dirty memory at which the background kernel
+flusher threads will start writeback.
Note: dirty_background_bytes is the counterpart of dirty_background_ratio. Only
one of them may be specified at a time. When one sysctl is written it is
@@ -89,7 +89,7 @@
dirty_background_ratio
Contains, as a percentage of total system memory, the number of pages at which
-the pdflush background writeback daemon will start writing out dirty data.
+the background kernel flusher threads will start writing out dirty data.
==============================================================
@@ -112,9 +112,9 @@
dirty_expire_centisecs
This tunable is used to define when dirty data is old enough to be eligible
-for writeout by the pdflush daemons. It is expressed in 100'ths of a second.
-Data which has been dirty in-memory for longer than this interval will be
-written out next time a pdflush daemon wakes up.
+for writeout by the kernel flusher threads. It is expressed in 100'ths
+of a second. Data which has been dirty in-memory for longer than this
+interval will be written out next time a flusher thread wakes up.
==============================================================
@@ -128,7 +128,7 @@
dirty_writeback_centisecs
-The pdflush writeback daemons will periodically wake up and write `old' data
+The kernel flusher threads will periodically wake up and write `old' data
out to disk. This tunable expresses the interval between those wakeups, in
100'ths of a second.
diff --git a/Documentation/usb/persist.txt b/Documentation/usb/persist.txt
index 074b159..35d70ed 100644
--- a/Documentation/usb/persist.txt
+++ b/Documentation/usb/persist.txt
@@ -155,6 +155,9 @@
data corruption and to crash your system. You'll have no one to blame
but yourself.
+For those devices with avoid_reset_quirk attribute being set, persist
+maybe fail because they may morph after reset.
+
YOU HAVE BEEN WARNED! USE AT YOUR OWN RISK!
That having been said, most of the time there shouldn't be any trouble
diff --git a/Documentation/vm/hugetlbpage.txt b/Documentation/vm/hugetlbpage.txt
index f8551b3..4ac359b 100644
--- a/Documentation/vm/hugetlbpage.txt
+++ b/Documentation/vm/hugetlbpage.txt
@@ -299,11 +299,17 @@
*******************************************************************
/*
- * hugepage-shm: see Documentation/vm/hugepage-shm.c
+ * map_hugetlb: see tools/testing/selftests/vm/map_hugetlb.c
*/
*******************************************************************
/*
- * hugepage-mmap: see Documentation/vm/hugepage-mmap.c
+ * hugepage-shm: see tools/testing/selftests/vm/hugepage-shm.c
+ */
+
+*******************************************************************
+
+/*
+ * hugepage-mmap: see tools/testing/selftests/vm/hugepage-mmap.c
*/
diff --git a/Documentation/w1/slaves/w1_therm b/Documentation/w1/slaves/w1_therm
index 0403aaa..874a8ca 100644
--- a/Documentation/w1/slaves/w1_therm
+++ b/Documentation/w1/slaves/w1_therm
@@ -3,6 +3,7 @@
Supported chips:
* Maxim ds18*20 based temperature sensors.
+ * Maxim ds1825 based temperature sensors.
Author: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
@@ -15,6 +16,7 @@
W1_THERM_DS18S20 0x10
W1_THERM_DS1822 0x22
W1_THERM_DS18B20 0x28
+W1_THERM_DS1825 0x3B
Support is provided through the sysfs w1_slave file. Each open and
read sequence will initiate a temperature conversion then provide two
diff --git a/MAINTAINERS b/MAINTAINERS
index 94b823f..fdc0119 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -827,24 +827,24 @@
ARM/INTEL IOP32X ARM ARCHITECTURE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IOP33X ARM ARCHITECTURE
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IOP13XX ARM ARCHITECTURE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
ARM/INTEL IQ81342EX MACHINE SUPPORT
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
@@ -869,7 +869,7 @@
ARM/INTEL XSC3 (MANZANO) ARM CORE
M: Lennert Buytenhek <kernel@wantstofly.org>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
@@ -925,14 +925,14 @@
ARM/NOMADIK ARCHITECTURE
M: Alessandro Rubini <rubini@unipv.it>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
M: STEricsson <STEricsson_nomadik_linux@list.st.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-nomadik/
F: arch/arm/plat-nomadik/
F: drivers/i2c/busses/i2c-nomadik.c
-T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
+T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik.git
ARM/OPENMOKO NEO FREERUNNER (GTA02) MACHINE SUPPORT
M: Nelson Castillo <arhuaco@freaks-unidos.net>
@@ -1146,7 +1146,7 @@
F: drivers/video/nuc900fb.c
ARM/U300 MACHINE SUPPORT
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Supported
F: arch/arm/mach-u300/
@@ -1161,15 +1161,20 @@
ARM/Ux500 ARM ARCHITECTURE
M: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: arch/arm/mach-ux500/
+F: drivers/clocksource/clksrc-dbx500-prcmu.c
F: drivers/dma/ste_dma40*
+F: drivers/hwspinlock/u8500_hsem.c
F: drivers/mfd/abx500*
F: drivers/mfd/ab8500*
-F: drivers/mfd/stmpe*
+F: drivers/mfd/dbx500*
+F: drivers/mfd/db8500*
+F: drivers/pinctrl/pinctrl-nomadik*
F: drivers/rtc/rtc-ab8500.c
+F: drivers/rtc/rtc-pl031.c
T: git git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-stericsson.git
ARM/VFP SUPPORT
@@ -1227,9 +1232,9 @@
F: drivers/hwmon/asb100.c
ASYNCHRONOUS TRANSFERS/TRANSFORMS (IOAT) API
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
W: http://sourceforge.net/projects/xscaleiop
-S: Supported
+S: Maintained
F: Documentation/crypto/async-tx-api.txt
F: crypto/async_tx/
F: drivers/dma/
@@ -2212,7 +2217,7 @@
F: drivers/scsi/tmscsim.*
DC395x SCSI driver
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
M: Ali Akcaagac <aliakc@web.de>
M: Jamie Lenehan <lenehan@twibble.org>
W: http://twibble.org/dist/dc395x/
@@ -2359,7 +2364,7 @@
DMA GENERIC OFFLOAD ENGINE SUBSYSTEM
M: Vinod Koul <vinod.koul@intel.com>
-M: Dan Williams <dan.j.williams@intel.com>
+M: Dan Williams <djbw@fb.com>
S: Supported
F: drivers/dma/
F: include/linux/dma*
@@ -3094,7 +3099,7 @@
GPIO SUBSYSTEM
M: Grant Likely <grant.likely@secretlab.ca>
-M: Linus Walleij <linus.walleij@stericsson.com>
+M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
T: git git://git.secretlab.ca/git/linux-2.6.git
F: Documentation/gpio.txt
@@ -3547,7 +3552,6 @@
INTEL C600 SERIES SAS CONTROLLER DRIVER
M: Intel SCU Linux support <intel-linux-scu@intel.com>
-M: Dan Williams <dan.j.williams@intel.com>
M: Dave Jiang <dave.jiang@intel.com>
M: Ed Nadolski <edmund.nadolski@intel.com>
L: linux-scsi@vger.kernel.org
@@ -3590,8 +3594,8 @@
F: arch/x86/kernel/microcode_intel.c
INTEL I/OAT DMA DRIVER
-M: Dan Williams <dan.j.williams@intel.com>
-S: Supported
+M: Dan Williams <djbw@fb.com>
+S: Maintained
F: drivers/dma/ioat*
INTEL IOMMU (VT-d)
@@ -3603,8 +3607,8 @@
F: include/linux/intel-iommu.h
INTEL IOP-ADMA DMA DRIVER
-M: Dan Williams <dan.j.williams@intel.com>
-S: Maintained
+M: Dan Williams <djbw@fb.com>
+S: Odd fixes
F: drivers/dma/iop-adma.c
INTEL IXP4XX QMGR, NPE, ETHERNET and HSS SUPPORT
@@ -4533,7 +4537,7 @@
F: arch/microblaze/
MICROTEK X6 SCANNER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
S: Maintained
F: drivers/usb/image/microtek.*
@@ -5329,14 +5333,15 @@
M: Linus Walleij <linus.walleij@linaro.org>
S: Maintained
F: drivers/pinctrl/
+F: include/linux/pinctrl/
PIN CONTROLLER - ST SPEAR
-M: Viresh Kumar <viresh.linux@gmail.com>
+M: Viresh Kumar <viresh.linux@gmail.com>
L: spear-devel@list.st.com
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
W: http://www.st.com/spear
S: Maintained
-F: driver/pinctrl/spear/
+F: drivers/pinctrl/spear/
PKTCDVD DRIVER
M: Peter Osterlund <petero2@telia.com>
@@ -7071,7 +7076,7 @@
F: include/mtd/ubi-user.h
USB ACM DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: Documentation/usb/acm.txt
@@ -7092,7 +7097,7 @@
F: drivers/block/ub.c
USB CDC ETHERNET DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/net/usb/cdc_*.c
@@ -7165,7 +7170,7 @@
F: include/linux/usb/isp116x.h
USB KAWASAKI LSI DRIVER
-M: Oliver Neukum <oliver@neukum.name>
+M: Oliver Neukum <oliver@neukum.org>
L: linux-usb@vger.kernel.org
S: Maintained
F: drivers/usb/serial/kl5kusb105.*
@@ -7283,6 +7288,12 @@
S: Supported
F: drivers/usb/serial/whiteheat*
+USB SMSC75XX ETHERNET DRIVER
+M: Steve Glendinning <steve.glendinning@shawell.net>
+L: netdev@vger.kernel.org
+S: Maintained
+F: drivers/net/usb/smsc75xx.*
+
USB SMSC95XX ETHERNET DRIVER
M: Steve Glendinning <steve.glendinning@shawell.net>
L: netdev@vger.kernel.org
@@ -7665,23 +7676,28 @@
F: Documentation/hwmon/wm83??
F: arch/arm/mach-s3c64xx/mach-crag6410*
F: drivers/clk/clk-wm83*.c
+F: drivers/extcon/extcon-arizona.c
F: drivers/leds/leds-wm83*.c
F: drivers/gpio/gpio-*wm*.c
+F: drivers/gpio/gpio-arizona.c
F: drivers/hwmon/wm83??-hwmon.c
F: drivers/input/misc/wm831x-on.c
F: drivers/input/touchscreen/wm831x-ts.c
F: drivers/input/touchscreen/wm97*.c
-F: drivers/mfd/wm8*.c
+F: drivers/mfd/arizona*
+F: drivers/mfd/wm*.c
F: drivers/power/wm83*.c
F: drivers/rtc/rtc-wm83*.c
F: drivers/regulator/wm8*.c
F: drivers/video/backlight/wm83*_bl.c
F: drivers/watchdog/wm83*_wdt.c
+F: include/linux/mfd/arizona/
F: include/linux/mfd/wm831x/
F: include/linux/mfd/wm8350/
F: include/linux/mfd/wm8400*
F: include/linux/wm97xx.h
F: include/sound/wm????.h
+F: sound/soc/codecs/arizona.?
F: sound/soc/codecs/wm*
WORKQUEUE
diff --git a/Makefile b/Makefile
index ddf5be9..3540268 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
VERSION = 3
PATCHLEVEL = 6
SUBLEVEL = 0
-EXTRAVERSION = -rc1
+EXTRAVERSION = -rc3
NAME = Saber-toothed Squirrel
# *DOCUMENTATION*
diff --git a/arch/alpha/Kconfig b/arch/alpha/Kconfig
index d5b9b5e..9944ded 100644
--- a/arch/alpha/Kconfig
+++ b/arch/alpha/Kconfig
@@ -18,6 +18,8 @@
select ARCH_HAVE_NMI_SAFE_CMPXCHG
select GENERIC_SMP_IDLE_THREAD
select GENERIC_CMOS_UPDATE
+ select GENERIC_STRNCPY_FROM_USER
+ select GENERIC_STRNLEN_USER
help
The Alpha is a 64-bit general-purpose processor designed and
marketed by the Digital Equipment Corporation of blessed memory,
diff --git a/arch/alpha/include/asm/atomic.h b/arch/alpha/include/asm/atomic.h
index 3bb7ffe..c2cbe4f 100644
--- a/arch/alpha/include/asm/atomic.h
+++ b/arch/alpha/include/asm/atomic.h
@@ -14,8 +14,8 @@
*/
-#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
-#define ATOMIC64_INIT(i) ( (atomic64_t) { (i) } )
+#define ATOMIC_INIT(i) { (i) }
+#define ATOMIC64_INIT(i) { (i) }
#define atomic_read(v) (*(volatile int *)&(v)->counter)
#define atomic64_read(v) (*(volatile long *)&(v)->counter)
diff --git a/arch/alpha/include/asm/fpu.h b/arch/alpha/include/asm/fpu.h
index db00f78..e477bcd 100644
--- a/arch/alpha/include/asm/fpu.h
+++ b/arch/alpha/include/asm/fpu.h
@@ -1,7 +1,9 @@
#ifndef __ASM_ALPHA_FPU_H
#define __ASM_ALPHA_FPU_H
+#ifdef __KERNEL__
#include <asm/special_insns.h>
+#endif
/*
* Alpha floating-point control register defines:
diff --git a/arch/alpha/include/asm/ptrace.h b/arch/alpha/include/asm/ptrace.h
index fd698a1..b87755a 100644
--- a/arch/alpha/include/asm/ptrace.h
+++ b/arch/alpha/include/asm/ptrace.h
@@ -76,7 +76,10 @@
#define task_pt_regs(task) \
((struct pt_regs *) (task_stack_page(task) + 2*PAGE_SIZE) - 1)
-#define force_successful_syscall_return() (task_pt_regs(current)->r0 = 0)
+#define current_pt_regs() \
+ ((struct pt_regs *) ((char *)current_thread_info() + 2*PAGE_SIZE) - 1)
+
+#define force_successful_syscall_return() (current_pt_regs()->r0 = 0)
#endif
diff --git a/arch/alpha/include/asm/socket.h b/arch/alpha/include/asm/socket.h
index dcb221a..7d2f75b 100644
--- a/arch/alpha/include/asm/socket.h
+++ b/arch/alpha/include/asm/socket.h
@@ -76,9 +76,11 @@
/* Instruct lower device to use last 4-bytes of skb data as FCS */
#define SO_NOFCS 43
+#ifdef __KERNEL__
/* O_NONBLOCK clashes with the bits used for socket types. Therefore we
* have to define SOCK_NONBLOCK to a different value here.
*/
#define SOCK_NONBLOCK 0x40000000
+#endif /* __KERNEL__ */
#endif /* _ASM_SOCKET_H */
diff --git a/arch/alpha/include/asm/uaccess.h b/arch/alpha/include/asm/uaccess.h
index b49ec2f..766fdfd 100644
--- a/arch/alpha/include/asm/uaccess.h
+++ b/arch/alpha/include/asm/uaccess.h
@@ -433,36 +433,12 @@
#undef __module_address
#undef __module_call
-/* Returns: -EFAULT if exception before terminator, N if the entire
- buffer filled, else strlen. */
+#define user_addr_max() \
+ (segment_eq(get_fs(), USER_DS) ? TASK_SIZE : ~0UL)
-extern long __strncpy_from_user(char *__to, const char __user *__from, long __to_len);
-
-extern inline long
-strncpy_from_user(char *to, const char __user *from, long n)
-{
- long ret = -EFAULT;
- if (__access_ok((unsigned long)from, 0, get_fs()))
- ret = __strncpy_from_user(to, from, n);
- return ret;
-}
-
-/* Returns: 0 if bad, string length+1 (memory size) of string if ok */
-extern long __strlen_user(const char __user *);
-
-extern inline long strlen_user(const char __user *str)
-{
- return access_ok(VERIFY_READ,str,0) ? __strlen_user(str) : 0;
-}
-
-/* Returns: 0 if exception before NUL or reaching the supplied limit (N),
- * a value greater than N if the limit would be exceeded, else strlen. */
-extern long __strnlen_user(const char __user *, long);
-
-extern inline long strnlen_user(const char __user *str, long n)
-{
- return access_ok(VERIFY_READ,str,0) ? __strnlen_user(str, n) : 0;
-}
+extern long strncpy_from_user(char *dest, const char __user *src, long count);
+extern __must_check long strlen_user(const char __user *str);
+extern __must_check long strnlen_user(const char __user *str, long n);
/*
* About the exception table:
diff --git a/arch/alpha/include/asm/unistd.h b/arch/alpha/include/asm/unistd.h
index 633b23b..a31a78e 100644
--- a/arch/alpha/include/asm/unistd.h
+++ b/arch/alpha/include/asm/unistd.h
@@ -465,10 +465,12 @@
#define __NR_setns 501
#define __NR_accept4 502
#define __NR_sendmmsg 503
+#define __NR_process_vm_readv 504
+#define __NR_process_vm_writev 505
#ifdef __KERNEL__
-#define NR_SYSCALLS 504
+#define NR_SYSCALLS 506
#define __ARCH_WANT_OLD_READDIR
#define __ARCH_WANT_STAT64
diff --git a/arch/alpha/include/asm/word-at-a-time.h b/arch/alpha/include/asm/word-at-a-time.h
new file mode 100644
index 0000000..6b340d0
--- /dev/null
+++ b/arch/alpha/include/asm/word-at-a-time.h
@@ -0,0 +1,55 @@
+#ifndef _ASM_WORD_AT_A_TIME_H
+#define _ASM_WORD_AT_A_TIME_H
+
+#include <asm/compiler.h>
+
+/*
+ * word-at-a-time interface for Alpha.
+ */
+
+/*
+ * We do not use the word_at_a_time struct on Alpha, but it needs to be
+ * implemented to humour the generic code.
+ */
+struct word_at_a_time {
+ const unsigned long unused;
+};
+
+#define WORD_AT_A_TIME_CONSTANTS { 0 }
+
+/* Return nonzero if val has a zero */
+static inline unsigned long has_zero(unsigned long val, unsigned long *bits, const struct word_at_a_time *c)
+{
+ unsigned long zero_locations = __kernel_cmpbge(0, val);
+ *bits = zero_locations;
+ return zero_locations;
+}
+
+static inline unsigned long prep_zero_mask(unsigned long val, unsigned long bits, const struct word_at_a_time *c)
+{
+ return bits;
+}
+
+#define create_zero_mask(bits) (bits)
+
+static inline unsigned long find_zero(unsigned long bits)
+{
+#if defined(CONFIG_ALPHA_EV6) && defined(CONFIG_ALPHA_EV67)
+ /* Simple if have CIX instructions */
+ return __kernel_cttz(bits);
+#else
+ unsigned long t1, t2, t3;
+ /* Retain lowest set bit only */
+ bits &= -bits;
+ /* Binary search for lowest set bit */
+ t1 = bits & 0xf0;
+ t2 = bits & 0xcc;
+ t3 = bits & 0xaa;
+ if (t1) t1 = 4;
+ if (t2) t2 = 2;
+ if (t3) t3 = 1;
+ return t1 + t2 + t3;
+#endif
+}
+
+#endif /* _ASM_WORD_AT_A_TIME_H */
diff --git a/arch/alpha/kernel/alpha_ksyms.c b/arch/alpha/kernel/alpha_ksyms.c
index d96e742..15fa821 100644
--- a/arch/alpha/kernel/alpha_ksyms.c
+++ b/arch/alpha/kernel/alpha_ksyms.c
@@ -52,7 +52,6 @@
/* entry.S */
EXPORT_SYMBOL(kernel_thread);
-EXPORT_SYMBOL(kernel_execve);
/* Networking helper routines. */
EXPORT_SYMBOL(csum_tcpudp_magic);
@@ -74,8 +73,6 @@
*/
EXPORT_SYMBOL(__copy_user);
EXPORT_SYMBOL(__do_clear_user);
-EXPORT_SYMBOL(__strncpy_from_user);
-EXPORT_SYMBOL(__strnlen_user);
/*
* SMP-specific symbols.
diff --git a/arch/alpha/kernel/entry.S b/arch/alpha/kernel/entry.S
index 6d159ce..ec0da05 100644
--- a/arch/alpha/kernel/entry.S
+++ b/arch/alpha/kernel/entry.S
@@ -663,58 +663,6 @@
br ret_to_kernel
.end kernel_thread
-/*
- * kernel_execve(path, argv, envp)
- */
- .align 4
- .globl kernel_execve
- .ent kernel_execve
-kernel_execve:
- /* We can be called from a module. */
- ldgp $gp, 0($27)
- lda $sp, -(32+SIZEOF_PT_REGS+8)($sp)
- .frame $sp, 32+SIZEOF_PT_REGS+8, $26, 0
- stq $26, 0($sp)
- stq $16, 8($sp)
- stq $17, 16($sp)
- stq $18, 24($sp)
- .prologue 1
-
- lda $16, 32($sp)
- lda $17, 0
- lda $18, SIZEOF_PT_REGS
- bsr $26, memset !samegp
-
- /* Avoid the HAE being gratuitously wrong, which would cause us
- to do the whole turn off interrupts thing and restore it. */
- ldq $2, alpha_mv+HAE_CACHE
- stq $2, 152+32($sp)
-
- ldq $16, 8($sp)
- ldq $17, 16($sp)
- ldq $18, 24($sp)
- lda $19, 32($sp)
- bsr $26, do_execve !samegp
-
- ldq $26, 0($sp)
- bne $0, 1f /* error! */
-
- /* Move the temporary pt_regs struct from its current location
- to the top of the kernel stack frame. See copy_thread for
- details for a normal process. */
- lda $16, 0x4000 - SIZEOF_PT_REGS($8)
- lda $17, 32($sp)
- lda $18, SIZEOF_PT_REGS
- bsr $26, memmove !samegp
-
- /* Take that over as our new stack frame and visit userland! */
- lda $sp, 0x4000 - SIZEOF_PT_REGS($8)
- br $31, ret_from_sys_call
-
-1: lda $sp, 32+SIZEOF_PT_REGS+8($sp)
- ret
-.end kernel_execve
-
/*
* Special system calls. Most of these are special in that they either
@@ -797,115 +745,6 @@
.end sys_rt_sigreturn
.align 4
- .globl sys_sethae
- .ent sys_sethae
-sys_sethae:
- .prologue 0
- stq $16, 152($sp)
- ret
-.end sys_sethae
-
- .align 4
- .globl osf_getpriority
- .ent osf_getpriority
-osf_getpriority:
- lda $sp, -16($sp)
- stq $26, 0($sp)
- .prologue 0
-
- jsr $26, sys_getpriority
-
- ldq $26, 0($sp)
- blt $0, 1f
-
- /* Return value is the unbiased priority, i.e. 20 - prio.
- This does result in negative return values, so signal
- no error by writing into the R0 slot. */
- lda $1, 20
- stq $31, 16($sp)
- subl $1, $0, $0
- unop
-
-1: lda $sp, 16($sp)
- ret
-.end osf_getpriority
-
- .align 4
- .globl sys_getxuid
- .ent sys_getxuid
-sys_getxuid:
- .prologue 0
- ldq $2, TI_TASK($8)
- ldq $3, TASK_CRED($2)
- ldl $0, CRED_UID($3)
- ldl $1, CRED_EUID($3)
- stq $1, 80($sp)
- ret
-.end sys_getxuid
-
- .align 4
- .globl sys_getxgid
- .ent sys_getxgid
-sys_getxgid:
- .prologue 0
- ldq $2, TI_TASK($8)
- ldq $3, TASK_CRED($2)
- ldl $0, CRED_GID($3)
- ldl $1, CRED_EGID($3)
- stq $1, 80($sp)
- ret
-.end sys_getxgid
-
- .align 4
- .globl sys_getxpid
- .ent sys_getxpid
-sys_getxpid:
- .prologue 0
- ldq $2, TI_TASK($8)
-
- /* See linux/kernel/timer.c sys_getppid for discussion
- about this loop. */
- ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
- ldl $0, TASK_TGID($2)
-1: ldl $1, TASK_TGID($4)
-#ifdef CONFIG_SMP
- mov $4, $5
- mb
- ldq $3, TASK_GROUP_LEADER($2)
- ldq $4, TASK_REAL_PARENT($3)
- cmpeq $4, $5, $5
- beq $5, 1b
-#endif
- stq $1, 80($sp)
- ret
-.end sys_getxpid
-
- .align 4
- .globl sys_alpha_pipe
- .ent sys_alpha_pipe
-sys_alpha_pipe:
- lda $sp, -16($sp)
- stq $26, 0($sp)
- .prologue 0
-
- mov $31, $17
- lda $16, 8($sp)
- jsr $26, do_pipe_flags
-
- ldq $26, 0($sp)
- bne $0, 1f
-
- /* The return values are in $0 and $20. */
- ldl $1, 12($sp)
- ldl $0, 8($sp)
-
- stq $1, 80+16($sp)
-1: lda $sp, 16($sp)
- ret
-.end sys_alpha_pipe
-
- .align 4
.globl sys_execve
.ent sys_execve
sys_execve:
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c
index 98a1036..bc1acdd 100644
--- a/arch/alpha/kernel/osf_sys.c
+++ b/arch/alpha/kernel/osf_sys.c
@@ -1404,3 +1404,52 @@
}
#endif
+
+SYSCALL_DEFINE2(osf_getpriority, int, which, int, who)
+{
+ int prio = sys_getpriority(which, who);
+ if (prio >= 0) {
+ /* Return value is the unbiased priority, i.e. 20 - prio.
+ This does result in negative return values, so signal
+ no error */
+ force_successful_syscall_return();
+ prio = 20 - prio;
+ }
+ return prio;
+}
+
+SYSCALL_DEFINE0(getxuid)
+{
+ current_pt_regs()->r20 = sys_geteuid();
+ return sys_getuid();
+}
+
+SYSCALL_DEFINE0(getxgid)
+{
+ current_pt_regs()->r20 = sys_getegid();
+ return sys_getgid();
+}
+
+SYSCALL_DEFINE0(getxpid)
+{
+ current_pt_regs()->r20 = sys_getppid();
+ return sys_getpid();
+}
+
+SYSCALL_DEFINE0(alpha_pipe)
+{
+ int fd[2];
+ int res = do_pipe_flags(fd, 0);
+ if (!res) {
+ /* The return values are in $0 and $20. */
+ current_pt_regs()->r20 = fd[1];
+ res = fd[0];
+ }
+ return res;
+}
+
+SYSCALL_DEFINE1(sethae, unsigned long, val)
+{
+ current_pt_regs()->hae = val;
+ return 0;
+}
diff --git a/arch/alpha/kernel/process.c b/arch/alpha/kernel/process.c
index 153d3fc..d6fde98 100644
--- a/arch/alpha/kernel/process.c
+++ b/arch/alpha/kernel/process.c
@@ -455,3 +455,22 @@
}
return pc;
}
+
+int kernel_execve(const char *path, const char *const argv[], const char *const envp[])
+{
+ /* Avoid the HAE being gratuitously wrong, which would cause us
+ to do the whole turn off interrupts thing and restore it. */
+ struct pt_regs regs = {.hae = alpha_mv.hae_cache};
+ int err = do_execve(path, argv, envp, ®s);
+ if (!err) {
+ struct pt_regs *p = current_pt_regs();
+ /* copy regs to normal position and off to userland we go... */
+ *p = regs;
+ __asm__ __volatile__ (
+ "mov %0, $sp;"
+ "br $31, ret_from_sys_call"
+ : : "r"(p));
+ }
+ return err;
+}
+EXPORT_SYMBOL(kernel_execve);
diff --git a/arch/alpha/kernel/systbls.S b/arch/alpha/kernel/systbls.S
index 8783523..2ac6b45 100644
--- a/arch/alpha/kernel/systbls.S
+++ b/arch/alpha/kernel/systbls.S
@@ -111,7 +111,7 @@
.quad sys_socket
.quad sys_connect
.quad sys_accept
- .quad osf_getpriority /* 100 */
+ .quad sys_osf_getpriority /* 100 */
.quad sys_send
.quad sys_recv
.quad sys_sigreturn
@@ -522,6 +522,8 @@
.quad sys_setns
.quad sys_accept4
.quad sys_sendmmsg
+ .quad sys_process_vm_readv
+ .quad sys_process_vm_writev /* 505 */
.size sys_call_table, . - sys_call_table
.type sys_call_table, @object
diff --git a/arch/alpha/lib/Makefile b/arch/alpha/lib/Makefile
index c0a83ab..5966074 100644
--- a/arch/alpha/lib/Makefile
+++ b/arch/alpha/lib/Makefile
@@ -31,8 +31,6 @@
$(ev6-y)memchr.o \
$(ev6-y)copy_user.o \
$(ev6-y)clear_user.o \
- $(ev6-y)strncpy_from_user.o \
- $(ev67-y)strlen_user.o \
$(ev6-y)csum_ipv6_magic.o \
$(ev6-y)clear_page.o \
$(ev6-y)copy_page.o \
diff --git a/arch/alpha/lib/ev6-strncpy_from_user.S b/arch/alpha/lib/ev6-strncpy_from_user.S
deleted file mode 100644
index d2e2817..0000000
--- a/arch/alpha/lib/ev6-strncpy_from_user.S
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * arch/alpha/lib/ev6-strncpy_from_user.S
- * 21264 version contributed by Rick Gorton <rick.gorton@alpha-processor.com>
- *
- * Just like strncpy except in the return value:
- *
- * -EFAULT if an exception occurs before the terminator is copied.
- * N if the buffer filled.
- *
- * Otherwise the length of the string is returned.
- *
- * Much of the information about 21264 scheduling/coding comes from:
- * Compiler Writer's Guide for the Alpha 21264
- * abbreviated as 'CWG' in other comments here
- * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
- * Scheduling notation:
- * E - either cluster
- * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
- * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
- * A bunch of instructions got moved and temp registers were changed
- * to aid in scheduling. Control flow was also re-arranged to eliminate
- * branches, and to provide longer code sequences to enable better scheduling.
- * A total rewrite (using byte load/stores for start & tail sequences)
- * is desirable, but very difficult to do without a from-scratch rewrite.
- * Save that for the future.
- */
-
-
-#include <asm/errno.h>
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda $31, $exception-99b($0); \
- .previous
-
-
- .set noat
- .set noreorder
- .text
-
- .globl __strncpy_from_user
- .ent __strncpy_from_user
- .frame $30, 0, $26
- .prologue 0
-
- .align 4
-__strncpy_from_user:
- and a0, 7, t3 # E : find dest misalignment
- beq a2, $zerolength # U :
-
- /* Are source and destination co-aligned? */
- mov a0, v0 # E : save the string start
- xor a0, a1, t4 # E :
- EX( ldq_u t1, 0(a1) ) # L : Latency=3 load first quadword
- ldq_u t0, 0(a0) # L : load first (partial) aligned dest quadword
-
- addq a2, t3, a2 # E : bias count by dest misalignment
- subq a2, 1, a3 # E :
- addq zero, 1, t10 # E :
- and t4, 7, t4 # E : misalignment between the two
-
- and a3, 7, t6 # E : number of tail bytes
- sll t10, t6, t10 # E : t10 = bitmask of last count byte
- bne t4, $unaligned # U :
- lda t2, -1 # E : build a mask against false zero
-
- /*
- * We are co-aligned; take care of a partial first word.
- * On entry to this basic block:
- * t0 == the first destination word for masking back in
- * t1 == the first source word.
- */
-
- srl a3, 3, a2 # E : a2 = loop counter = (count - 1)/8
- addq a1, 8, a1 # E :
- mskqh t2, a1, t2 # U : detection in the src word
- nop
-
- /* Create the 1st output word and detect 0's in the 1st input word. */
- mskqh t1, a1, t3 # U :
- mskql t0, a1, t0 # U : assemble the first output word
- ornot t1, t2, t2 # E :
- nop
-
- cmpbge zero, t2, t8 # E : bits set iff null found
- or t0, t3, t0 # E :
- beq a2, $a_eoc # U :
- bne t8, $a_eos # U : 2nd branch in a quad. Bad.
-
- /* On entry to this basic block:
- * t0 == a source quad not containing a null.
- * a0 - current aligned destination address
- * a1 - current aligned source address
- * a2 - count of quadwords to move.
- * NOTE: Loop improvement - unrolling this is going to be
- * a huge win, since we're going to stall otherwise.
- * Fix this later. For _really_ large copies, look
- * at using wh64 on a look-ahead basis. See the code
- * in clear_user.S and copy_user.S.
- * Presumably, since (a0) and (a1) do not overlap (by C definition)
- * Lots of nops here:
- * - Separate loads from stores
- * - Keep it to 1 branch/quadpack so the branch predictor
- * can train.
- */
-$a_loop:
- stq_u t0, 0(a0) # L :
- addq a0, 8, a0 # E :
- nop
- subq a2, 1, a2 # E :
-
- EX( ldq_u t0, 0(a1) ) # L :
- addq a1, 8, a1 # E :
- cmpbge zero, t0, t8 # E : Stall 2 cycles on t0
- beq a2, $a_eoc # U :
-
- beq t8, $a_loop # U :
- nop
- nop
- nop
-
- /* Take care of the final (partial) word store. At this point
- * the end-of-count bit is set in t8 iff it applies.
- *
- * On entry to this basic block we have:
- * t0 == the source word containing the null
- * t8 == the cmpbge mask that found it.
- */
-$a_eos:
- negq t8, t12 # E : find low bit set
- and t8, t12, t12 # E :
-
- /* We're doing a partial word store and so need to combine
- our source and original destination words. */
- ldq_u t1, 0(a0) # L :
- subq t12, 1, t6 # E :
-
- or t12, t6, t8 # E :
- zapnot t0, t8, t0 # U : clear src bytes > null
- zap t1, t8, t1 # U : clear dst bytes <= null
- or t0, t1, t0 # E :
-
- stq_u t0, 0(a0) # L :
- br $finish_up # L0 :
- nop
- nop
-
- /* Add the end-of-count bit to the eos detection bitmask. */
- .align 4
-$a_eoc:
- or t10, t8, t8
- br $a_eos
- nop
- nop
-
-
-/* The source and destination are not co-aligned. Align the destination
- and cope. We have to be very careful about not reading too much and
- causing a SEGV. */
-
- .align 4
-$u_head:
- /* We know just enough now to be able to assemble the first
- full source word. We can still find a zero at the end of it
- that prevents us from outputting the whole thing.
-
- On entry to this basic block:
- t0 == the first dest word, unmasked
- t1 == the shifted low bits of the first source word
- t6 == bytemask that is -1 in dest word bytes */
-
- EX( ldq_u t2, 8(a1) ) # L : load second src word
- addq a1, 8, a1 # E :
- mskql t0, a0, t0 # U : mask trailing garbage in dst
- extqh t2, a1, t4 # U :
-
- or t1, t4, t1 # E : first aligned src word complete
- mskqh t1, a0, t1 # U : mask leading garbage in src
- or t0, t1, t0 # E : first output word complete
- or t0, t6, t6 # E : mask original data for zero test
-
- cmpbge zero, t6, t8 # E :
- beq a2, $u_eocfin # U :
- bne t8, $u_final # U : bad news - 2nd branch in a quad
- lda t6, -1 # E : mask out the bits we have
-
- mskql t6, a1, t6 # U : already seen
- stq_u t0, 0(a0) # L : store first output word
- or t6, t2, t2 # E :
- cmpbge zero, t2, t8 # E : find nulls in second partial
-
- addq a0, 8, a0 # E :
- subq a2, 1, a2 # E :
- bne t8, $u_late_head_exit # U :
- nop
-
- /* Finally, we've got all the stupid leading edge cases taken care
- of and we can set up to enter the main loop. */
-
- extql t2, a1, t1 # U : position hi-bits of lo word
- EX( ldq_u t2, 8(a1) ) # L : read next high-order source word
- addq a1, 8, a1 # E :
- cmpbge zero, t2, t8 # E :
-
- beq a2, $u_eoc # U :
- bne t8, $u_eos # U :
- nop
- nop
-
- /* Unaligned copy main loop. In order to avoid reading too much,
- the loop is structured to detect zeros in aligned source words.
- This has, unfortunately, effectively pulled half of a loop
- iteration out into the head and half into the tail, but it does
- prevent nastiness from accumulating in the very thing we want
- to run as fast as possible.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word
-
- We further know that t2 does not contain a null terminator. */
-
- /*
- * Extra nops here:
- * separate load quads from store quads
- * only one branch/quad to permit predictor training
- */
-
- .align 4
-$u_loop:
- extqh t2, a1, t0 # U : extract high bits for current word
- addq a1, 8, a1 # E :
- extql t2, a1, t3 # U : extract low bits for next time
- addq a0, 8, a0 # E :
-
- or t0, t1, t0 # E : current dst word now complete
- EX( ldq_u t2, 0(a1) ) # L : load high word for next time
- subq a2, 1, a2 # E :
- nop
-
- stq_u t0, -8(a0) # L : save the current word
- mov t3, t1 # E :
- cmpbge zero, t2, t8 # E : test new word for eos
- beq a2, $u_eoc # U :
-
- beq t8, $u_loop # U :
- nop
- nop
- nop
-
- /* We've found a zero somewhere in the source word we just read.
- If it resides in the lower half, we have one (probably partial)
- word to write out, and if it resides in the upper half, we
- have one full and one partial word left to write out.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word. */
- .align 4
-$u_eos:
- extqh t2, a1, t0 # U :
- or t0, t1, t0 # E : first (partial) source word complete
- cmpbge zero, t0, t8 # E : is the null in this first bit?
- nop
-
- bne t8, $u_final # U :
- stq_u t0, 0(a0) # L : the null was in the high-order bits
- addq a0, 8, a0 # E :
- subq a2, 1, a2 # E :
-
- .align 4
-$u_late_head_exit:
- extql t2, a1, t0 # U :
- cmpbge zero, t0, t8 # E :
- or t8, t10, t6 # E :
- cmoveq a2, t6, t8 # E :
-
- /* Take care of a final (probably partial) result word.
- On entry to this basic block:
- t0 == assembled source word
- t8 == cmpbge mask that found the null. */
- .align 4
-$u_final:
- negq t8, t6 # E : isolate low bit set
- and t6, t8, t12 # E :
- ldq_u t1, 0(a0) # L :
- subq t12, 1, t6 # E :
-
- or t6, t12, t8 # E :
- zapnot t0, t8, t0 # U : kill source bytes > null
- zap t1, t8, t1 # U : kill dest bytes <= null
- or t0, t1, t0 # E :
-
- stq_u t0, 0(a0) # E :
- br $finish_up # U :
- nop
- nop
-
- .align 4
-$u_eoc: # end-of-count
- extqh t2, a1, t0 # U :
- or t0, t1, t0 # E :
- cmpbge zero, t0, t8 # E :
- nop
-
- .align 4
-$u_eocfin: # end-of-count, final word
- or t10, t8, t8 # E :
- br $u_final # U :
- nop
- nop
-
- /* Unaligned copy entry point. */
- .align 4
-$unaligned:
-
- srl a3, 3, a2 # U : a2 = loop counter = (count - 1)/8
- and a0, 7, t4 # E : find dest misalignment
- and a1, 7, t5 # E : find src misalignment
- mov zero, t0 # E :
-
- /* Conditionally load the first destination word and a bytemask
- with 0xff indicating that the destination byte is sacrosanct. */
-
- mov zero, t6 # E :
- beq t4, 1f # U :
- ldq_u t0, 0(a0) # L :
- lda t6, -1 # E :
-
- mskql t6, a0, t6 # E :
- nop
- nop
- nop
-
- .align 4
-1:
- subq a1, t4, a1 # E : sub dest misalignment from src addr
- /* If source misalignment is larger than dest misalignment, we need
- extra startup checks to avoid SEGV. */
- cmplt t4, t5, t12 # E :
- extql t1, a1, t1 # U : shift src into place
- lda t2, -1 # E : for creating masks later
-
- beq t12, $u_head # U :
- mskqh t2, t5, t2 # U : begin src byte validity mask
- cmpbge zero, t1, t8 # E : is there a zero?
- nop
-
- extql t2, a1, t2 # U :
- or t8, t10, t5 # E : test for end-of-count too
- cmpbge zero, t2, t3 # E :
- cmoveq a2, t5, t8 # E : Latency=2, extra map slot
-
- nop # E : goes with cmov
- andnot t8, t3, t8 # E :
- beq t8, $u_head # U :
- nop
-
- /* At this point we've found a zero in the first partial word of
- the source. We need to isolate the valid source data and mask
- it into the original destination data. (Incidentally, we know
- that we'll need at least one byte of that original dest word.) */
-
- ldq_u t0, 0(a0) # L :
- negq t8, t6 # E : build bitmask of bytes <= zero
- mskqh t1, t4, t1 # U :
- and t6, t8, t12 # E :
-
- subq t12, 1, t6 # E :
- or t6, t12, t8 # E :
- zapnot t2, t8, t2 # U : prepare source word; mirror changes
- zapnot t1, t8, t1 # U : to source validity mask
-
- andnot t0, t2, t0 # E : zero place for source to reside
- or t0, t1, t0 # E : and put it there
- stq_u t0, 0(a0) # L :
- nop
-
- .align 4
-$finish_up:
- zapnot t0, t12, t4 # U : was last byte written null?
- and t12, 0xf0, t3 # E : binary search for the address of the
- cmovne t4, 1, t4 # E : Latency=2, extra map slot
- nop # E : with cmovne
-
- and t12, 0xcc, t2 # E : last byte written
- and t12, 0xaa, t1 # E :
- cmovne t3, 4, t3 # E : Latency=2, extra map slot
- nop # E : with cmovne
-
- bic a0, 7, t0
- cmovne t2, 2, t2 # E : Latency=2, extra map slot
- nop # E : with cmovne
- nop
-
- cmovne t1, 1, t1 # E : Latency=2, extra map slot
- nop # E : with cmovne
- addq t0, t3, t0 # E :
- addq t1, t2, t1 # E :
-
- addq t0, t1, t0 # E :
- addq t0, t4, t0 # add one if we filled the buffer
- subq t0, v0, v0 # find string length
- ret # L0 :
-
- .align 4
-$zerolength:
- nop
- nop
- nop
- clr v0
-
-$exception:
- nop
- nop
- nop
- ret
-
- .end __strncpy_from_user
diff --git a/arch/alpha/lib/ev67-strlen_user.S b/arch/alpha/lib/ev67-strlen_user.S
deleted file mode 100644
index 57e0d77..0000000
--- a/arch/alpha/lib/ev67-strlen_user.S
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * arch/alpha/lib/ev67-strlen_user.S
- * 21264 version contributed by Rick Gorton <rick.gorton@api-networks.com>
- *
- * Return the length of the string including the NULL terminator
- * (strlen+1) or zero if an error occurred.
- *
- * In places where it is critical to limit the processing time,
- * and the data is not trusted, strnlen_user() should be used.
- * It will return a value greater than its second argument if
- * that limit would be exceeded. This implementation is allowed
- * to access memory beyond the limit, but will not cross a page
- * boundary when doing so.
- *
- * Much of the information about 21264 scheduling/coding comes from:
- * Compiler Writer's Guide for the Alpha 21264
- * abbreviated as 'CWG' in other comments here
- * ftp.digital.com/pub/Digital/info/semiconductor/literature/dsc-library.html
- * Scheduling notation:
- * E - either cluster
- * U - upper subcluster; U0 - subcluster U0; U1 - subcluster U1
- * L - lower subcluster; L0 - subcluster L0; L1 - subcluster L1
- * Try not to change the actual algorithm if possible for consistency.
- */
-
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda v0, $exception-99b(zero); \
- .previous
-
-
- .set noreorder
- .set noat
- .text
-
- .globl __strlen_user
- .ent __strlen_user
- .frame sp, 0, ra
-
- .align 4
-__strlen_user:
- ldah a1, 32767(zero) # do not use plain strlen_user() for strings
- # that might be almost 2 GB long; you should
- # be using strnlen_user() instead
- nop
- nop
- nop
-
- .globl __strnlen_user
-
- .align 4
-__strnlen_user:
- .prologue 0
- EX( ldq_u t0, 0(a0) ) # L : load first quadword (a0 may be misaligned)
- lda t1, -1(zero) # E :
-
- insqh t1, a0, t1 # U :
- andnot a0, 7, v0 # E :
- or t1, t0, t0 # E :
- subq a0, 1, a0 # E : get our +1 for the return
-
- cmpbge zero, t0, t1 # E : t1 <- bitmask: bit i == 1 <==> i-th byte == 0
- subq a1, 7, t2 # E :
- subq a0, v0, t0 # E :
- bne t1, $found # U :
-
- addq t2, t0, t2 # E :
- addq a1, 1, a1 # E :
- nop # E :
- nop # E :
-
- .align 4
-$loop: ble t2, $limit # U :
- EX( ldq t0, 8(v0) ) # L :
- nop # E :
- nop # E :
-
- cmpbge zero, t0, t1 # E :
- subq t2, 8, t2 # E :
- addq v0, 8, v0 # E : addr += 8
- beq t1, $loop # U :
-
-$found: cttz t1, t2 # U0 :
- addq v0, t2, v0 # E :
- subq v0, a0, v0 # E :
- ret # L0 :
-
-$exception:
- nop
- nop
- nop
- ret
-
- .align 4 # currently redundant
-$limit:
- nop
- nop
- subq a1, t2, v0
- ret
-
- .end __strlen_user
diff --git a/arch/alpha/lib/strlen_user.S b/arch/alpha/lib/strlen_user.S
deleted file mode 100644
index 508a18e..0000000
--- a/arch/alpha/lib/strlen_user.S
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * arch/alpha/lib/strlen_user.S
- *
- * Return the length of the string including the NUL terminator
- * (strlen+1) or zero if an error occurred.
- *
- * In places where it is critical to limit the processing time,
- * and the data is not trusted, strnlen_user() should be used.
- * It will return a value greater than its second argument if
- * that limit would be exceeded. This implementation is allowed
- * to access memory beyond the limit, but will not cross a page
- * boundary when doing so.
- */
-
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda v0, $exception-99b(zero); \
- .previous
-
-
- .set noreorder
- .set noat
- .text
-
- .globl __strlen_user
- .ent __strlen_user
- .frame sp, 0, ra
-
- .align 3
-__strlen_user:
- ldah a1, 32767(zero) # do not use plain strlen_user() for strings
- # that might be almost 2 GB long; you should
- # be using strnlen_user() instead
-
- .globl __strnlen_user
-
- .align 3
-__strnlen_user:
- .prologue 0
-
- EX( ldq_u t0, 0(a0) ) # load first quadword (a0 may be misaligned)
- lda t1, -1(zero)
- insqh t1, a0, t1
- andnot a0, 7, v0
- or t1, t0, t0
- subq a0, 1, a0 # get our +1 for the return
- cmpbge zero, t0, t1 # t1 <- bitmask: bit i == 1 <==> i-th byte == 0
- subq a1, 7, t2
- subq a0, v0, t0
- bne t1, $found
-
- addq t2, t0, t2
- addq a1, 1, a1
-
- .align 3
-$loop: ble t2, $limit
- EX( ldq t0, 8(v0) )
- subq t2, 8, t2
- addq v0, 8, v0 # addr += 8
- cmpbge zero, t0, t1
- beq t1, $loop
-
-$found: negq t1, t2 # clear all but least set bit
- and t1, t2, t1
-
- and t1, 0xf0, t2 # binary search for that set bit
- and t1, 0xcc, t3
- and t1, 0xaa, t4
- cmovne t2, 4, t2
- cmovne t3, 2, t3
- cmovne t4, 1, t4
- addq t2, t3, t2
- addq v0, t4, v0
- addq v0, t2, v0
- nop # dual issue next two on ev4 and ev5
- subq v0, a0, v0
-$exception:
- ret
-
- .align 3 # currently redundant
-$limit:
- subq a1, t2, v0
- ret
-
- .end __strlen_user
diff --git a/arch/alpha/lib/strncpy_from_user.S b/arch/alpha/lib/strncpy_from_user.S
deleted file mode 100644
index 73ee211..0000000
--- a/arch/alpha/lib/strncpy_from_user.S
+++ /dev/null
@@ -1,339 +0,0 @@
-/*
- * arch/alpha/lib/strncpy_from_user.S
- * Contributed by Richard Henderson (rth@tamu.edu)
- *
- * Just like strncpy except in the return value:
- *
- * -EFAULT if an exception occurs before the terminator is copied.
- * N if the buffer filled.
- *
- * Otherwise the length of the string is returned.
- */
-
-
-#include <asm/errno.h>
-#include <asm/regdef.h>
-
-
-/* Allow an exception for an insn; exit if we get one. */
-#define EX(x,y...) \
- 99: x,##y; \
- .section __ex_table,"a"; \
- .long 99b - .; \
- lda $31, $exception-99b($0); \
- .previous
-
-
- .set noat
- .set noreorder
- .text
-
- .globl __strncpy_from_user
- .ent __strncpy_from_user
- .frame $30, 0, $26
- .prologue 0
-
- .align 3
-$aligned:
- /* On entry to this basic block:
- t0 == the first destination word for masking back in
- t1 == the first source word. */
-
- /* Create the 1st output word and detect 0's in the 1st input word. */
- lda t2, -1 # e1 : build a mask against false zero
- mskqh t2, a1, t2 # e0 : detection in the src word
- mskqh t1, a1, t3 # e0 :
- ornot t1, t2, t2 # .. e1 :
- mskql t0, a1, t0 # e0 : assemble the first output word
- cmpbge zero, t2, t8 # .. e1 : bits set iff null found
- or t0, t3, t0 # e0 :
- beq a2, $a_eoc # .. e1 :
- bne t8, $a_eos # .. e1 :
-
- /* On entry to this basic block:
- t0 == a source word not containing a null. */
-
-$a_loop:
- stq_u t0, 0(a0) # e0 :
- addq a0, 8, a0 # .. e1 :
- EX( ldq_u t0, 0(a1) ) # e0 :
- addq a1, 8, a1 # .. e1 :
- subq a2, 1, a2 # e0 :
- cmpbge zero, t0, t8 # .. e1 (stall)
- beq a2, $a_eoc # e1 :
- beq t8, $a_loop # e1 :
-
- /* Take care of the final (partial) word store. At this point
- the end-of-count bit is set in t8 iff it applies.
-
- On entry to this basic block we have:
- t0 == the source word containing the null
- t8 == the cmpbge mask that found it. */
-
-$a_eos:
- negq t8, t12 # e0 : find low bit set
- and t8, t12, t12 # e1 (stall)
-
- /* For the sake of the cache, don't read a destination word
- if we're not going to need it. */
- and t12, 0x80, t6 # e0 :
- bne t6, 1f # .. e1 (zdb)
-
- /* We're doing a partial word store and so need to combine
- our source and original destination words. */
- ldq_u t1, 0(a0) # e0 :
- subq t12, 1, t6 # .. e1 :
- or t12, t6, t8 # e0 :
- unop #
- zapnot t0, t8, t0 # e0 : clear src bytes > null
- zap t1, t8, t1 # .. e1 : clear dst bytes <= null
- or t0, t1, t0 # e1 :
-
-1: stq_u t0, 0(a0)
- br $finish_up
-
- /* Add the end-of-count bit to the eos detection bitmask. */
-$a_eoc:
- or t10, t8, t8
- br $a_eos
-
- /*** The Function Entry Point ***/
- .align 3
-__strncpy_from_user:
- mov a0, v0 # save the string start
- beq a2, $zerolength
-
- /* Are source and destination co-aligned? */
- xor a0, a1, t1 # e0 :
- and a0, 7, t0 # .. e1 : find dest misalignment
- and t1, 7, t1 # e0 :
- addq a2, t0, a2 # .. e1 : bias count by dest misalignment
- subq a2, 1, a2 # e0 :
- and a2, 7, t2 # e1 :
- srl a2, 3, a2 # e0 : a2 = loop counter = (count - 1)/8
- addq zero, 1, t10 # .. e1 :
- sll t10, t2, t10 # e0 : t10 = bitmask of last count byte
- bne t1, $unaligned # .. e1 :
-
- /* We are co-aligned; take care of a partial first word. */
-
- EX( ldq_u t1, 0(a1) ) # e0 : load first src word
- addq a1, 8, a1 # .. e1 :
-
- beq t0, $aligned # avoid loading dest word if not needed
- ldq_u t0, 0(a0) # e0 :
- br $aligned # .. e1 :
-
-
-/* The source and destination are not co-aligned. Align the destination
- and cope. We have to be very careful about not reading too much and
- causing a SEGV. */
-
- .align 3
-$u_head:
- /* We know just enough now to be able to assemble the first
- full source word. We can still find a zero at the end of it
- that prevents us from outputting the whole thing.
-
- On entry to this basic block:
- t0 == the first dest word, unmasked
- t1 == the shifted low bits of the first source word
- t6 == bytemask that is -1 in dest word bytes */
-
- EX( ldq_u t2, 8(a1) ) # e0 : load second src word
- addq a1, 8, a1 # .. e1 :
- mskql t0, a0, t0 # e0 : mask trailing garbage in dst
- extqh t2, a1, t4 # e0 :
- or t1, t4, t1 # e1 : first aligned src word complete
- mskqh t1, a0, t1 # e0 : mask leading garbage in src
- or t0, t1, t0 # e0 : first output word complete
- or t0, t6, t6 # e1 : mask original data for zero test
- cmpbge zero, t6, t8 # e0 :
- beq a2, $u_eocfin # .. e1 :
- bne t8, $u_final # e1 :
-
- lda t6, -1 # e1 : mask out the bits we have
- mskql t6, a1, t6 # e0 : already seen
- stq_u t0, 0(a0) # e0 : store first output word
- or t6, t2, t2 # .. e1 :
- cmpbge zero, t2, t8 # e0 : find nulls in second partial
- addq a0, 8, a0 # .. e1 :
- subq a2, 1, a2 # e0 :
- bne t8, $u_late_head_exit # .. e1 :
-
- /* Finally, we've got all the stupid leading edge cases taken care
- of and we can set up to enter the main loop. */
-
- extql t2, a1, t1 # e0 : position hi-bits of lo word
- EX( ldq_u t2, 8(a1) ) # .. e1 : read next high-order source word
- addq a1, 8, a1 # e0 :
- cmpbge zero, t2, t8 # e1 (stall)
- beq a2, $u_eoc # e1 :
- bne t8, $u_eos # e1 :
-
- /* Unaligned copy main loop. In order to avoid reading too much,
- the loop is structured to detect zeros in aligned source words.
- This has, unfortunately, effectively pulled half of a loop
- iteration out into the head and half into the tail, but it does
- prevent nastiness from accumulating in the very thing we want
- to run as fast as possible.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word
-
- We further know that t2 does not contain a null terminator. */
-
- .align 3
-$u_loop:
- extqh t2, a1, t0 # e0 : extract high bits for current word
- addq a1, 8, a1 # .. e1 :
- extql t2, a1, t3 # e0 : extract low bits for next time
- addq a0, 8, a0 # .. e1 :
- or t0, t1, t0 # e0 : current dst word now complete
- EX( ldq_u t2, 0(a1) ) # .. e1 : load high word for next time
- stq_u t0, -8(a0) # e0 : save the current word
- mov t3, t1 # .. e1 :
- subq a2, 1, a2 # e0 :
- cmpbge zero, t2, t8 # .. e1 : test new word for eos
- beq a2, $u_eoc # e1 :
- beq t8, $u_loop # e1 :
-
- /* We've found a zero somewhere in the source word we just read.
- If it resides in the lower half, we have one (probably partial)
- word to write out, and if it resides in the upper half, we
- have one full and one partial word left to write out.
-
- On entry to this basic block:
- t1 == the shifted high-order bits from the previous source word
- t2 == the unshifted current source word. */
-$u_eos:
- extqh t2, a1, t0 # e0 :
- or t0, t1, t0 # e1 : first (partial) source word complete
-
- cmpbge zero, t0, t8 # e0 : is the null in this first bit?
- bne t8, $u_final # .. e1 (zdb)
-
- stq_u t0, 0(a0) # e0 : the null was in the high-order bits
- addq a0, 8, a0 # .. e1 :
- subq a2, 1, a2 # e1 :
-
-$u_late_head_exit:
- extql t2, a1, t0 # .. e0 :
- cmpbge zero, t0, t8 # e0 :
- or t8, t10, t6 # e1 :
- cmoveq a2, t6, t8 # e0 :
- nop # .. e1 :
-
- /* Take care of a final (probably partial) result word.
- On entry to this basic block:
- t0 == assembled source word
- t8 == cmpbge mask that found the null. */
-$u_final:
- negq t8, t6 # e0 : isolate low bit set
- and t6, t8, t12 # e1 :
-
- and t12, 0x80, t6 # e0 : avoid dest word load if we can
- bne t6, 1f # .. e1 (zdb)
-
- ldq_u t1, 0(a0) # e0 :
- subq t12, 1, t6 # .. e1 :
- or t6, t12, t8 # e0 :
- zapnot t0, t8, t0 # .. e1 : kill source bytes > null
- zap t1, t8, t1 # e0 : kill dest bytes <= null
- or t0, t1, t0 # e1 :
-
-1: stq_u t0, 0(a0) # e0 :
- br $finish_up
-
-$u_eoc: # end-of-count
- extqh t2, a1, t0
- or t0, t1, t0
- cmpbge zero, t0, t8
-
-$u_eocfin: # end-of-count, final word
- or t10, t8, t8
- br $u_final
-
- /* Unaligned copy entry point. */
- .align 3
-$unaligned:
-
- EX( ldq_u t1, 0(a1) ) # e0 : load first source word
-
- and a0, 7, t4 # .. e1 : find dest misalignment
- and a1, 7, t5 # e0 : find src misalignment
-
- /* Conditionally load the first destination word and a bytemask
- with 0xff indicating that the destination byte is sacrosanct. */
-
- mov zero, t0 # .. e1 :
- mov zero, t6 # e0 :
- beq t4, 1f # .. e1 :
- ldq_u t0, 0(a0) # e0 :
- lda t6, -1 # .. e1 :
- mskql t6, a0, t6 # e0 :
-1:
- subq a1, t4, a1 # .. e1 : sub dest misalignment from src addr
-
- /* If source misalignment is larger than dest misalignment, we need
- extra startup checks to avoid SEGV. */
-
- cmplt t4, t5, t12 # e1 :
- extql t1, a1, t1 # .. e0 : shift src into place
- lda t2, -1 # e0 : for creating masks later
- beq t12, $u_head # e1 :
-
- mskqh t2, t5, t2 # e0 : begin src byte validity mask
- cmpbge zero, t1, t8 # .. e1 : is there a zero?
- extql t2, a1, t2 # e0 :
- or t8, t10, t5 # .. e1 : test for end-of-count too
- cmpbge zero, t2, t3 # e0 :
- cmoveq a2, t5, t8 # .. e1 :
- andnot t8, t3, t8 # e0 :
- beq t8, $u_head # .. e1 (zdb)
-
- /* At this point we've found a zero in the first partial word of
- the source. We need to isolate the valid source data and mask
- it into the original destination data. (Incidentally, we know
- that we'll need at least one byte of that original dest word.) */
-
- ldq_u t0, 0(a0) # e0 :
- negq t8, t6 # .. e1 : build bitmask of bytes <= zero
- mskqh t1, t4, t1 # e0 :
- and t6, t8, t12 # .. e1 :
- subq t12, 1, t6 # e0 :
- or t6, t12, t8 # e1 :
-
- zapnot t2, t8, t2 # e0 : prepare source word; mirror changes
- zapnot t1, t8, t1 # .. e1 : to source validity mask
-
- andnot t0, t2, t0 # e0 : zero place for source to reside
- or t0, t1, t0 # e1 : and put it there
- stq_u t0, 0(a0) # e0 :
-
-$finish_up:
- zapnot t0, t12, t4 # was last byte written null?
- cmovne t4, 1, t4
-
- and t12, 0xf0, t3 # binary search for the address of the
- and t12, 0xcc, t2 # last byte written
- and t12, 0xaa, t1
- bic a0, 7, t0
- cmovne t3, 4, t3
- cmovne t2, 2, t2
- cmovne t1, 1, t1
- addq t0, t3, t0
- addq t1, t2, t1
- addq t0, t1, t0
- addq t0, t4, t0 # add one if we filled the buffer
-
- subq t0, v0, v0 # find string length
- ret
-
-$zerolength:
- clr v0
-$exception:
- ret
-
- .end __strncpy_from_user
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c
index 5eecab1..0c4132d 100644
--- a/arch/alpha/mm/fault.c
+++ b/arch/alpha/mm/fault.c
@@ -89,6 +89,8 @@
const struct exception_table_entry *fixup;
int fault, si_code = SEGV_MAPERR;
siginfo_t info;
+ unsigned int flags = (FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE |
+ (cause > 0 ? FAULT_FLAG_WRITE : 0));
/* As of EV6, a load into $31/$f31 is a prefetch, and never faults
(or is suppressed by the PALcode). Support that for older CPUs
@@ -114,6 +116,7 @@
goto vmalloc_fault;
#endif
+retry:
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (!vma)
@@ -144,8 +147,11 @@
/* If for any reason at all we couldn't handle the fault,
make sure we exit gracefully rather than endlessly redo
the fault. */
- fault = handle_mm_fault(mm, vma, address, cause > 0 ? FAULT_FLAG_WRITE : 0);
- up_read(&mm->mmap_sem);
+ fault = handle_mm_fault(mm, vma, address, flags);
+
+ if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+ return;
+
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM)
goto out_of_memory;
@@ -153,10 +159,26 @@
goto do_sigbus;
BUG();
}
- if (fault & VM_FAULT_MAJOR)
- current->maj_flt++;
- else
- current->min_flt++;
+
+ if (flags & FAULT_FLAG_ALLOW_RETRY) {
+ if (fault & VM_FAULT_MAJOR)
+ current->maj_flt++;
+ else
+ current->min_flt++;
+ if (fault & VM_FAULT_RETRY) {
+ flags &= ~FAULT_FLAG_ALLOW_RETRY;
+
+ /* No need to up_read(&mm->mmap_sem) as we would
+ * have already released it in __lock_page_or_retry
+ * in mm/filemap.c.
+ */
+
+ goto retry;
+ }
+ }
+
+ up_read(&mm->mmap_sem);
+
return;
/* Something tried to access memory that isn't in our memory map.
@@ -186,12 +208,14 @@
/* We ran out of memory, or some other thing happened to us that
made us unable to handle the page fault gracefully. */
out_of_memory:
+ up_read(&mm->mmap_sem);
if (!user_mode(regs))
goto no_context;
pagefault_out_of_memory();
return;
do_sigbus:
+ up_read(&mm->mmap_sem);
/* Send a sigbus, regardless of whether we were in kernel
or user mode. */
info.si_signo = SIGBUS;
diff --git a/arch/alpha/oprofile/common.c b/arch/alpha/oprofile/common.c
index a0a5d27..b8ce18f 100644
--- a/arch/alpha/oprofile/common.c
+++ b/arch/alpha/oprofile/common.c
@@ -12,6 +12,7 @@
#include <linux/smp.h>
#include <linux/errno.h>
#include <asm/ptrace.h>
+#include <asm/special_insns.h>
#include "op_impl.h"
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e91c7cd..6d6e18f 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -38,7 +38,6 @@
select HARDIRQS_SW_RESEND
select GENERIC_IRQ_PROBE
select GENERIC_IRQ_SHOW
- select GENERIC_IRQ_PROBE
select ARCH_WANT_IPC_PARSE_VERSION
select HARDIRQS_SW_RESEND
select CPU_PM if (SUSPEND || CPU_IDLE)
@@ -126,11 +125,6 @@
bool
default y
-config GENERIC_LOCKBREAK
- bool
- default y
- depends on SMP && PREEMPT
-
config RWSEM_GENERIC_SPINLOCK
bool
default y
diff --git a/arch/arm/boot/dts/imx23.dtsi b/arch/arm/boot/dts/imx23.dtsi
index a874dbf..e613831 100644
--- a/arch/arm/boot/dts/imx23.dtsi
+++ b/arch/arm/boot/dts/imx23.dtsi
@@ -51,11 +51,11 @@
dma-apbh@80004000 {
compatible = "fsl,imx23-dma-apbh";
- reg = <0x80004000 2000>;
+ reg = <0x80004000 0x2000>;
};
ecc@80008000 {
- reg = <0x80008000 2000>;
+ reg = <0x80008000 0x2000>;
status = "disabled";
};
@@ -63,7 +63,7 @@
compatible = "fsl,imx23-gpmi-nand";
#address-cells = <1>;
#size-cells = <1>;
- reg = <0x8000c000 2000>, <0x8000a000 2000>;
+ reg = <0x8000c000 0x2000>, <0x8000a000 0x2000>;
reg-names = "gpmi-nand", "bch";
interrupts = <13>, <56>;
interrupt-names = "gpmi-dma", "bch";
@@ -72,14 +72,14 @@
};
ssp0: ssp@80010000 {
- reg = <0x80010000 2000>;
+ reg = <0x80010000 0x2000>;
interrupts = <15 14>;
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
etm@80014000 {
- reg = <0x80014000 2000>;
+ reg = <0x80014000 0x2000>;
status = "disabled";
};
@@ -87,7 +87,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx23-pinctrl", "simple-bus";
- reg = <0x80018000 2000>;
+ reg = <0x80018000 0x2000>;
gpio0: gpio@0 {
compatible = "fsl,imx23-gpio", "fsl,mxs-gpio";
@@ -273,32 +273,32 @@
};
emi@80020000 {
- reg = <0x80020000 2000>;
+ reg = <0x80020000 0x2000>;
status = "disabled";
};
dma-apbx@80024000 {
compatible = "fsl,imx23-dma-apbx";
- reg = <0x80024000 2000>;
+ reg = <0x80024000 0x2000>;
};
dcp@80028000 {
- reg = <0x80028000 2000>;
+ reg = <0x80028000 0x2000>;
status = "disabled";
};
pxp@8002a000 {
- reg = <0x8002a000 2000>;
+ reg = <0x8002a000 0x2000>;
status = "disabled";
};
ocotp@8002c000 {
- reg = <0x8002c000 2000>;
+ reg = <0x8002c000 0x2000>;
status = "disabled";
};
axi-ahb@8002e000 {
- reg = <0x8002e000 2000>;
+ reg = <0x8002e000 0x2000>;
status = "disabled";
};
@@ -310,14 +310,14 @@
};
ssp1: ssp@80034000 {
- reg = <0x80034000 2000>;
+ reg = <0x80034000 0x2000>;
interrupts = <2 20>;
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
tvenc@80038000 {
- reg = <0x80038000 2000>;
+ reg = <0x80038000 0x2000>;
status = "disabled";
};
};
@@ -330,37 +330,37 @@
ranges;
clkctl@80040000 {
- reg = <0x80040000 2000>;
+ reg = <0x80040000 0x2000>;
status = "disabled";
};
saif0: saif@80042000 {
- reg = <0x80042000 2000>;
+ reg = <0x80042000 0x2000>;
status = "disabled";
};
power@80044000 {
- reg = <0x80044000 2000>;
+ reg = <0x80044000 0x2000>;
status = "disabled";
};
saif1: saif@80046000 {
- reg = <0x80046000 2000>;
+ reg = <0x80046000 0x2000>;
status = "disabled";
};
audio-out@80048000 {
- reg = <0x80048000 2000>;
+ reg = <0x80048000 0x2000>;
status = "disabled";
};
audio-in@8004c000 {
- reg = <0x8004c000 2000>;
+ reg = <0x8004c000 0x2000>;
status = "disabled";
};
lradc@80050000 {
- reg = <0x80050000 2000>;
+ reg = <0x80050000 0x2000>;
status = "disabled";
};
@@ -370,26 +370,26 @@
};
i2c@80058000 {
- reg = <0x80058000 2000>;
+ reg = <0x80058000 0x2000>;
status = "disabled";
};
rtc@8005c000 {
compatible = "fsl,imx23-rtc", "fsl,stmp3xxx-rtc";
- reg = <0x8005c000 2000>;
+ reg = <0x8005c000 0x2000>;
interrupts = <22>;
};
pwm: pwm@80064000 {
compatible = "fsl,imx23-pwm";
- reg = <0x80064000 2000>;
+ reg = <0x80064000 0x2000>;
#pwm-cells = <2>;
fsl,pwm-number = <5>;
status = "disabled";
};
timrot@80068000 {
- reg = <0x80068000 2000>;
+ reg = <0x80068000 0x2000>;
status = "disabled";
};
@@ -429,7 +429,7 @@
ranges;
usbctrl@80080000 {
- reg = <0x80080000 0x10000>;
+ reg = <0x80080000 0x40000>;
status = "disabled";
};
};
diff --git a/arch/arm/boot/dts/imx27-3ds.dts b/arch/arm/boot/dts/imx27-3ds.dts
index d3f8296..0a8978a 100644
--- a/arch/arm/boot/dts/imx27-3ds.dts
+++ b/arch/arm/boot/dts/imx27-3ds.dts
@@ -27,7 +27,7 @@
status = "okay";
};
- uart@1000a000 {
+ uart1: serial@1000a000 {
fsl,uart-has-rtscts;
status = "okay";
};
diff --git a/arch/arm/boot/dts/imx27.dtsi b/arch/arm/boot/dts/imx27.dtsi
index 00bae3a..5303ab6 100644
--- a/arch/arm/boot/dts/imx27.dtsi
+++ b/arch/arm/boot/dts/imx27.dtsi
@@ -19,6 +19,12 @@
serial3 = &uart4;
serial4 = &uart5;
serial5 = &uart6;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
};
avic: avic-interrupt-controller@e0000000 {
diff --git a/arch/arm/boot/dts/imx28.dtsi b/arch/arm/boot/dts/imx28.dtsi
index 787efac..3fa6d19 100644
--- a/arch/arm/boot/dts/imx28.dtsi
+++ b/arch/arm/boot/dts/imx28.dtsi
@@ -57,18 +57,18 @@
};
hsadc@80002000 {
- reg = <0x80002000 2000>;
+ reg = <0x80002000 0x2000>;
interrupts = <13 87>;
status = "disabled";
};
dma-apbh@80004000 {
compatible = "fsl,imx28-dma-apbh";
- reg = <0x80004000 2000>;
+ reg = <0x80004000 0x2000>;
};
perfmon@80006000 {
- reg = <0x80006000 800>;
+ reg = <0x80006000 0x800>;
interrupts = <27>;
status = "disabled";
};
@@ -77,7 +77,7 @@
compatible = "fsl,imx28-gpmi-nand";
#address-cells = <1>;
#size-cells = <1>;
- reg = <0x8000c000 2000>, <0x8000a000 2000>;
+ reg = <0x8000c000 0x2000>, <0x8000a000 0x2000>;
reg-names = "gpmi-nand", "bch";
interrupts = <88>, <41>;
interrupt-names = "gpmi-dma", "bch";
@@ -86,28 +86,28 @@
};
ssp0: ssp@80010000 {
- reg = <0x80010000 2000>;
+ reg = <0x80010000 0x2000>;
interrupts = <96 82>;
fsl,ssp-dma-channel = <0>;
status = "disabled";
};
ssp1: ssp@80012000 {
- reg = <0x80012000 2000>;
+ reg = <0x80012000 0x2000>;
interrupts = <97 83>;
fsl,ssp-dma-channel = <1>;
status = "disabled";
};
ssp2: ssp@80014000 {
- reg = <0x80014000 2000>;
+ reg = <0x80014000 0x2000>;
interrupts = <98 84>;
fsl,ssp-dma-channel = <2>;
status = "disabled";
};
ssp3: ssp@80016000 {
- reg = <0x80016000 2000>;
+ reg = <0x80016000 0x2000>;
interrupts = <99 85>;
fsl,ssp-dma-channel = <3>;
status = "disabled";
@@ -117,7 +117,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-pinctrl", "simple-bus";
- reg = <0x80018000 2000>;
+ reg = <0x80018000 0x2000>;
gpio0: gpio@0 {
compatible = "fsl,imx28-gpio", "fsl,mxs-gpio";
@@ -510,96 +510,96 @@
};
digctl@8001c000 {
- reg = <0x8001c000 2000>;
+ reg = <0x8001c000 0x2000>;
interrupts = <89>;
status = "disabled";
};
etm@80022000 {
- reg = <0x80022000 2000>;
+ reg = <0x80022000 0x2000>;
status = "disabled";
};
dma-apbx@80024000 {
compatible = "fsl,imx28-dma-apbx";
- reg = <0x80024000 2000>;
+ reg = <0x80024000 0x2000>;
};
dcp@80028000 {
- reg = <0x80028000 2000>;
+ reg = <0x80028000 0x2000>;
interrupts = <52 53 54>;
status = "disabled";
};
pxp@8002a000 {
- reg = <0x8002a000 2000>;
+ reg = <0x8002a000 0x2000>;
interrupts = <39>;
status = "disabled";
};
ocotp@8002c000 {
- reg = <0x8002c000 2000>;
+ reg = <0x8002c000 0x2000>;
status = "disabled";
};
axi-ahb@8002e000 {
- reg = <0x8002e000 2000>;
+ reg = <0x8002e000 0x2000>;
status = "disabled";
};
lcdif@80030000 {
compatible = "fsl,imx28-lcdif";
- reg = <0x80030000 2000>;
+ reg = <0x80030000 0x2000>;
interrupts = <38 86>;
status = "disabled";
};
can0: can@80032000 {
compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
- reg = <0x80032000 2000>;
+ reg = <0x80032000 0x2000>;
interrupts = <8>;
status = "disabled";
};
can1: can@80034000 {
compatible = "fsl,imx28-flexcan", "fsl,p1010-flexcan";
- reg = <0x80034000 2000>;
+ reg = <0x80034000 0x2000>;
interrupts = <9>;
status = "disabled";
};
simdbg@8003c000 {
- reg = <0x8003c000 200>;
+ reg = <0x8003c000 0x200>;
status = "disabled";
};
simgpmisel@8003c200 {
- reg = <0x8003c200 100>;
+ reg = <0x8003c200 0x100>;
status = "disabled";
};
simsspsel@8003c300 {
- reg = <0x8003c300 100>;
+ reg = <0x8003c300 0x100>;
status = "disabled";
};
simmemsel@8003c400 {
- reg = <0x8003c400 100>;
+ reg = <0x8003c400 0x100>;
status = "disabled";
};
gpiomon@8003c500 {
- reg = <0x8003c500 100>;
+ reg = <0x8003c500 0x100>;
status = "disabled";
};
simenet@8003c700 {
- reg = <0x8003c700 100>;
+ reg = <0x8003c700 0x100>;
status = "disabled";
};
armjtag@8003c800 {
- reg = <0x8003c800 100>;
+ reg = <0x8003c800 0x100>;
status = "disabled";
};
};
@@ -612,45 +612,45 @@
ranges;
clkctl@80040000 {
- reg = <0x80040000 2000>;
+ reg = <0x80040000 0x2000>;
status = "disabled";
};
saif0: saif@80042000 {
compatible = "fsl,imx28-saif";
- reg = <0x80042000 2000>;
+ reg = <0x80042000 0x2000>;
interrupts = <59 80>;
fsl,saif-dma-channel = <4>;
status = "disabled";
};
power@80044000 {
- reg = <0x80044000 2000>;
+ reg = <0x80044000 0x2000>;
status = "disabled";
};
saif1: saif@80046000 {
compatible = "fsl,imx28-saif";
- reg = <0x80046000 2000>;
+ reg = <0x80046000 0x2000>;
interrupts = <58 81>;
fsl,saif-dma-channel = <5>;
status = "disabled";
};
lradc@80050000 {
- reg = <0x80050000 2000>;
+ reg = <0x80050000 0x2000>;
status = "disabled";
};
spdif@80054000 {
- reg = <0x80054000 2000>;
+ reg = <0x80054000 0x2000>;
interrupts = <45 66>;
status = "disabled";
};
rtc@80056000 {
compatible = "fsl,imx28-rtc", "fsl,stmp3xxx-rtc";
- reg = <0x80056000 2000>;
+ reg = <0x80056000 0x2000>;
interrupts = <29>;
};
@@ -658,7 +658,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-i2c";
- reg = <0x80058000 2000>;
+ reg = <0x80058000 0x2000>;
interrupts = <111 68>;
clock-frequency = <100000>;
status = "disabled";
@@ -668,7 +668,7 @@
#address-cells = <1>;
#size-cells = <0>;
compatible = "fsl,imx28-i2c";
- reg = <0x8005a000 2000>;
+ reg = <0x8005a000 0x2000>;
interrupts = <110 69>;
clock-frequency = <100000>;
status = "disabled";
@@ -676,14 +676,14 @@
pwm: pwm@80064000 {
compatible = "fsl,imx28-pwm", "fsl,imx23-pwm";
- reg = <0x80064000 2000>;
+ reg = <0x80064000 0x2000>;
#pwm-cells = <2>;
fsl,pwm-number = <8>;
status = "disabled";
};
timrot@80068000 {
- reg = <0x80068000 2000>;
+ reg = <0x80068000 0x2000>;
status = "disabled";
};
diff --git a/arch/arm/boot/dts/imx51-babbage.dts b/arch/arm/boot/dts/imx51-babbage.dts
index de065b5..cd86177 100644
--- a/arch/arm/boot/dts/imx51-babbage.dts
+++ b/arch/arm/boot/dts/imx51-babbage.dts
@@ -53,7 +53,7 @@
spi-max-frequency = <6000000>;
reg = <0>;
interrupt-parent = <&gpio1>;
- interrupts = <8>;
+ interrupts = <8 0x4>;
regulators {
sw1_reg: sw1 {
diff --git a/arch/arm/boot/dts/imx51.dtsi b/arch/arm/boot/dts/imx51.dtsi
index 53cbaa3..aba28dc 100644
--- a/arch/arm/boot/dts/imx51.dtsi
+++ b/arch/arm/boot/dts/imx51.dtsi
@@ -17,6 +17,10 @@
serial0 = &uart1;
serial1 = &uart2;
serial2 = &uart3;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
};
tzic: tz-interrupt-controller@e0000000 {
diff --git a/arch/arm/boot/dts/imx53-ard.dts b/arch/arm/boot/dts/imx53-ard.dts
index 5b8eafc..da895e9 100644
--- a/arch/arm/boot/dts/imx53-ard.dts
+++ b/arch/arm/boot/dts/imx53-ard.dts
@@ -64,12 +64,32 @@
reg = <0xf4000000 0x2000000>;
phy-mode = "mii";
interrupt-parent = <&gpio2>;
- interrupts = <31>;
+ interrupts = <31 0x8>;
reg-io-width = <4>;
+ /*
+ * VDD33A and VDDVARIO of LAN9220 are supplied by
+ * SW4_3V3 of LTC3589. Before the regulator driver
+ * for this PMIC is available, we use a fixed dummy
+ * 3V3 regulator to get LAN9220 driver probing work.
+ */
+ vdd33a-supply = <®_3p3v>;
+ vddvario-supply = <®_3p3v>;
smsc,irq-push-pull;
};
};
+ regulators {
+ compatible = "simple-bus";
+
+ reg_3p3v: 3p3v {
+ compatible = "regulator-fixed";
+ regulator-name = "3P3V";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+ };
+
gpio-keys {
compatible = "gpio-keys";
diff --git a/arch/arm/boot/dts/imx53.dtsi b/arch/arm/boot/dts/imx53.dtsi
index fc79cdc..cd37165 100644
--- a/arch/arm/boot/dts/imx53.dtsi
+++ b/arch/arm/boot/dts/imx53.dtsi
@@ -19,6 +19,13 @@
serial2 = &uart3;
serial3 = &uart4;
serial4 = &uart5;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
+ gpio6 = &gpio7;
};
tzic: tz-interrupt-controller@0fffc000 {
diff --git a/arch/arm/boot/dts/imx6q-sabrelite.dts b/arch/arm/boot/dts/imx6q-sabrelite.dts
index d42e851..72f30f3 100644
--- a/arch/arm/boot/dts/imx6q-sabrelite.dts
+++ b/arch/arm/boot/dts/imx6q-sabrelite.dts
@@ -53,6 +53,7 @@
fsl,pins = <
144 0x80000000 /* MX6Q_PAD_EIM_D22__GPIO_3_22 */
121 0x80000000 /* MX6Q_PAD_EIM_D19__GPIO_3_19 */
+ 953 0x80000000 /* MX6Q_PAD_GPIO_0__CCM_CLKO */
>;
};
};
diff --git a/arch/arm/boot/dts/imx6q.dtsi b/arch/arm/boot/dts/imx6q.dtsi
index 3d3c64b..fd57079 100644
--- a/arch/arm/boot/dts/imx6q.dtsi
+++ b/arch/arm/boot/dts/imx6q.dtsi
@@ -19,6 +19,13 @@
serial2 = &uart3;
serial3 = &uart4;
serial4 = &uart5;
+ gpio0 = &gpio1;
+ gpio1 = &gpio2;
+ gpio2 = &gpio3;
+ gpio3 = &gpio4;
+ gpio4 = &gpio5;
+ gpio5 = &gpio6;
+ gpio6 = &gpio7;
};
cpus {
diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig
index f725b96..3c9f32f 100644
--- a/arch/arm/configs/imx_v6_v7_defconfig
+++ b/arch/arm/configs/imx_v6_v7_defconfig
@@ -192,6 +192,7 @@
CONFIG_RTC_DRV_MXC=y
CONFIG_DMADEVICES=y
CONFIG_IMX_SDMA=y
+CONFIG_MXS_DMA=y
CONFIG_COMMON_CLK_DEBUG=y
# CONFIG_IOMMU_SUPPORT is not set
CONFIG_EXT2_FS=y
diff --git a/arch/arm/configs/mxs_defconfig b/arch/arm/configs/mxs_defconfig
index ccdb635..4edcfb4 100644
--- a/arch/arm/configs/mxs_defconfig
+++ b/arch/arm/configs/mxs_defconfig
@@ -34,7 +34,6 @@
CONFIG_HIGH_RES_TIMERS=y
CONFIG_PREEMPT_VOLUNTARY=y
CONFIG_AEABI=y
-CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
CONFIG_AUTO_ZRELADDR=y
CONFIG_FPE_NWFPE=y
CONFIG_NET=y
diff --git a/arch/arm/configs/tct_hammer_defconfig b/arch/arm/configs/tct_hammer_defconfig
index 1d24f84..71277a1 100644
--- a/arch/arm/configs/tct_hammer_defconfig
+++ b/arch/arm/configs/tct_hammer_defconfig
@@ -7,7 +7,7 @@
CONFIG_BLK_DEV_INITRD=y
CONFIG_EXPERT=y
# CONFIG_KALLSYMS is not set
-# CONFIG_BUG is not set
+# CONFIG_BUGVERBOSE is not set
# CONFIG_ELF_CORE is not set
# CONFIG_SHMEM is not set
CONFIG_SLOB=y
diff --git a/arch/arm/include/asm/pgtable.h b/arch/arm/include/asm/pgtable.h
index f66626d..41dc31f 100644
--- a/arch/arm/include/asm/pgtable.h
+++ b/arch/arm/include/asm/pgtable.h
@@ -195,25 +195,6 @@
#define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0)
-#if __LINUX_ARM_ARCH__ < 6
-static inline void __sync_icache_dcache(pte_t pteval)
-{
-}
-#else
-extern void __sync_icache_dcache(pte_t pteval);
-#endif
-
-static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
- pte_t *ptep, pte_t pteval)
-{
- if (addr >= TASK_SIZE)
- set_pte_ext(ptep, pteval, 0);
- else {
- __sync_icache_dcache(pteval);
- set_pte_ext(ptep, pteval, PTE_EXT_NG);
- }
-}
-
#define pte_none(pte) (!pte_val(pte))
#define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT)
#define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY))
@@ -226,6 +207,27 @@
((pte_val(pte) & (L_PTE_PRESENT | L_PTE_USER)) == \
(L_PTE_PRESENT | L_PTE_USER))
+#if __LINUX_ARM_ARCH__ < 6
+static inline void __sync_icache_dcache(pte_t pteval)
+{
+}
+#else
+extern void __sync_icache_dcache(pte_t pteval);
+#endif
+
+static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
+ pte_t *ptep, pte_t pteval)
+{
+ unsigned long ext = 0;
+
+ if (addr < TASK_SIZE && pte_present_user(pteval)) {
+ __sync_icache_dcache(pteval);
+ ext |= PTE_EXT_NG;
+ }
+
+ set_pte_ext(ptep, pteval, ext);
+}
+
#define PTE_BIT_FUNC(fn,op) \
static inline pte_t pte_##fn(pte_t pte) { pte_val(pte) op; return pte; }
@@ -251,13 +253,13 @@
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
- * <--------------- offset --------------------> <- type --> 0 0 0
+ * <--------------- offset ----------------------> < type -> 0 0 0
*
- * This gives us up to 63 swap files and 32GB per swap file. Note that
+ * This gives us up to 31 swap files and 64GB per swap file. Note that
* the offset field is always non-zero.
*/
#define __SWP_TYPE_SHIFT 3
-#define __SWP_TYPE_BITS 6
+#define __SWP_TYPE_BITS 5
#define __SWP_TYPE_MASK ((1 << __SWP_TYPE_BITS) - 1)
#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT)
diff --git a/arch/arm/include/asm/sched_clock.h b/arch/arm/include/asm/sched_clock.h
index e3f7572..05b8e82 100644
--- a/arch/arm/include/asm/sched_clock.h
+++ b/arch/arm/include/asm/sched_clock.h
@@ -10,5 +10,7 @@
extern void sched_clock_postinit(void);
extern void setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate);
+extern void setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
+ unsigned long rate);
#endif
diff --git a/arch/arm/kernel/sched_clock.c b/arch/arm/kernel/sched_clock.c
index 27d186a..f451539 100644
--- a/arch/arm/kernel/sched_clock.c
+++ b/arch/arm/kernel/sched_clock.c
@@ -21,6 +21,8 @@
u32 epoch_cyc_copy;
u32 mult;
u32 shift;
+ bool suspended;
+ bool needs_suspend;
};
static void sched_clock_poll(unsigned long wrap_ticks);
@@ -49,6 +51,9 @@
u64 epoch_ns;
u32 epoch_cyc;
+ if (cd.suspended)
+ return cd.epoch_ns;
+
/*
* Load the epoch_cyc and epoch_ns atomically. We do this by
* ensuring that we always write epoch_cyc, epoch_ns and
@@ -98,6 +103,13 @@
update_sched_clock();
}
+void __init setup_sched_clock_needs_suspend(u32 (*read)(void), int bits,
+ unsigned long rate)
+{
+ setup_sched_clock(read, bits, rate);
+ cd.needs_suspend = true;
+}
+
void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
{
unsigned long r, w;
@@ -169,11 +181,23 @@
static int sched_clock_suspend(void)
{
sched_clock_poll(sched_clock_timer.data);
+ if (cd.needs_suspend)
+ cd.suspended = true;
return 0;
}
+static void sched_clock_resume(void)
+{
+ if (cd.needs_suspend) {
+ cd.epoch_cyc = read_sched_clock();
+ cd.epoch_cyc_copy = cd.epoch_cyc;
+ cd.suspended = false;
+ }
+}
+
static struct syscore_ops sched_clock_ops = {
.suspend = sched_clock_suspend,
+ .resume = sched_clock_resume,
};
static int __init sched_clock_syscore_init(void)
diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c
index 198b0845..26c12c6 100644
--- a/arch/arm/kernel/topology.c
+++ b/arch/arm/kernel/topology.c
@@ -321,7 +321,7 @@
* init_cpu_topology is called at boot when only one cpu is running
* which prevent simultaneous write access to cpu_topology array
*/
-void init_cpu_topology(void)
+void __init init_cpu_topology(void)
{
unsigned int cpu;
diff --git a/arch/arm/lib/Makefile b/arch/arm/lib/Makefile
index 2473fd1..af72969 100644
--- a/arch/arm/lib/Makefile
+++ b/arch/arm/lib/Makefile
@@ -16,13 +16,30 @@
call_with_stack.o
mmu-y := clear_user.o copy_page.o getuser.o putuser.o
-mmu-y += copy_from_user.o copy_to_user.o
+
+# the code in uaccess.S is not preemption safe and
+# probably faster on ARMv3 only
+ifeq ($(CONFIG_PREEMPT),y)
+ mmu-y += copy_from_user.o copy_to_user.o
+else
+ifneq ($(CONFIG_CPU_32v3),y)
+ mmu-y += copy_from_user.o copy_to_user.o
+else
+ mmu-y += uaccess.o
+endif
+endif
# using lib_ here won't override already available weak symbols
obj-$(CONFIG_UACCESS_WITH_MEMCPY) += uaccess_with_memcpy.o
-lib-$(CONFIG_MMU) += $(mmu-y)
-lib-y += io-readsw-armv4.o io-writesw-armv4.o
+lib-$(CONFIG_MMU) += $(mmu-y)
+
+ifeq ($(CONFIG_CPU_32v3),y)
+ lib-y += io-readsw-armv3.o io-writesw-armv3.o
+else
+ lib-y += io-readsw-armv4.o io-writesw-armv4.o
+endif
+
lib-$(CONFIG_ARCH_RPC) += ecard.o io-acorn.o floppydma.o
lib-$(CONFIG_ARCH_SHARK) += io-shark.o
diff --git a/arch/arm/lib/io-readsw-armv3.S b/arch/arm/lib/io-readsw-armv3.S
new file mode 100644
index 0000000..88487c8
--- /dev/null
+++ b/arch/arm/lib/io-readsw-armv3.S
@@ -0,0 +1,106 @@
+/*
+ * linux/arch/arm/lib/io-readsw-armv3.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+.Linsw_bad_alignment:
+ adr r0, .Linsw_bad_align_msg
+ mov r2, lr
+ b panic
+.Linsw_bad_align_msg:
+ .asciz "insw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+ .align
+
+.Linsw_align: tst r1, #1
+ bne .Linsw_bad_alignment
+
+ ldr r3, [r0]
+ strb r3, [r1], #1
+ mov r3, r3, lsr #8
+ strb r3, [r1], #1
+
+ subs r2, r2, #1
+ moveq pc, lr
+
+ENTRY(__raw_readsw)
+ teq r2, #0 @ do we have to check for the zero len?
+ moveq pc, lr
+ tst r1, #3
+ bne .Linsw_align
+
+.Linsw_aligned: mov ip, #0xff
+ orr ip, ip, ip, lsl #8
+ stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .Lno_insw_8
+
+.Linsw_8_lp: ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ ldr r5, [r0]
+ and r5, r5, ip
+ ldr r6, [r0]
+ orr r5, r5, r6, lsl #16
+
+ ldr r6, [r0]
+ and r6, r6, ip
+ ldr lr, [r0]
+ orr r6, r6, lr, lsl #16
+
+ stmia r1!, {r3 - r6}
+
+ subs r2, r2, #8
+ bpl .Linsw_8_lp
+
+ tst r2, #7
+ ldmeqfd sp!, {r4, r5, r6, pc}
+
+.Lno_insw_8: tst r2, #4
+ beq .Lno_insw_4
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ ldr r4, [r0]
+ and r4, r4, ip
+ ldr r5, [r0]
+ orr r4, r4, r5, lsl #16
+
+ stmia r1!, {r3, r4}
+
+.Lno_insw_4: tst r2, #2
+ beq .Lno_insw_2
+
+ ldr r3, [r0]
+ and r3, r3, ip
+ ldr r4, [r0]
+ orr r3, r3, r4, lsl #16
+
+ str r3, [r1], #4
+
+.Lno_insw_2: tst r2, #1
+ ldrne r3, [r0]
+ strneb r3, [r1], #1
+ movne r3, r3, lsr #8
+ strneb r3, [r1]
+
+ ldmfd sp!, {r4, r5, r6, pc}
+
+
diff --git a/arch/arm/lib/io-writesw-armv3.S b/arch/arm/lib/io-writesw-armv3.S
new file mode 100644
index 0000000..49b8004
--- /dev/null
+++ b/arch/arm/lib/io-writesw-armv3.S
@@ -0,0 +1,126 @@
+/*
+ * linux/arch/arm/lib/io-writesw-armv3.S
+ *
+ * Copyright (C) 1995-2000 Russell King
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+
+.Loutsw_bad_alignment:
+ adr r0, .Loutsw_bad_align_msg
+ mov r2, lr
+ b panic
+.Loutsw_bad_align_msg:
+ .asciz "outsw: bad buffer alignment (0x%p, lr=0x%08lX)\n"
+ .align
+
+.Loutsw_align: tst r1, #1
+ bne .Loutsw_bad_alignment
+
+ add r1, r1, #2
+
+ ldr r3, [r1, #-4]
+ mov r3, r3, lsr #16
+ orr r3, r3, r3, lsl #16
+ str r3, [r0]
+ subs r2, r2, #1
+ moveq pc, lr
+
+ENTRY(__raw_writesw)
+ teq r2, #0 @ do we have to check for the zero len?
+ moveq pc, lr
+ tst r1, #3
+ bne .Loutsw_align
+
+ stmfd sp!, {r4, r5, r6, lr}
+
+ subs r2, r2, #8
+ bmi .Lno_outsw_8
+
+.Loutsw_8_lp: ldmia r1!, {r3, r4, r5, r6}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r5, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r5, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r6, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r6, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ subs r2, r2, #8
+ bpl .Loutsw_8_lp
+
+ tst r2, #7
+ ldmeqfd sp!, {r4, r5, r6, pc}
+
+.Lno_outsw_8: tst r2, #4
+ beq .Lno_outsw_4
+
+ ldmia r1!, {r3, r4}
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+ mov ip, r4, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r4, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.Lno_outsw_4: tst r2, #2
+ beq .Lno_outsw_2
+
+ ldr r3, [r1], #4
+
+ mov ip, r3, lsl #16
+ orr ip, ip, ip, lsr #16
+ str ip, [r0]
+
+ mov ip, r3, lsr #16
+ orr ip, ip, ip, lsl #16
+ str ip, [r0]
+
+.Lno_outsw_2: tst r2, #1
+
+ ldrne r3, [r1]
+
+ movne ip, r3, lsl #16
+ orrne ip, ip, ip, lsr #16
+ strne ip, [r0]
+
+ ldmfd sp!, {r4, r5, r6, pc}
diff --git a/arch/arm/lib/uaccess.S b/arch/arm/lib/uaccess.S
new file mode 100644
index 0000000..5c908b1
--- /dev/null
+++ b/arch/arm/lib/uaccess.S
@@ -0,0 +1,564 @@
+/*
+ * linux/arch/arm/lib/uaccess.S
+ *
+ * Copyright (C) 1995, 1996,1997,1998 Russell King
+ *
+ * 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.
+ *
+ * Routines to block copy data to/from user memory
+ * These are highly optimised both for the 4k page size
+ * and for various alignments.
+ */
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/errno.h>
+#include <asm/domain.h>
+
+ .text
+
+#define PAGE_SHIFT 12
+
+/* Prototype: int __copy_to_user(void *to, const char *from, size_t n)
+ * Purpose : copy a block to user memory from kernel memory
+ * Params : to - user memory
+ * : from - kernel memory
+ * : n - number of bytes to copy
+ * Returns : Number of bytes NOT copied.
+ */
+
+.Lc2u_dest_not_aligned:
+ rsb ip, ip, #4
+ cmp ip, #2
+ ldrb r3, [r1], #1
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #1
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ sub r2, r2, ip
+ b .Lc2u_dest_aligned
+
+ENTRY(__copy_to_user)
+ stmfd sp!, {r2, r4 - r7, lr}
+ cmp r2, #4
+ blt .Lc2u_not_enough
+ ands ip, r0, #3
+ bne .Lc2u_dest_not_aligned
+.Lc2u_dest_aligned:
+
+ ands ip, r1, #3
+ bne .Lc2u_src_not_aligned
+/*
+ * Seeing as there has to be at least 8 bytes to copy, we can
+ * copy one word, and force a user-mode page fault...
+ */
+
+.Lc2u_0fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_0nowords
+ ldr r3, [r1], #4
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_0fupi
+/*
+ * ip = max no. of bytes to copy before needing another "strt" insn
+ */
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #32
+ blt .Lc2u_0rem8lp
+
+.Lc2u_0cpy8lp: ldmia r1!, {r3 - r6}
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ ldmia r1!, {r3 - r6}
+ subs ip, ip, #32
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_0cpy8lp
+
+.Lc2u_0rem8lp: cmn ip, #16
+ ldmgeia r1!, {r3 - r6}
+ stmgeia r0!, {r3 - r6} @ Shouldnt fault
+ tst ip, #8
+ ldmneia r1!, {r3 - r4}
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ ldrne r3, [r1], #4
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_0fupi
+.Lc2u_0nowords: teq ip, #0
+ beq .Lc2u_finished
+.Lc2u_nowords: cmp ip, #2
+ ldrb r3, [r1], #1
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #1
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_not_enough:
+ movs ip, r2
+ bne .Lc2u_nowords
+.Lc2u_finished: mov r0, #0
+ ldmfd sp!, {r2, r4 - r7, pc}
+
+.Lc2u_src_not_aligned:
+ bic r1, r1, #3
+ ldr r7, [r1], #4
+ cmp ip, #2
+ bgt .Lc2u_3fupi
+ beq .Lc2u_2fupi
+.Lc2u_1fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_1nowords
+ mov r3, r7, pull #8
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #24
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_1fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_1rem8lp
+
+.Lc2u_1cpy8lp: mov r3, r7, pull #8
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #24
+ mov r4, r4, pull #8
+ orr r4, r4, r5, push #24
+ mov r5, r5, pull #8
+ orr r5, r5, r6, push #24
+ mov r6, r6, pull #8
+ orr r6, r6, r7, push #24
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_1cpy8lp
+
+.Lc2u_1rem8lp: tst ip, #8
+ movne r3, r7, pull #8
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #24
+ movne r4, r4, pull #8
+ orrne r4, r4, r7, push #24
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #8
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #24
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_1fupi
+.Lc2u_1nowords: mov r3, r7, get_byte_1
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ movge r3, r7, get_byte_2
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ movgt r3, r7, get_byte_3
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_2fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_2nowords
+ mov r3, r7, pull #16
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #16
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_2fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_2rem8lp
+
+.Lc2u_2cpy8lp: mov r3, r7, pull #16
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #16
+ mov r4, r4, pull #16
+ orr r4, r4, r5, push #16
+ mov r5, r5, pull #16
+ orr r5, r5, r6, push #16
+ mov r6, r6, pull #16
+ orr r6, r6, r7, push #16
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_2cpy8lp
+
+.Lc2u_2rem8lp: tst ip, #8
+ movne r3, r7, pull #16
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #16
+ movne r4, r4, pull #16
+ orrne r4, r4, r7, push #16
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #16
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #16
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_2fupi
+.Lc2u_2nowords: mov r3, r7, get_byte_2
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ movge r3, r7, get_byte_3
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #0
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+
+.Lc2u_3fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lc2u_3nowords
+ mov r3, r7, pull #24
+ ldr r7, [r1], #4
+ orr r3, r3, r7, push #8
+USER( TUSER( str) r3, [r0], #4) @ May fault
+ mov ip, r0, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lc2u_3fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lc2u_3rem8lp
+
+.Lc2u_3cpy8lp: mov r3, r7, pull #24
+ ldmia r1!, {r4 - r7}
+ subs ip, ip, #16
+ orr r3, r3, r4, push #8
+ mov r4, r4, pull #24
+ orr r4, r4, r5, push #8
+ mov r5, r5, pull #24
+ orr r5, r5, r6, push #8
+ mov r6, r6, pull #24
+ orr r6, r6, r7, push #8
+ stmia r0!, {r3 - r6} @ Shouldnt fault
+ bpl .Lc2u_3cpy8lp
+
+.Lc2u_3rem8lp: tst ip, #8
+ movne r3, r7, pull #24
+ ldmneia r1!, {r4, r7}
+ orrne r3, r3, r4, push #8
+ movne r4, r4, pull #24
+ orrne r4, r4, r7, push #8
+ stmneia r0!, {r3 - r4} @ Shouldnt fault
+ tst ip, #4
+ movne r3, r7, pull #24
+ ldrne r7, [r1], #4
+ orrne r3, r3, r7, push #8
+ TUSER( strne) r3, [r0], #4 @ Shouldnt fault
+ ands ip, ip, #3
+ beq .Lc2u_3fupi
+.Lc2u_3nowords: mov r3, r7, get_byte_3
+ teq ip, #0
+ beq .Lc2u_finished
+ cmp ip, #2
+USER( TUSER( strb) r3, [r0], #1) @ May fault
+ ldrgeb r3, [r1], #1
+USER( TUSER( strgeb) r3, [r0], #1) @ May fault
+ ldrgtb r3, [r1], #0
+USER( TUSER( strgtb) r3, [r0], #1) @ May fault
+ b .Lc2u_finished
+ENDPROC(__copy_to_user)
+
+ .pushsection .fixup,"ax"
+ .align 0
+9001: ldmfd sp!, {r0, r4 - r7, pc}
+ .popsection
+
+/* Prototype: unsigned long __copy_from_user(void *to,const void *from,unsigned long n);
+ * Purpose : copy a block from user memory to kernel memory
+ * Params : to - kernel memory
+ * : from - user memory
+ * : n - number of bytes to copy
+ * Returns : Number of bytes NOT copied.
+ */
+.Lcfu_dest_not_aligned:
+ rsb ip, ip, #4
+ cmp ip, #2
+USER( TUSER( ldrb) r3, [r1], #1) @ May fault
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ sub r2, r2, ip
+ b .Lcfu_dest_aligned
+
+ENTRY(__copy_from_user)
+ stmfd sp!, {r0, r2, r4 - r7, lr}
+ cmp r2, #4
+ blt .Lcfu_not_enough
+ ands ip, r0, #3
+ bne .Lcfu_dest_not_aligned
+.Lcfu_dest_aligned:
+ ands ip, r1, #3
+ bne .Lcfu_src_not_aligned
+
+/*
+ * Seeing as there has to be at least 8 bytes to copy, we can
+ * copy one word, and force a user-mode page fault...
+ */
+
+.Lcfu_0fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_0nowords
+USER( TUSER( ldr) r3, [r1], #4)
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT @ On each page, use a ld/st??t instruction
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_0fupi
+/*
+ * ip = max no. of bytes to copy before needing another "strt" insn
+ */
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #32
+ blt .Lcfu_0rem8lp
+
+.Lcfu_0cpy8lp: ldmia r1!, {r3 - r6} @ Shouldnt fault
+ stmia r0!, {r3 - r6}
+ ldmia r1!, {r3 - r6} @ Shouldnt fault
+ subs ip, ip, #32
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_0cpy8lp
+
+.Lcfu_0rem8lp: cmn ip, #16
+ ldmgeia r1!, {r3 - r6} @ Shouldnt fault
+ stmgeia r0!, {r3 - r6}
+ tst ip, #8
+ ldmneia r1!, {r3 - r4} @ Shouldnt fault
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ TUSER( ldrne) r3, [r1], #4 @ Shouldnt fault
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_0fupi
+.Lcfu_0nowords: teq ip, #0
+ beq .Lcfu_finished
+.Lcfu_nowords: cmp ip, #2
+USER( TUSER( ldrb) r3, [r1], #1) @ May fault
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_not_enough:
+ movs ip, r2
+ bne .Lcfu_nowords
+.Lcfu_finished: mov r0, #0
+ add sp, sp, #8
+ ldmfd sp!, {r4 - r7, pc}
+
+.Lcfu_src_not_aligned:
+ bic r1, r1, #3
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ cmp ip, #2
+ bgt .Lcfu_3fupi
+ beq .Lcfu_2fupi
+.Lcfu_1fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_1nowords
+ mov r3, r7, pull #8
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #24
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_1fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_1rem8lp
+
+.Lcfu_1cpy8lp: mov r3, r7, pull #8
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ subs ip, ip, #16
+ orr r3, r3, r4, push #24
+ mov r4, r4, pull #8
+ orr r4, r4, r5, push #24
+ mov r5, r5, pull #8
+ orr r5, r5, r6, push #24
+ mov r6, r6, pull #8
+ orr r6, r6, r7, push #24
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_1cpy8lp
+
+.Lcfu_1rem8lp: tst ip, #8
+ movne r3, r7, pull #8
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #24
+ movne r4, r4, pull #8
+ orrne r4, r4, r7, push #24
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #8
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #24
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_1fupi
+.Lcfu_1nowords: mov r3, r7, get_byte_1
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+ movge r3, r7, get_byte_2
+ strgeb r3, [r0], #1
+ movgt r3, r7, get_byte_3
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_2fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_2nowords
+ mov r3, r7, pull #16
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #16
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_2fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_2rem8lp
+
+
+.Lcfu_2cpy8lp: mov r3, r7, pull #16
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ subs ip, ip, #16
+ orr r3, r3, r4, push #16
+ mov r4, r4, pull #16
+ orr r4, r4, r5, push #16
+ mov r5, r5, pull #16
+ orr r5, r5, r6, push #16
+ mov r6, r6, pull #16
+ orr r6, r6, r7, push #16
+ stmia r0!, {r3 - r6}
+ bpl .Lcfu_2cpy8lp
+
+.Lcfu_2rem8lp: tst ip, #8
+ movne r3, r7, pull #16
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #16
+ movne r4, r4, pull #16
+ orrne r4, r4, r7, push #16
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #16
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #16
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_2fupi
+.Lcfu_2nowords: mov r3, r7, get_byte_2
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+ movge r3, r7, get_byte_3
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #0) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+
+.Lcfu_3fupi: subs r2, r2, #4
+ addmi ip, r2, #4
+ bmi .Lcfu_3nowords
+ mov r3, r7, pull #24
+USER( TUSER( ldr) r7, [r1], #4) @ May fault
+ orr r3, r3, r7, push #8
+ str r3, [r0], #4
+ mov ip, r1, lsl #32 - PAGE_SHIFT
+ rsb ip, ip, #0
+ movs ip, ip, lsr #32 - PAGE_SHIFT
+ beq .Lcfu_3fupi
+ cmp r2, ip
+ movlt ip, r2
+ sub r2, r2, ip
+ subs ip, ip, #16
+ blt .Lcfu_3rem8lp
+
+.Lcfu_3cpy8lp: mov r3, r7, pull #24
+ ldmia r1!, {r4 - r7} @ Shouldnt fault
+ orr r3, r3, r4, push #8
+ mov r4, r4, pull #24
+ orr r4, r4, r5, push #8
+ mov r5, r5, pull #24
+ orr r5, r5, r6, push #8
+ mov r6, r6, pull #24
+ orr r6, r6, r7, push #8
+ stmia r0!, {r3 - r6}
+ subs ip, ip, #16
+ bpl .Lcfu_3cpy8lp
+
+.Lcfu_3rem8lp: tst ip, #8
+ movne r3, r7, pull #24
+ ldmneia r1!, {r4, r7} @ Shouldnt fault
+ orrne r3, r3, r4, push #8
+ movne r4, r4, pull #24
+ orrne r4, r4, r7, push #8
+ stmneia r0!, {r3 - r4}
+ tst ip, #4
+ movne r3, r7, pull #24
+USER( TUSER( ldrne) r7, [r1], #4) @ May fault
+ orrne r3, r3, r7, push #8
+ strne r3, [r0], #4
+ ands ip, ip, #3
+ beq .Lcfu_3fupi
+.Lcfu_3nowords: mov r3, r7, get_byte_3
+ teq ip, #0
+ beq .Lcfu_finished
+ cmp ip, #2
+ strb r3, [r0], #1
+USER( TUSER( ldrgeb) r3, [r1], #1) @ May fault
+ strgeb r3, [r0], #1
+USER( TUSER( ldrgtb) r3, [r1], #1) @ May fault
+ strgtb r3, [r0], #1
+ b .Lcfu_finished
+ENDPROC(__copy_from_user)
+
+ .pushsection .fixup,"ax"
+ .align 0
+ /*
+ * We took an exception. r0 contains a pointer to
+ * the byte not copied.
+ */
+9001: ldr r2, [sp], #4 @ void *to
+ sub r2, r0, r2 @ bytes copied
+ ldr r1, [sp], #4 @ unsigned long count
+ subs r4, r1, r2 @ bytes left to copy
+ movne r1, r4
+ blne __memzero
+ mov r0, r4
+ ldmfd sp!, {r4 - r7, pc}
+ .popsection
+
diff --git a/arch/arm/mach-davinci/board-neuros-osd2.c b/arch/arm/mach-davinci/board-neuros-osd2.c
index 5de69f2..f6b9fc7 100644
--- a/arch/arm/mach-davinci/board-neuros-osd2.c
+++ b/arch/arm/mach-davinci/board-neuros-osd2.c
@@ -162,38 +162,6 @@
dm644x_init();
}
-/*
- I2C initialization
-*/
-static struct davinci_i2c_platform_data ntosd2_i2c_pdata = {
- .bus_freq = 20 /* kHz */,
- .bus_delay = 100 /* usec */,
-};
-
-static struct i2c_board_info __initdata ntosd2_i2c_info[] = {
-};
-
-static int ntosd2_init_i2c(void)
-{
- int status;
-
- davinci_init_i2c(&ntosd2_i2c_pdata);
- status = gpio_request(NTOSD2_MSP430_IRQ, ntosd2_i2c_info[0].type);
- if (status == 0) {
- status = gpio_direction_input(NTOSD2_MSP430_IRQ);
- if (status == 0) {
- status = gpio_to_irq(NTOSD2_MSP430_IRQ);
- if (status > 0) {
- ntosd2_i2c_info[0].irq = status;
- i2c_register_board_info(1,
- ntosd2_i2c_info,
- ARRAY_SIZE(ntosd2_i2c_info));
- }
- }
- }
- return status;
-}
-
static struct davinci_mmc_config davinci_ntosd2_mmc_config = {
.wires = 4,
.version = MMC_CTLR_VERSION_1
@@ -218,7 +186,6 @@
{
struct clk *aemif_clk;
struct davinci_soc_info *soc_info = &davinci_soc_info;
- int status;
aemif_clk = clk_get(NULL, "aemif");
clk_enable(aemif_clk);
@@ -242,12 +209,6 @@
platform_add_devices(davinci_ntosd2_devices,
ARRAY_SIZE(davinci_ntosd2_devices));
- /* Initialize I2C interface specific for this board */
- status = ntosd2_init_i2c();
- if (status < 0)
- pr_warning("davinci_ntosd2_init: msp430 irq setup failed:"
- " %d\n", status);
-
davinci_serial_init(&uart_config);
dm644x_init_asp(&dm644x_ntosd2_snd_data);
diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c
index 373c3c0..c0bc83a 100644
--- a/arch/arm/mach-exynos/pm_domains.c
+++ b/arch/arm/mach-exynos/pm_domains.c
@@ -115,7 +115,7 @@
}
#endif /* CONFIG_OF */
-static __init void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
+static __init __maybe_unused void exynos_pm_add_dev_to_genpd(struct platform_device *pdev,
struct exynos_pm_domain *pd)
{
if (pdev->dev.bus) {
diff --git a/arch/arm/mach-imx/clk-imx27.c b/arch/arm/mach-imx/clk-imx27.c
index 7aa6313..f69ca46 100644
--- a/arch/arm/mach-imx/clk-imx27.c
+++ b/arch/arm/mach-imx/clk-imx27.c
@@ -223,7 +223,7 @@
clk_register_clkdev(clk[per3_gate], "per", "imx-fb.0");
clk_register_clkdev(clk[lcdc_ipg_gate], "ipg", "imx-fb.0");
clk_register_clkdev(clk[lcdc_ahb_gate], "ahb", "imx-fb.0");
- clk_register_clkdev(clk[csi_ahb_gate], NULL, "mx2-camera.0");
+ clk_register_clkdev(clk[csi_ahb_gate], "ahb", "mx2-camera.0");
clk_register_clkdev(clk[usb_div], "per", "fsl-usb2-udc");
clk_register_clkdev(clk[usb_ipg_gate], "ipg", "fsl-usb2-udc");
clk_register_clkdev(clk[usb_ahb_gate], "ahb", "fsl-usb2-udc");
@@ -250,8 +250,10 @@
clk_register_clkdev(clk[i2c2_ipg_gate], NULL, "imx-i2c.1");
clk_register_clkdev(clk[owire_ipg_gate], NULL, "mxc_w1.0");
clk_register_clkdev(clk[kpp_ipg_gate], NULL, "imx-keypad");
- clk_register_clkdev(clk[emma_ahb_gate], "ahb", "imx-emma");
- clk_register_clkdev(clk[emma_ipg_gate], "ipg", "imx-emma");
+ clk_register_clkdev(clk[emma_ahb_gate], "emma-ahb", "mx2-camera.0");
+ clk_register_clkdev(clk[emma_ipg_gate], "emma-ipg", "mx2-camera.0");
+ clk_register_clkdev(clk[emma_ahb_gate], "ahb", "m2m-emmaprp.0");
+ clk_register_clkdev(clk[emma_ipg_gate], "ipg", "m2m-emmaprp.0");
clk_register_clkdev(clk[iim_ipg_gate], "iim", NULL);
clk_register_clkdev(clk[gpio_ipg_gate], "gpio", NULL);
clk_register_clkdev(clk[brom_ahb_gate], "brom", NULL);
diff --git a/arch/arm/mach-imx/clk-imx31.c b/arch/arm/mach-imx/clk-imx31.c
index 8e19e70..1253af2 100644
--- a/arch/arm/mach-imx/clk-imx31.c
+++ b/arch/arm/mach-imx/clk-imx31.c
@@ -130,7 +130,7 @@
clk_register_clkdev(clk[nfc], NULL, "mxc_nand.0");
clk_register_clkdev(clk[ipu_gate], NULL, "ipu-core");
clk_register_clkdev(clk[ipu_gate], NULL, "mx3_sdc_fb");
- clk_register_clkdev(clk[kpp_gate], "kpp", NULL);
+ clk_register_clkdev(clk[kpp_gate], NULL, "imx-keypad");
clk_register_clkdev(clk[usb_div_post], "per", "mxc-ehci.0");
clk_register_clkdev(clk[usb_gate], "ahb", "mxc-ehci.0");
clk_register_clkdev(clk[ipg], "ipg", "mxc-ehci.0");
diff --git a/arch/arm/mach-imx/clk-imx51-imx53.c b/arch/arm/mach-imx/clk-imx51-imx53.c
index f608669..4bdcaa9 100644
--- a/arch/arm/mach-imx/clk-imx51-imx53.c
+++ b/arch/arm/mach-imx/clk-imx51-imx53.c
@@ -303,6 +303,7 @@
clk_prepare_enable(clk[aips_tz2]); /* fec */
clk_prepare_enable(clk[spba]);
clk_prepare_enable(clk[emi_fast_gate]); /* fec */
+ clk_prepare_enable(clk[emi_slow_gate]); /* eim */
clk_prepare_enable(clk[tmax1]);
clk_prepare_enable(clk[tmax2]); /* esdhc2, fec */
clk_prepare_enable(clk[tmax3]); /* esdhc1, esdhc4 */
diff --git a/arch/arm/mach-integrator/core.c b/arch/arm/mach-integrator/core.c
index ebf680b..3fa6c51 100644
--- a/arch/arm/mach-integrator/core.c
+++ b/arch/arm/mach-integrator/core.c
@@ -11,6 +11,7 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/device.h>
+#include <linux/export.h>
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
diff --git a/arch/arm/mach-integrator/integrator_ap.c b/arch/arm/mach-integrator/integrator_ap.c
index 7b1055c..3b22675 100644
--- a/arch/arm/mach-integrator/integrator_ap.c
+++ b/arch/arm/mach-integrator/integrator_ap.c
@@ -456,7 +456,7 @@
clk = clk_get_sys("ap_timer", NULL);
BUG_ON(IS_ERR(clk));
- clk_enable(clk);
+ clk_prepare_enable(clk);
rate = clk_get_rate(clk);
writel(0, TIMER0_VA_BASE + TIMER_CTRL);
diff --git a/arch/arm/mach-kirkwood/Makefile.boot b/arch/arm/mach-kirkwood/Makefile.boot
index 2a576ab..a571755 100644
--- a/arch/arm/mach-kirkwood/Makefile.boot
+++ b/arch/arm/mach-kirkwood/Makefile.boot
@@ -9,5 +9,5 @@
dtb-$(CONFIG_MACH_IB62X0_DT) += kirkwood-ib62x0.dtb
dtb-$(CONFIG_MACH_TS219_DT) += kirkwood-qnap-ts219.dtb
dtb-$(CONFIG_MACH_GOFLEXNET_DT) += kirkwood-goflexnet.dtb
-dbt-$(CONFIG_MACH_LSXL_DT) += kirkwood-lschlv2.dtb
-dbt-$(CONFIG_MACH_LSXL_DT) += kirkwood-lsxhl.dtb
+dtb-$(CONFIG_MACH_LSXL_DT) += kirkwood-lschlv2.dtb
+dtb-$(CONFIG_MACH_LSXL_DT) += kirkwood-lsxhl.dtb
diff --git a/arch/arm/mach-mxs/Kconfig b/arch/arm/mach-mxs/Kconfig
index ccdf83b..9a8bbda 100644
--- a/arch/arm/mach-mxs/Kconfig
+++ b/arch/arm/mach-mxs/Kconfig
@@ -2,9 +2,6 @@
source "arch/arm/mach-mxs/devices/Kconfig"
-config MXS_OCOTP
- bool
-
config SOC_IMX23
bool
select ARM_AMBA
@@ -66,7 +63,6 @@
select MXS_HAVE_PLATFORM_MXS_SAIF
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_RTC_STMP3XXX
- select MXS_OCOTP
help
Include support for MX28EVK platform. This includes specific
configurations for the board and its peripherals.
@@ -94,7 +90,6 @@
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_MXS_MMC
select MXS_HAVE_PLATFORM_MXSFB
- select MXS_OCOTP
config MODULE_APX4
bool
@@ -106,7 +101,6 @@
select MXS_HAVE_PLATFORM_MXS_I2C
select MXS_HAVE_PLATFORM_MXS_MMC
select MXS_HAVE_PLATFORM_MXS_SAIF
- select MXS_OCOTP
config MACH_TX28
bool "Ka-Ro TX28 module"
diff --git a/arch/arm/mach-mxs/Makefile b/arch/arm/mach-mxs/Makefile
index e41590c..fed3695 100644
--- a/arch/arm/mach-mxs/Makefile
+++ b/arch/arm/mach-mxs/Makefile
@@ -1,7 +1,6 @@
# Common support
-obj-y := devices.o icoll.o iomux.o system.o timer.o mm.o
+obj-y := devices.o icoll.o iomux.o ocotp.o system.o timer.o mm.o
-obj-$(CONFIG_MXS_OCOTP) += ocotp.o
obj-$(CONFIG_PM) += pm.o
obj-$(CONFIG_MACH_MXS_DT) += mach-mxs.o
diff --git a/arch/arm/mach-pxa/raumfeld.c b/arch/arm/mach-pxa/raumfeld.c
index 5905ed1..d89d87ae 100644
--- a/arch/arm/mach-pxa/raumfeld.c
+++ b/arch/arm/mach-pxa/raumfeld.c
@@ -953,12 +953,12 @@
static struct eeti_ts_platform_data eeti_ts_pdata = {
.irq_active_high = 1,
+ .irq_gpio = GPIO_TOUCH_IRQ,
};
static struct i2c_board_info raumfeld_controller_i2c_board_info __initdata = {
.type = "eeti_ts",
.addr = 0x0a,
- .irq = PXA_GPIO_TO_IRQ(GPIO_TOUCH_IRQ),
.platform_data = &eeti_ts_pdata,
};
diff --git a/arch/arm/mach-s3c24xx/Kconfig b/arch/arm/mach-s3c24xx/Kconfig
index e249611..d56b0f7 100644
--- a/arch/arm/mach-s3c24xx/Kconfig
+++ b/arch/arm/mach-s3c24xx/Kconfig
@@ -483,7 +483,7 @@
select I2C
select POWER_SUPPLY
select MACH_NEO1973
- select S3C2410_PWM
+ select S3C24XX_PWM
select S3C_DEV_USB_HOST
help
Say Y here if you are using the Openmoko GTA02 / Freerunner GSM Phone
@@ -493,7 +493,7 @@
select S3C24XX_DCLK
select PM_H1940 if PM
select I2C
- select S3C2410_PWM
+ select S3C24XX_PWM
select S3C_DEV_NAND
select S3C2410_IOTIMING if S3C2440_CPUFREQ
select S3C2440_XTAL_16934400
diff --git a/arch/arm/mach-sa1100/leds-hackkit.c b/arch/arm/mach-sa1100/leds-hackkit.c
index 6a23524..f8e4723 100644
--- a/arch/arm/mach-sa1100/leds-hackkit.c
+++ b/arch/arm/mach-sa1100/leds-hackkit.c
@@ -10,6 +10,7 @@
* as cpu led, the green one is used as timer led.
*/
#include <linux/init.h>
+#include <linux/io.h>
#include <mach/hardware.h>
#include <asm/leds.h>
diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c
index 8fd387b..b7344be 100644
--- a/arch/arm/mach-tegra/board-harmony-power.c
+++ b/arch/arm/mach-tegra/board-harmony-power.c
@@ -51,7 +51,7 @@
.consumer_supplies = tps658621_ldo0_supply,
};
-#define HARMONY_REGULATOR_INIT(_id, _name, _supply, _minmv, _maxmv) \
+#define HARMONY_REGULATOR_INIT(_id, _name, _supply, _minmv, _maxmv, _on)\
static struct regulator_init_data _id##_data = { \
.supply_regulator = _supply, \
.constraints = { \
@@ -63,21 +63,22 @@
.valid_ops_mask = (REGULATOR_CHANGE_MODE | \
REGULATOR_CHANGE_STATUS | \
REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _on, \
}, \
}
-HARMONY_REGULATOR_INIT(sm0, "vdd_sm0", "vdd_sys", 725, 1500);
-HARMONY_REGULATOR_INIT(sm1, "vdd_sm1", "vdd_sys", 725, 1500);
-HARMONY_REGULATOR_INIT(sm2, "vdd_sm2", "vdd_sys", 3000, 4550);
-HARMONY_REGULATOR_INIT(ldo1, "vdd_ldo1", "vdd_sm2", 725, 1500);
-HARMONY_REGULATOR_INIT(ldo2, "vdd_ldo2", "vdd_sm2", 725, 1500);
-HARMONY_REGULATOR_INIT(ldo3, "vdd_ldo3", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo4, "vdd_ldo4", "vdd_sm2", 1700, 2475);
-HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", NULL, 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo6, "vdd_ldo6", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo7, "vdd_ldo7", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo8, "vdd_ldo8", "vdd_sm2", 1250, 3300);
-HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300);
+HARMONY_REGULATOR_INIT(sm0, "vdd_sm0", "vdd_sys", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(sm1, "vdd_sm1", "vdd_sys", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(sm2, "vdd_sm2", "vdd_sys", 3000, 4550, 1);
+HARMONY_REGULATOR_INIT(ldo1, "vdd_ldo1", "vdd_sm2", 725, 1500, 1);
+HARMONY_REGULATOR_INIT(ldo2, "vdd_ldo2", "vdd_sm2", 725, 1500, 0);
+HARMONY_REGULATOR_INIT(ldo3, "vdd_ldo3", "vdd_sm2", 1250, 3300, 1);
+HARMONY_REGULATOR_INIT(ldo4, "vdd_ldo4", "vdd_sm2", 1700, 2475, 1);
+HARMONY_REGULATOR_INIT(ldo5, "vdd_ldo5", NULL, 1250, 3300, 1);
+HARMONY_REGULATOR_INIT(ldo6, "vdd_ldo6", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo7, "vdd_ldo7", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo8, "vdd_ldo8", "vdd_sm2", 1250, 3300, 0);
+HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300, 1);
#define TPS_REG(_id, _data) \
{ \
@@ -119,9 +120,10 @@
int __init harmony_regulator_init(void)
{
+ regulator_register_always_on(0, "vdd_sys",
+ NULL, 0, 5000000);
+
if (machine_is_harmony()) {
- regulator_register_always_on(0, "vdd_sys",
- NULL, 0, 5000000);
i2c_register_board_info(3, harmony_regulators, 1);
} else { /* Harmony, booted using device tree */
struct device_node *np;
diff --git a/arch/arm/mach-vt8500/bv07.c b/arch/arm/mach-vt8500/bv07.c
index f9fbeb2..6fd9d60 100644
--- a/arch/arm/mach-vt8500/bv07.c
+++ b/arch/arm/mach-vt8500/bv07.c
@@ -33,6 +33,7 @@
&vt8500_device_uart0,
&vt8500_device_lcdc,
&vt8500_device_ehci,
+ &vt8500_device_uhci,
&vt8500_device_ge_rops,
&vt8500_device_pwm,
&vt8500_device_pwmbl,
diff --git a/arch/arm/mach-vt8500/devices-vt8500.c b/arch/arm/mach-vt8500/devices-vt8500.c
index 19519ae..def7fe3 100644
--- a/arch/arm/mach-vt8500/devices-vt8500.c
+++ b/arch/arm/mach-vt8500/devices-vt8500.c
@@ -48,6 +48,11 @@
tmp[1] = wmt_irq_res(IRQ_EHCI);
wmt_res_add(&vt8500_device_ehci, tmp, 2);
+ /* vt8500 uses a single IRQ for both EHCI and UHCI controllers */
+ tmp[0] = wmt_mmio_res(VT8500_UHCI_BASE, SZ_512);
+ tmp[1] = wmt_irq_res(IRQ_EHCI);
+ wmt_res_add(&vt8500_device_uhci, tmp, 2);
+
tmp[0] = wmt_mmio_res(VT8500_GEGEA_BASE, SZ_256);
wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
diff --git a/arch/arm/mach-vt8500/devices-wm8505.c b/arch/arm/mach-vt8500/devices-wm8505.c
index db4594e..c810454 100644
--- a/arch/arm/mach-vt8500/devices-wm8505.c
+++ b/arch/arm/mach-vt8500/devices-wm8505.c
@@ -55,6 +55,10 @@
tmp[1] = wmt_irq_res(IRQ_EHCI);
wmt_res_add(&vt8500_device_ehci, tmp, 2);
+ tmp[0] = wmt_mmio_res(WM8505_UHCI_BASE, SZ_512);
+ tmp[1] = wmt_irq_res(IRQ_UHCI);
+ wmt_res_add(&vt8500_device_uhci, tmp, 2);
+
tmp[0] = wmt_mmio_res(WM8505_GEGEA_BASE, SZ_256);
wmt_res_add(&vt8500_device_ge_rops, tmp, 1);
diff --git a/arch/arm/mach-vt8500/devices.c b/arch/arm/mach-vt8500/devices.c
index 1fcdc36..46ff82d 100644
--- a/arch/arm/mach-vt8500/devices.c
+++ b/arch/arm/mach-vt8500/devices.c
@@ -204,6 +204,17 @@
},
};
+static u64 uhci_dma_mask = DMA_BIT_MASK(32);
+
+struct platform_device vt8500_device_uhci = {
+ .name = "platform-uhci",
+ .id = 0,
+ .dev = {
+ .dma_mask = &uhci_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
struct platform_device vt8500_device_ge_rops = {
.name = "wmt_ge_rops",
.id = -1,
diff --git a/arch/arm/mach-vt8500/devices.h b/arch/arm/mach-vt8500/devices.h
index 188d4e1..0e6d9f9 100644
--- a/arch/arm/mach-vt8500/devices.h
+++ b/arch/arm/mach-vt8500/devices.h
@@ -81,6 +81,7 @@
extern struct platform_device vt8500_device_lcdc;
extern struct platform_device vt8500_device_wm8505_fb;
extern struct platform_device vt8500_device_ehci;
+extern struct platform_device vt8500_device_uhci;
extern struct platform_device vt8500_device_ge_rops;
extern struct platform_device vt8500_device_pwm;
extern struct platform_device vt8500_device_pwmbl;
diff --git a/arch/arm/mach-vt8500/wm8505_7in.c b/arch/arm/mach-vt8500/wm8505_7in.c
index db19886..4804e2a 100644
--- a/arch/arm/mach-vt8500/wm8505_7in.c
+++ b/arch/arm/mach-vt8500/wm8505_7in.c
@@ -32,6 +32,7 @@
static struct platform_device *devices[] __initdata = {
&vt8500_device_uart0,
&vt8500_device_ehci,
+ &vt8500_device_uhci,
&vt8500_device_wm8505_fb,
&vt8500_device_ge_rops,
&vt8500_device_pwm,
diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index c2cdf65..4e7d118 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -358,7 +358,7 @@
if (end > arm_lowmem_limit)
end = arm_lowmem_limit;
if (start >= end)
- return;
+ continue;
map.pfn = __phys_to_pfn(start);
map.virtual = __phys_to_virt(start);
@@ -423,7 +423,7 @@
unsigned int pageno;
unsigned long flags;
void *ptr = NULL;
- size_t align;
+ unsigned long align_mask;
if (!pool->vaddr) {
WARN(1, "coherent pool not initialised!\n");
@@ -435,11 +435,11 @@
* small, so align them to their order in pages, minimum is a page
* size. This helps reduce fragmentation of the DMA space.
*/
- align = PAGE_SIZE << get_order(size);
+ align_mask = (1 << get_order(size)) - 1;
spin_lock_irqsave(&pool->lock, flags);
pageno = bitmap_find_next_zero_area(pool->bitmap, pool->nr_pages,
- 0, count, (1 << align) - 1);
+ 0, count, align_mask);
if (pageno < pool->nr_pages) {
bitmap_set(pool->bitmap, pageno, count);
ptr = pool->vaddr + PAGE_SIZE * pageno;
@@ -648,12 +648,12 @@
if (arch_is_coherent() || nommu()) {
__dma_free_buffer(page, size);
+ } else if (__free_from_pool(cpu_addr, size)) {
+ return;
} else if (!IS_ENABLED(CONFIG_CMA)) {
__dma_free_remap(cpu_addr, size);
__dma_free_buffer(page, size);
} else {
- if (__free_from_pool(cpu_addr, size))
- return;
/*
* Non-atomic allocations cannot be freed with IRQs disabled
*/
diff --git a/arch/arm/mm/flush.c b/arch/arm/mm/flush.c
index 7745854..40ca11e 100644
--- a/arch/arm/mm/flush.c
+++ b/arch/arm/mm/flush.c
@@ -231,8 +231,6 @@
struct page *page;
struct address_space *mapping;
- if (!pte_present_user(pteval))
- return;
if (cache_is_vipt_nonaliasing() && !pte_exec(pteval))
/* only flush non-aliasing VIPT caches for exec mappings */
return;
diff --git a/arch/arm/mm/tlb-v7.S b/arch/arm/mm/tlb-v7.S
index c202113..ea94765 100644
--- a/arch/arm/mm/tlb-v7.S
+++ b/arch/arm/mm/tlb-v7.S
@@ -38,10 +38,10 @@
dsb
mov r0, r0, lsr #PAGE_SHIFT @ align address
mov r1, r1, lsr #PAGE_SHIFT
-#ifdef CONFIG_ARM_ERRATA_720789
- mov r3, #0
-#else
asid r3, r3 @ mask ASID
+#ifdef CONFIG_ARM_ERRATA_720789
+ ALT_SMP(W(mov) r3, #0 )
+ ALT_UP(W(nop) )
#endif
orr r0, r3, r0, lsl #PAGE_SHIFT @ Create initial MVA
mov r1, r1, lsl #PAGE_SHIFT
diff --git a/arch/arm/plat-samsung/Kconfig b/arch/arm/plat-samsung/Kconfig
index 7aca31c..9c3b90c 100644
--- a/arch/arm/plat-samsung/Kconfig
+++ b/arch/arm/plat-samsung/Kconfig
@@ -403,7 +403,8 @@
config S3C24XX_PWM
bool "PWM device support"
- select HAVE_PWM
+ select PWM
+ select PWM_SAMSUNG
help
Support for exporting the PWM timer blocks via the pwm device
system
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index fb849d0..c834b32 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -719,8 +719,10 @@
if ((fmrx(MVFR1) & 0x000fff00) == 0x00011100)
elf_hwcap |= HWCAP_NEON;
#endif
+#ifdef CONFIG_VFPv3
if ((fmrx(MVFR1) & 0xf0000000) == 0x10000000)
elf_hwcap |= HWCAP_VFPv4;
+#endif
}
}
return 0;
diff --git a/arch/blackfin/kernel/setup.c b/arch/blackfin/kernel/setup.c
index ada8f0f..fb96e60 100644
--- a/arch/blackfin/kernel/setup.c
+++ b/arch/blackfin/kernel/setup.c
@@ -52,7 +52,6 @@
#ifdef CONFIG_MTD_UCLINUX
extern struct map_info uclinux_ram_map;
unsigned long memory_mtd_end, memory_mtd_start, mtd_size;
-unsigned long _ebss;
EXPORT_SYMBOL(memory_mtd_end);
EXPORT_SYMBOL(memory_mtd_start);
EXPORT_SYMBOL(mtd_size);
diff --git a/arch/c6x/Kconfig b/arch/c6x/Kconfig
index 052f81a..983c859 100644
--- a/arch/c6x/Kconfig
+++ b/arch/c6x/Kconfig
@@ -6,6 +6,7 @@
config C6X
def_bool y
select CLKDEV_LOOKUP
+ select GENERIC_ATOMIC64
select GENERIC_IRQ_SHOW
select HAVE_ARCH_TRACEHOOK
select HAVE_DMA_API_DEBUG
diff --git a/arch/c6x/include/asm/cache.h b/arch/c6x/include/asm/cache.h
index 6d521d9..09c5a0f 100644
--- a/arch/c6x/include/asm/cache.h
+++ b/arch/c6x/include/asm/cache.h
@@ -1,7 +1,7 @@
/*
* Port on Texas Instruments TMS320C6x architecture
*
- * Copyright (C) 2005, 2006, 2009, 2010 Texas Instruments Incorporated
+ * Copyright (C) 2005, 2006, 2009, 2010, 2012 Texas Instruments Incorporated
* Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
*
* This program is free software; you can redistribute it and/or modify
@@ -16,9 +16,14 @@
/*
* Cache line size
*/
-#define L1D_CACHE_BYTES 64
-#define L1P_CACHE_BYTES 32
-#define L2_CACHE_BYTES 128
+#define L1D_CACHE_SHIFT 6
+#define L1D_CACHE_BYTES (1 << L1D_CACHE_SHIFT)
+
+#define L1P_CACHE_SHIFT 5
+#define L1P_CACHE_BYTES (1 << L1P_CACHE_SHIFT)
+
+#define L2_CACHE_SHIFT 7
+#define L2_CACHE_BYTES (1 << L2_CACHE_SHIFT)
/*
* L2 used as cache
@@ -29,7 +34,8 @@
* For practical reasons the L1_CACHE_BYTES defines should not be smaller than
* the L2 line size
*/
-#define L1_CACHE_BYTES L2_CACHE_BYTES
+#define L1_CACHE_SHIFT L2_CACHE_SHIFT
+#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
#define L2_CACHE_ALIGN_LOW(x) \
(((x) & ~(L2_CACHE_BYTES - 1)))
diff --git a/arch/ia64/configs/generic_defconfig b/arch/ia64/configs/generic_defconfig
index 954d81e..7913695 100644
--- a/arch/ia64/configs/generic_defconfig
+++ b/arch/ia64/configs/generic_defconfig
@@ -234,5 +234,4 @@
CONFIG_CRYPTO_MD5=y
# CONFIG_CRYPTO_ANSI_CPRNG is not set
CONFIG_CRC_T10DIF=y
-CONFIG_MISC_DEVICES=y
CONFIG_INTEL_IOMMU=y
diff --git a/arch/ia64/configs/gensparse_defconfig b/arch/ia64/configs/gensparse_defconfig
index 91c41ec..f8e9133 100644
--- a/arch/ia64/configs/gensparse_defconfig
+++ b/arch/ia64/configs/gensparse_defconfig
@@ -209,4 +209,3 @@
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_MUTEXES=y
CONFIG_CRYPTO_MD5=y
-CONFIG_MISC_DEVICES=y
diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 6f38b61..44057885 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -497,7 +497,7 @@
srat_num_cpus++;
}
-void __init
+int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
unsigned long paddr, size;
@@ -512,7 +512,7 @@
/* Ignore disabled entries */
if (!(ma->flags & ACPI_SRAT_MEM_ENABLED))
- return;
+ return -1;
/* record this node in proximity bitmap */
pxm_bit_set(pxm);
@@ -531,6 +531,7 @@
p->size = size;
p->nid = pxm;
num_node_memblks++;
+ return 0;
}
void __init acpi_numa_arch_fixup(void)
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 0b0f8b8..b22df94 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -5,6 +5,7 @@
select HAVE_AOUT if MMU
select HAVE_GENERIC_HARDIRQS
select GENERIC_IRQ_SHOW
+ select GENERIC_ATOMIC64
select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
select GENERIC_CPU_DEVICES
select GENERIC_STRNCPY_FROM_USER if MMU
@@ -54,18 +55,6 @@
bool
default y
-config CPU_HAS_NO_BITFIELDS
- bool
-
-config CPU_HAS_NO_MULDIV64
- bool
-
-config CPU_HAS_ADDRESS_SPACES
- bool
-
-config FPU
- bool
-
config HZ
int
default 1000 if CLEOPATRA
diff --git a/arch/m68k/Kconfig.cpu b/arch/m68k/Kconfig.cpu
index 43a9f8f..c4eb79e 100644
--- a/arch/m68k/Kconfig.cpu
+++ b/arch/m68k/Kconfig.cpu
@@ -28,6 +28,7 @@
select CPU_HAS_NO_BITFIELDS
select CPU_HAS_NO_MULDIV64
select GENERIC_CSUM
+ select HAVE_CLK
endchoice
@@ -37,6 +38,7 @@
bool
select CPU_HAS_NO_BITFIELDS
select CPU_HAS_NO_MULDIV64
+ select CPU_HAS_NO_UNALIGNED
select GENERIC_CSUM
help
The Freescale (was Motorola) 68000 CPU is the first generation of
@@ -48,6 +50,7 @@
config MCPU32
bool
select CPU_HAS_NO_BITFIELDS
+ select CPU_HAS_NO_UNALIGNED
help
The Freescale (was then Motorola) CPU32 is a CPU core that is
based on the 68020 processor. For the most part it is used in
@@ -56,7 +59,6 @@
config M68020
bool "68020 support"
depends on MMU
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68020
@@ -67,7 +69,6 @@
config M68030
bool "68030 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68030
@@ -77,7 +78,6 @@
config M68040
bool "68040 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68LC040
@@ -88,7 +88,6 @@
config M68060
bool "68060 support"
depends on MMU && !MMU_SUN3
- select GENERIC_ATOMIC64
select CPU_HAS_ADDRESS_SPACES
help
If you anticipate running this kernel on a computer with a MC68060
@@ -376,6 +375,18 @@
default "3"
depends on !SINGLE_MEMORY_CHUNK
+config CPU_HAS_NO_BITFIELDS
+ bool
+
+config CPU_HAS_NO_MULDIV64
+ bool
+
+config CPU_HAS_NO_UNALIGNED
+ bool
+
+config CPU_HAS_ADDRESS_SPACES
+ bool
+
config FPU
bool
diff --git a/arch/m68k/apollo/config.c b/arch/m68k/apollo/config.c
index 0a30406..f5565d6 100644
--- a/arch/m68k/apollo/config.c
+++ b/arch/m68k/apollo/config.c
@@ -177,8 +177,8 @@
timer_handler(irq, dev_id);
- x=*(volatile unsigned char *)(timer+3);
- x=*(volatile unsigned char *)(timer+5);
+ x = *(volatile unsigned char *)(apollo_timer + 3);
+ x = *(volatile unsigned char *)(apollo_timer + 5);
return IRQ_HANDLED;
}
@@ -186,17 +186,17 @@
void dn_sched_init(irq_handler_t timer_routine)
{
/* program timer 1 */
- *(volatile unsigned char *)(timer+3)=0x01;
- *(volatile unsigned char *)(timer+1)=0x40;
- *(volatile unsigned char *)(timer+5)=0x09;
- *(volatile unsigned char *)(timer+7)=0xc4;
+ *(volatile unsigned char *)(apollo_timer + 3) = 0x01;
+ *(volatile unsigned char *)(apollo_timer + 1) = 0x40;
+ *(volatile unsigned char *)(apollo_timer + 5) = 0x09;
+ *(volatile unsigned char *)(apollo_timer + 7) = 0xc4;
/* enable IRQ of PIC B */
*(volatile unsigned char *)(pica+1)&=(~8);
#if 0
- printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
- printk("*(0x10803) %02x\n",*(volatile unsigned char *)(timer+0x3));
+ printk("*(0x10803) %02x\n",*(volatile unsigned char *)(apollo_timer + 0x3));
+ printk("*(0x10803) %02x\n",*(volatile unsigned char *)(apollo_timer + 0x3));
#endif
if (request_irq(IRQ_APOLLO, dn_timer_int, 0, "time", timer_routine))
diff --git a/arch/m68k/include/asm/Kbuild b/arch/m68k/include/asm/Kbuild
index eafa253..a74e5d9 100644
--- a/arch/m68k/include/asm/Kbuild
+++ b/arch/m68k/include/asm/Kbuild
@@ -1,4 +1,29 @@
include include/asm-generic/Kbuild.asm
header-y += cachectl.h
+generic-y += bitsperlong.h
+generic-y += cputime.h
+generic-y += device.h
+generic-y += emergency-restart.h
+generic-y += errno.h
+generic-y += futex.h
+generic-y += ioctl.h
+generic-y += ipcbuf.h
+generic-y += irq_regs.h
+generic-y += kdebug.h
+generic-y += kmap_types.h
+generic-y += kvm_para.h
+generic-y += local64.h
+generic-y += local.h
+generic-y += mman.h
+generic-y += mutex.h
+generic-y += percpu.h
+generic-y += resource.h
+generic-y += scatterlist.h
+generic-y += sections.h
+generic-y += siginfo.h
+generic-y += statfs.h
+generic-y += topology.h
+generic-y += types.h
generic-y += word-at-a-time.h
+generic-y += xor.h
diff --git a/arch/m68k/include/asm/MC68332.h b/arch/m68k/include/asm/MC68332.h
deleted file mode 100644
index 6bb8f02..0000000
--- a/arch/m68k/include/asm/MC68332.h
+++ /dev/null
@@ -1,152 +0,0 @@
-
-/* include/asm-m68knommu/MC68332.h: '332 control registers
- *
- * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
- *
- */
-
-#ifndef _MC68332_H_
-#define _MC68332_H_
-
-#define BYTE_REF(addr) (*((volatile unsigned char*)addr))
-#define WORD_REF(addr) (*((volatile unsigned short*)addr))
-
-#define PORTE_ADDR 0xfffa11
-#define PORTE BYTE_REF(PORTE_ADDR)
-#define DDRE_ADDR 0xfffa15
-#define DDRE BYTE_REF(DDRE_ADDR)
-#define PEPAR_ADDR 0xfffa17
-#define PEPAR BYTE_REF(PEPAR_ADDR)
-
-#define PORTF_ADDR 0xfffa19
-#define PORTF BYTE_REF(PORTF_ADDR)
-#define DDRF_ADDR 0xfffa1d
-#define DDRF BYTE_REF(DDRF_ADDR)
-#define PFPAR_ADDR 0xfffa1f
-#define PFPAR BYTE_REF(PFPAR_ADDR)
-
-#define PORTQS_ADDR 0xfffc15
-#define PORTQS BYTE_REF(PORTQS_ADDR)
-#define DDRQS_ADDR 0xfffc17
-#define DDRQS BYTE_REF(DDRQS_ADDR)
-#define PQSPAR_ADDR 0xfffc16
-#define PQSPAR BYTE_REF(PQSPAR_ADDR)
-
-#define CSPAR0_ADDR 0xFFFA44
-#define CSPAR0 WORD_REF(CSPAR0_ADDR)
-#define CSPAR1_ADDR 0xFFFA46
-#define CSPAR1 WORD_REF(CSPAR1_ADDR)
-#define CSARBT_ADDR 0xFFFA48
-#define CSARBT WORD_REF(CSARBT_ADDR)
-#define CSOPBT_ADDR 0xFFFA4A
-#define CSOPBT WORD_REF(CSOPBT_ADDR)
-#define CSBAR0_ADDR 0xFFFA4C
-#define CSBAR0 WORD_REF(CSBAR0_ADDR)
-#define CSOR0_ADDR 0xFFFA4E
-#define CSOR0 WORD_REF(CSOR0_ADDR)
-#define CSBAR1_ADDR 0xFFFA50
-#define CSBAR1 WORD_REF(CSBAR1_ADDR)
-#define CSOR1_ADDR 0xFFFA52
-#define CSOR1 WORD_REF(CSOR1_ADDR)
-#define CSBAR2_ADDR 0xFFFA54
-#define CSBAR2 WORD_REF(CSBAR2_ADDR)
-#define CSOR2_ADDR 0xFFFA56
-#define CSOR2 WORD_REF(CSOR2_ADDR)
-#define CSBAR3_ADDR 0xFFFA58
-#define CSBAR3 WORD_REF(CSBAR3_ADDR)
-#define CSOR3_ADDR 0xFFFA5A
-#define CSOR3 WORD_REF(CSOR3_ADDR)
-#define CSBAR4_ADDR 0xFFFA5C
-#define CSBAR4 WORD_REF(CSBAR4_ADDR)
-#define CSOR4_ADDR 0xFFFA5E
-#define CSOR4 WORD_REF(CSOR4_ADDR)
-#define CSBAR5_ADDR 0xFFFA60
-#define CSBAR5 WORD_REF(CSBAR5_ADDR)
-#define CSOR5_ADDR 0xFFFA62
-#define CSOR5 WORD_REF(CSOR5_ADDR)
-#define CSBAR6_ADDR 0xFFFA64
-#define CSBAR6 WORD_REF(CSBAR6_ADDR)
-#define CSOR6_ADDR 0xFFFA66
-#define CSOR6 WORD_REF(CSOR6_ADDR)
-#define CSBAR7_ADDR 0xFFFA68
-#define CSBAR7 WORD_REF(CSBAR7_ADDR)
-#define CSOR7_ADDR 0xFFFA6A
-#define CSOR7 WORD_REF(CSOR7_ADDR)
-#define CSBAR8_ADDR 0xFFFA6C
-#define CSBAR8 WORD_REF(CSBAR8_ADDR)
-#define CSOR8_ADDR 0xFFFA6E
-#define CSOR8 WORD_REF(CSOR8_ADDR)
-#define CSBAR9_ADDR 0xFFFA70
-#define CSBAR9 WORD_REF(CSBAR9_ADDR)
-#define CSOR9_ADDR 0xFFFA72
-#define CSOR9 WORD_REF(CSOR9_ADDR)
-#define CSBAR10_ADDR 0xFFFA74
-#define CSBAR10 WORD_REF(CSBAR10_ADDR)
-#define CSOR10_ADDR 0xFFFA76
-#define CSOR10 WORD_REF(CSOR10_ADDR)
-
-#define CSOR_MODE_ASYNC 0x0000
-#define CSOR_MODE_SYNC 0x8000
-#define CSOR_MODE_MASK 0x8000
-#define CSOR_BYTE_DISABLE 0x0000
-#define CSOR_BYTE_UPPER 0x4000
-#define CSOR_BYTE_LOWER 0x2000
-#define CSOR_BYTE_BOTH 0x6000
-#define CSOR_BYTE_MASK 0x6000
-#define CSOR_RW_RSVD 0x0000
-#define CSOR_RW_READ 0x0800
-#define CSOR_RW_WRITE 0x1000
-#define CSOR_RW_BOTH 0x1800
-#define CSOR_RW_MASK 0x1800
-#define CSOR_STROBE_DS 0x0400
-#define CSOR_STROBE_AS 0x0000
-#define CSOR_STROBE_MASK 0x0400
-#define CSOR_DSACK_WAIT(x) (wait << 6)
-#define CSOR_DSACK_FTERM (14 << 6)
-#define CSOR_DSACK_EXTERNAL (15 << 6)
-#define CSOR_DSACK_MASK 0x03c0
-#define CSOR_SPACE_CPU 0x0000
-#define CSOR_SPACE_USER 0x0010
-#define CSOR_SPACE_SU 0x0020
-#define CSOR_SPACE_BOTH 0x0030
-#define CSOR_SPACE_MASK 0x0030
-#define CSOR_IPL_ALL 0x0000
-#define CSOR_IPL_PRIORITY(x) (x << 1)
-#define CSOR_IPL_MASK 0x000e
-#define CSOR_AVEC_ON 0x0001
-#define CSOR_AVEC_OFF 0x0000
-#define CSOR_AVEC_MASK 0x0001
-
-#define CSBAR_ADDR(x) ((addr >> 11) << 3)
-#define CSBAR_ADDR_MASK 0xfff8
-#define CSBAR_BLKSIZE_2K 0x0000
-#define CSBAR_BLKSIZE_8K 0x0001
-#define CSBAR_BLKSIZE_16K 0x0002
-#define CSBAR_BLKSIZE_64K 0x0003
-#define CSBAR_BLKSIZE_128K 0x0004
-#define CSBAR_BLKSIZE_256K 0x0005
-#define CSBAR_BLKSIZE_512K 0x0006
-#define CSBAR_BLKSIZE_1M 0x0007
-#define CSBAR_BLKSIZE_MASK 0x0007
-
-#define CSPAR_DISC 0
-#define CSPAR_ALT 1
-#define CSPAR_CS8 2
-#define CSPAR_CS16 3
-#define CSPAR_MASK 3
-
-#define CSPAR0_CSBOOT(x) (x << 0)
-#define CSPAR0_CS0(x) (x << 2)
-#define CSPAR0_CS1(x) (x << 4)
-#define CSPAR0_CS2(x) (x << 6)
-#define CSPAR0_CS3(x) (x << 8)
-#define CSPAR0_CS4(x) (x << 10)
-#define CSPAR0_CS5(x) (x << 12)
-
-#define CSPAR1_CS6(x) (x << 0)
-#define CSPAR1_CS7(x) (x << 2)
-#define CSPAR1_CS8(x) (x << 4)
-#define CSPAR1_CS9(x) (x << 6)
-#define CSPAR1_CS10(x) (x << 8)
-
-#endif
diff --git a/arch/m68k/include/asm/apollodma.h b/arch/m68k/include/asm/apollodma.h
deleted file mode 100644
index 954adc8..0000000
--- a/arch/m68k/include/asm/apollodma.h
+++ /dev/null
@@ -1,248 +0,0 @@
-/*
- * linux/include/asm/dma.h: Defines for using and allocating dma channels.
- * Written by Hennus Bergman, 1992.
- * High DMA channel support & info by Hannu Savolainen
- * and John Boyd, Nov. 1992.
- */
-
-#ifndef _ASM_APOLLO_DMA_H
-#define _ASM_APOLLO_DMA_H
-
-#include <asm/apollohw.h> /* need byte IO */
-#include <linux/spinlock.h> /* And spinlocks */
-#include <linux/delay.h>
-
-
-#define dma_outb(val,addr) (*((volatile unsigned char *)(addr+IO_BASE)) = (val))
-#define dma_inb(addr) (*((volatile unsigned char *)(addr+IO_BASE)))
-
-/*
- * NOTES about DMA transfers:
- *
- * controller 1: channels 0-3, byte operations, ports 00-1F
- * controller 2: channels 4-7, word operations, ports C0-DF
- *
- * - ALL registers are 8 bits only, regardless of transfer size
- * - channel 4 is not used - cascades 1 into 2.
- * - channels 0-3 are byte - addresses/counts are for physical bytes
- * - channels 5-7 are word - addresses/counts are for physical words
- * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
- * - transfer count loaded to registers is 1 less than actual count
- * - controller 2 offsets are all even (2x offsets for controller 1)
- * - page registers for 5-7 don't use data bit 0, represent 128K pages
- * - page registers for 0-3 use bit 0, represent 64K pages
- *
- * DMA transfers are limited to the lower 16MB of _physical_ memory.
- * Note that addresses loaded into registers must be _physical_ addresses,
- * not logical addresses (which may differ if paging is active).
- *
- * Address mapping for channels 0-3:
- *
- * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses)
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * | ... | | ... | | ... |
- * P7 ... P0 A7 ... A0 A7 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Address mapping for channels 5-7:
- *
- * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses)
- * | ... | \ \ ... \ \ \ ... \ \
- * | ... | \ \ ... \ \ \ ... \ (not used)
- * | ... | \ \ ... \ \ \ ... \
- * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0
- * | Page | Addr MSB | Addr LSB | (DMA registers)
- *
- * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
- * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
- * the hardware level, so odd-byte transfers aren't possible).
- *
- * Transfer count (_not # bytes_) is limited to 64K, represented as actual
- * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more,
- * and up to 128K bytes may be transferred on channels 5-7 in one operation.
- *
- */
-
-#define MAX_DMA_CHANNELS 8
-
-/* The maximum address that we can perform a DMA transfer to on this platform */#define MAX_DMA_ADDRESS (PAGE_OFFSET+0x1000000)
-
-/* 8237 DMA controllers */
-#define IO_DMA1_BASE 0x10C00 /* 8 bit slave DMA, channels 0..3 */
-#define IO_DMA2_BASE 0x10D00 /* 16 bit master DMA, ch 4(=slave input)..7 */
-
-/* DMA controller registers */
-#define DMA1_CMD_REG (IO_DMA1_BASE+0x08) /* command register (w) */
-#define DMA1_STAT_REG (IO_DMA1_BASE+0x08) /* status register (r) */
-#define DMA1_REQ_REG (IO_DMA1_BASE+0x09) /* request register (w) */
-#define DMA1_MASK_REG (IO_DMA1_BASE+0x0A) /* single-channel mask (w) */
-#define DMA1_MODE_REG (IO_DMA1_BASE+0x0B) /* mode register (w) */
-#define DMA1_CLEAR_FF_REG (IO_DMA1_BASE+0x0C) /* clear pointer flip-flop (w) */
-#define DMA1_TEMP_REG (IO_DMA1_BASE+0x0D) /* Temporary Register (r) */
-#define DMA1_RESET_REG (IO_DMA1_BASE+0x0D) /* Master Clear (w) */
-#define DMA1_CLR_MASK_REG (IO_DMA1_BASE+0x0E) /* Clear Mask */
-#define DMA1_MASK_ALL_REG (IO_DMA1_BASE+0x0F) /* all-channels mask (w) */
-
-#define DMA2_CMD_REG (IO_DMA2_BASE+0x10) /* command register (w) */
-#define DMA2_STAT_REG (IO_DMA2_BASE+0x10) /* status register (r) */
-#define DMA2_REQ_REG (IO_DMA2_BASE+0x12) /* request register (w) */
-#define DMA2_MASK_REG (IO_DMA2_BASE+0x14) /* single-channel mask (w) */
-#define DMA2_MODE_REG (IO_DMA2_BASE+0x16) /* mode register (w) */
-#define DMA2_CLEAR_FF_REG (IO_DMA2_BASE+0x18) /* clear pointer flip-flop (w) */
-#define DMA2_TEMP_REG (IO_DMA2_BASE+0x1A) /* Temporary Register (r) */
-#define DMA2_RESET_REG (IO_DMA2_BASE+0x1A) /* Master Clear (w) */
-#define DMA2_CLR_MASK_REG (IO_DMA2_BASE+0x1C) /* Clear Mask */
-#define DMA2_MASK_ALL_REG (IO_DMA2_BASE+0x1E) /* all-channels mask (w) */
-
-#define DMA_ADDR_0 (IO_DMA1_BASE+0x00) /* DMA address registers */
-#define DMA_ADDR_1 (IO_DMA1_BASE+0x02)
-#define DMA_ADDR_2 (IO_DMA1_BASE+0x04)
-#define DMA_ADDR_3 (IO_DMA1_BASE+0x06)
-#define DMA_ADDR_4 (IO_DMA2_BASE+0x00)
-#define DMA_ADDR_5 (IO_DMA2_BASE+0x04)
-#define DMA_ADDR_6 (IO_DMA2_BASE+0x08)
-#define DMA_ADDR_7 (IO_DMA2_BASE+0x0C)
-
-#define DMA_CNT_0 (IO_DMA1_BASE+0x01) /* DMA count registers */
-#define DMA_CNT_1 (IO_DMA1_BASE+0x03)
-#define DMA_CNT_2 (IO_DMA1_BASE+0x05)
-#define DMA_CNT_3 (IO_DMA1_BASE+0x07)
-#define DMA_CNT_4 (IO_DMA2_BASE+0x02)
-#define DMA_CNT_5 (IO_DMA2_BASE+0x06)
-#define DMA_CNT_6 (IO_DMA2_BASE+0x0A)
-#define DMA_CNT_7 (IO_DMA2_BASE+0x0E)
-
-#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
-#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
-#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */
-
-#define DMA_AUTOINIT 0x10
-
-#define DMA_8BIT 0
-#define DMA_16BIT 1
-#define DMA_BUSMASTER 2
-
-extern spinlock_t dma_spin_lock;
-
-static __inline__ unsigned long claim_dma_lock(void)
-{
- unsigned long flags;
- spin_lock_irqsave(&dma_spin_lock, flags);
- return flags;
-}
-
-static __inline__ void release_dma_lock(unsigned long flags)
-{
- spin_unlock_irqrestore(&dma_spin_lock, flags);
-}
-
-/* enable/disable a specific DMA channel */
-static __inline__ void enable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(dmanr, DMA1_MASK_REG);
- else
- dma_outb(dmanr & 3, DMA2_MASK_REG);
-}
-
-static __inline__ void disable_dma(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(dmanr | 4, DMA1_MASK_REG);
- else
- dma_outb((dmanr & 3) | 4, DMA2_MASK_REG);
-}
-
-/* Clear the 'DMA Pointer Flip Flop'.
- * Write 0 for LSB/MSB, 1 for MSB/LSB access.
- * Use this once to initialize the FF to a known state.
- * After that, keep track of it. :-)
- * --- In order to do that, the DMA routines below should ---
- * --- only be used while holding the DMA lock ! ---
- */
-static __inline__ void clear_dma_ff(unsigned int dmanr)
-{
- if (dmanr<=3)
- dma_outb(0, DMA1_CLEAR_FF_REG);
- else
- dma_outb(0, DMA2_CLEAR_FF_REG);
-}
-
-/* set mode (above) for a specific DMA channel */
-static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
-{
- if (dmanr<=3)
- dma_outb(mode | dmanr, DMA1_MODE_REG);
- else
- dma_outb(mode | (dmanr&3), DMA2_MODE_REG);
-}
-
-/* Set transfer address & page bits for specific DMA channel.
- * Assumes dma flipflop is clear.
- */
-static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
-{
- if (dmanr <= 3) {
- dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
- dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
- } else {
- dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
- }
-}
-
-
-/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
- * a specific DMA channel.
- * You must ensure the parameters are valid.
- * NOTE: from a manual: "the number of transfers is one more
- * than the initial word count"! This is taken into account.
- * Assumes dma flip-flop is clear.
- * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
- */
-static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
-{
- count--;
- if (dmanr <= 3) {
- dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
- } else {
- dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
- }
-}
-
-
-/* Get DMA residue count. After a DMA transfer, this
- * should return zero. Reading this while a DMA transfer is
- * still in progress will return unpredictable results.
- * If called before the channel has been used, it may return 1.
- * Otherwise, it returns the number of _bytes_ left to transfer.
- *
- * Assumes DMA flip-flop is clear.
- */
-static __inline__ int get_dma_residue(unsigned int dmanr)
-{
- unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
- : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
-
- /* using short to get 16-bit wrap around */
- unsigned short count;
-
- count = 1 + dma_inb(io_port);
- count += dma_inb(io_port) << 8;
-
- return (dmanr<=3)? count : (count<<1);
-}
-
-
-/* These are in kernel/dma.c: */
-extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */
-extern void free_dma(unsigned int dmanr); /* release it again */
-
-/* These are in arch/m68k/apollo/dma.c: */
-extern unsigned short dma_map_page(unsigned long phys_addr,int count,int type);
-extern void dma_unmap_page(unsigned short dma_addr);
-
-#endif /* _ASM_APOLLO_DMA_H */
diff --git a/arch/m68k/include/asm/apollohw.h b/arch/m68k/include/asm/apollohw.h
index a1373b9..635ef4f 100644
--- a/arch/m68k/include/asm/apollohw.h
+++ b/arch/m68k/include/asm/apollohw.h
@@ -98,7 +98,7 @@
#define cpuctrl (*(volatile unsigned int *)(IO_BASE + cpuctrl_physaddr))
#define pica (IO_BASE + pica_physaddr)
#define picb (IO_BASE + picb_physaddr)
-#define timer (IO_BASE + timer_physaddr)
+#define apollo_timer (IO_BASE + timer_physaddr)
#define addr_xlat_map ((unsigned short *)(IO_BASE + 0x17000))
#define isaIO2mem(x) (((((x) & 0x3f8) << 7) | (((x) & 0xfc00) >> 6) | ((x) & 0x7)) + 0x40000 + IO_BASE)
diff --git a/arch/m68k/include/asm/bitsperlong.h b/arch/m68k/include/asm/bitsperlong.h
deleted file mode 100644
index 6dc0bb0..0000000
--- a/arch/m68k/include/asm/bitsperlong.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/bitsperlong.h>
diff --git a/arch/m68k/include/asm/cputime.h b/arch/m68k/include/asm/cputime.h
deleted file mode 100644
index c79c5e8..0000000
--- a/arch/m68k/include/asm/cputime.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __M68K_CPUTIME_H
-#define __M68K_CPUTIME_H
-
-#include <asm-generic/cputime.h>
-
-#endif /* __M68K_CPUTIME_H */
diff --git a/arch/m68k/include/asm/delay.h b/arch/m68k/include/asm/delay.h
index 9c09bec..12d8fe4 100644
--- a/arch/m68k/include/asm/delay.h
+++ b/arch/m68k/include/asm/delay.h
@@ -43,7 +43,7 @@
extern void __bad_udelay(void);
-#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
+#ifdef CONFIG_CPU_HAS_NO_MULDIV64
/*
* The simpler m68k and ColdFire processors do not have a 32*32->64
* multiply instruction. So we need to handle them a little differently.
diff --git a/arch/m68k/include/asm/device.h b/arch/m68k/include/asm/device.h
deleted file mode 100644
index d8f9872..0000000
--- a/arch/m68k/include/asm/device.h
+++ /dev/null
@@ -1,7 +0,0 @@
-/*
- * Arch specific extensions to struct device
- *
- * This file is released under the GPLv2
- */
-#include <asm-generic/device.h>
-
diff --git a/arch/m68k/include/asm/emergency-restart.h b/arch/m68k/include/asm/emergency-restart.h
deleted file mode 100644
index 108d8c4..0000000
--- a/arch/m68k/include/asm/emergency-restart.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_EMERGENCY_RESTART_H
-#define _ASM_EMERGENCY_RESTART_H
-
-#include <asm-generic/emergency-restart.h>
-
-#endif /* _ASM_EMERGENCY_RESTART_H */
diff --git a/arch/m68k/include/asm/errno.h b/arch/m68k/include/asm/errno.h
deleted file mode 100644
index 0d4e188..0000000
--- a/arch/m68k/include/asm/errno.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_ERRNO_H
-#define _M68K_ERRNO_H
-
-#include <asm-generic/errno.h>
-
-#endif /* _M68K_ERRNO_H */
diff --git a/arch/m68k/include/asm/futex.h b/arch/m68k/include/asm/futex.h
deleted file mode 100644
index 6a332a9..0000000
--- a/arch/m68k/include/asm/futex.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_FUTEX_H
-#define _ASM_FUTEX_H
-
-#include <asm-generic/futex.h>
-
-#endif
diff --git a/arch/m68k/include/asm/ioctl.h b/arch/m68k/include/asm/ioctl.h
deleted file mode 100644
index b279fe0..0000000
--- a/arch/m68k/include/asm/ioctl.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ioctl.h>
diff --git a/arch/m68k/include/asm/ipcbuf.h b/arch/m68k/include/asm/ipcbuf.h
deleted file mode 100644
index 84c7e51..0000000
--- a/arch/m68k/include/asm/ipcbuf.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/ipcbuf.h>
diff --git a/arch/m68k/include/asm/irq_regs.h b/arch/m68k/include/asm/irq_regs.h
deleted file mode 100644
index 3dd9c0b..0000000
--- a/arch/m68k/include/asm/irq_regs.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/irq_regs.h>
diff --git a/arch/m68k/include/asm/kdebug.h b/arch/m68k/include/asm/kdebug.h
deleted file mode 100644
index 6ece1b0..0000000
--- a/arch/m68k/include/asm/kdebug.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kdebug.h>
diff --git a/arch/m68k/include/asm/kmap_types.h b/arch/m68k/include/asm/kmap_types.h
deleted file mode 100644
index 3413cc1..0000000
--- a/arch/m68k/include/asm/kmap_types.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_M68K_KMAP_TYPES_H
-#define __ASM_M68K_KMAP_TYPES_H
-
-#include <asm-generic/kmap_types.h>
-
-#endif /* __ASM_M68K_KMAP_TYPES_H */
diff --git a/arch/m68k/include/asm/kvm_para.h b/arch/m68k/include/asm/kvm_para.h
deleted file mode 100644
index 14fab8f..0000000
--- a/arch/m68k/include/asm/kvm_para.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/kvm_para.h>
diff --git a/arch/m68k/include/asm/local.h b/arch/m68k/include/asm/local.h
deleted file mode 100644
index 6c25926..0000000
--- a/arch/m68k/include/asm/local.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_M68K_LOCAL_H
-#define _ASM_M68K_LOCAL_H
-
-#include <asm-generic/local.h>
-
-#endif /* _ASM_M68K_LOCAL_H */
diff --git a/arch/m68k/include/asm/local64.h b/arch/m68k/include/asm/local64.h
deleted file mode 100644
index 36c93b5..0000000
--- a/arch/m68k/include/asm/local64.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/local64.h>
diff --git a/arch/m68k/include/asm/mac_mouse.h b/arch/m68k/include/asm/mac_mouse.h
deleted file mode 100644
index 39a5c29..0000000
--- a/arch/m68k/include/asm/mac_mouse.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef _ASM_MAC_MOUSE_H
-#define _ASM_MAC_MOUSE_H
-
-/*
- * linux/include/asm-m68k/mac_mouse.h
- * header file for Macintosh ADB mouse driver
- * 27-10-97 Michael Schmitz
- * copied from:
- * header file for Atari Mouse driver
- * by Robert de Vries (robert@and.nl) on 19Jul93
- */
-
-struct mouse_status {
- char buttons;
- short dx;
- short dy;
- int ready;
- int active;
- wait_queue_head_t wait;
- struct fasync_struct *fasyncptr;
-};
-
-#endif
diff --git a/arch/m68k/include/asm/mcfmbus.h b/arch/m68k/include/asm/mcfmbus.h
deleted file mode 100644
index 319899c..0000000
--- a/arch/m68k/include/asm/mcfmbus.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************/
-
-/*
- * mcfmbus.h -- Coldfire MBUS support defines.
- *
- * (C) Copyright 1999, Martin Floeer (mfloeer@axcent.de)
- */
-
-/****************************************************************************/
-
-
-#ifndef mcfmbus_h
-#define mcfmbus_h
-
-
-#define MCFMBUS_BASE 0x280
-#define MCFMBUS_IRQ_VECTOR 0x19
-#define MCFMBUS_IRQ 0x1
-#define MCFMBUS_CLK 0x3f
-#define MCFMBUS_IRQ_LEVEL 0x07 /*IRQ Level 1*/
-#define MCFMBUS_ADDRESS 0x01
-
-
-/*
-* Define the 5307 MBUS register set addresses
-*/
-
-#define MCFMBUS_MADR 0x00
-#define MCFMBUS_MFDR 0x04
-#define MCFMBUS_MBCR 0x08
-#define MCFMBUS_MBSR 0x0C
-#define MCFMBUS_MBDR 0x10
-
-
-#define MCFMBUS_MADR_ADDR(a) (((a)&0x7F)<<0x01) /*Slave Address*/
-
-#define MCFMBUS_MFDR_MBC(a) ((a)&0x3F) /*M-Bus Clock*/
-
-/*
-* Define bit flags in Control Register
-*/
-
-#define MCFMBUS_MBCR_MEN (0x80) /* M-Bus Enable */
-#define MCFMBUS_MBCR_MIEN (0x40) /* M-Bus Interrupt Enable */
-#define MCFMBUS_MBCR_MSTA (0x20) /* Master/Slave Mode Select Bit */
-#define MCFMBUS_MBCR_MTX (0x10) /* Transmit/Rcv Mode Select Bit */
-#define MCFMBUS_MBCR_TXAK (0x08) /* Transmit Acknowledge Enable */
-#define MCFMBUS_MBCR_RSTA (0x04) /* Repeat Start */
-
-/*
-* Define bit flags in Status Register
-*/
-
-#define MCFMBUS_MBSR_MCF (0x80) /* Data Transfer Complete */
-#define MCFMBUS_MBSR_MAAS (0x40) /* Addressed as a Slave */
-#define MCFMBUS_MBSR_MBB (0x20) /* Bus Busy */
-#define MCFMBUS_MBSR_MAL (0x10) /* Arbitration Lost */
-#define MCFMBUS_MBSR_SRW (0x04) /* Slave Transmit */
-#define MCFMBUS_MBSR_MIF (0x02) /* M-Bus Interrupt */
-#define MCFMBUS_MBSR_RXAK (0x01) /* No Acknowledge Received */
-
-/*
-* Define bit flags in DATA I/O Register
-*/
-
-#define MCFMBUS_MBDR_READ (0x01) /* 1=read 0=write MBUS */
-
-#define MBUSIOCSCLOCK 1
-#define MBUSIOCGCLOCK 2
-#define MBUSIOCSADDR 3
-#define MBUSIOCGADDR 4
-#define MBUSIOCSSLADDR 5
-#define MBUSIOCGSLADDR 6
-#define MBUSIOCSSUBADDR 7
-#define MBUSIOCGSUBADDR 8
-
-#endif
diff --git a/arch/m68k/include/asm/mman.h b/arch/m68k/include/asm/mman.h
deleted file mode 100644
index 8eebf89f5..0000000
--- a/arch/m68k/include/asm/mman.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/mman.h>
diff --git a/arch/m68k/include/asm/mutex.h b/arch/m68k/include/asm/mutex.h
deleted file mode 100644
index 458c1f7..0000000
--- a/arch/m68k/include/asm/mutex.h
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * Pull in the generic implementation for the mutex fastpath.
- *
- * TODO: implement optimized primitives instead, or leave the generic
- * implementation in place, or pick the atomic_xchg() based generic
- * implementation. (see asm-generic/mutex-xchg.h for details)
- */
-
-#include <asm-generic/mutex-dec.h>
diff --git a/arch/m68k/include/asm/percpu.h b/arch/m68k/include/asm/percpu.h
deleted file mode 100644
index 0859d04..0000000
--- a/arch/m68k/include/asm/percpu.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef __ASM_M68K_PERCPU_H
-#define __ASM_M68K_PERCPU_H
-
-#include <asm-generic/percpu.h>
-
-#endif /* __ASM_M68K_PERCPU_H */
diff --git a/arch/m68k/include/asm/resource.h b/arch/m68k/include/asm/resource.h
deleted file mode 100644
index e7d3501..0000000
--- a/arch/m68k/include/asm/resource.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_RESOURCE_H
-#define _M68K_RESOURCE_H
-
-#include <asm-generic/resource.h>
-
-#endif /* _M68K_RESOURCE_H */
diff --git a/arch/m68k/include/asm/sbus.h b/arch/m68k/include/asm/sbus.h
deleted file mode 100644
index bfe3ba1..0000000
--- a/arch/m68k/include/asm/sbus.h
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * some sbus structures and macros to make usage of sbus drivers possible
- */
-
-#ifndef __M68K_SBUS_H
-#define __M68K_SBUS_H
-
-struct sbus_dev {
- struct {
- unsigned int which_io;
- unsigned int phys_addr;
- } reg_addrs[1];
-};
-
-/* sbus IO functions stolen from include/asm-sparc/io.h for the serial driver */
-/* No SBUS on the Sun3, kludge -- sam */
-
-static inline void _sbus_writeb(unsigned char val, unsigned long addr)
-{
- *(volatile unsigned char *)addr = val;
-}
-
-static inline unsigned char _sbus_readb(unsigned long addr)
-{
- return *(volatile unsigned char *)addr;
-}
-
-static inline void _sbus_writel(unsigned long val, unsigned long addr)
-{
- *(volatile unsigned long *)addr = val;
-
-}
-
-extern inline unsigned long _sbus_readl(unsigned long addr)
-{
- return *(volatile unsigned long *)addr;
-}
-
-
-#define sbus_readb(a) _sbus_readb((unsigned long)a)
-#define sbus_writeb(v, a) _sbus_writeb(v, (unsigned long)a)
-#define sbus_readl(a) _sbus_readl((unsigned long)a)
-#define sbus_writel(v, a) _sbus_writel(v, (unsigned long)a)
-
-#endif
diff --git a/arch/m68k/include/asm/scatterlist.h b/arch/m68k/include/asm/scatterlist.h
deleted file mode 100644
index 3125054..0000000
--- a/arch/m68k/include/asm/scatterlist.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_SCATTERLIST_H
-#define _M68K_SCATTERLIST_H
-
-#include <asm-generic/scatterlist.h>
-
-#endif /* !(_M68K_SCATTERLIST_H) */
diff --git a/arch/m68k/include/asm/sections.h b/arch/m68k/include/asm/sections.h
deleted file mode 100644
index 5277e52..0000000
--- a/arch/m68k/include/asm/sections.h
+++ /dev/null
@@ -1,8 +0,0 @@
-#ifndef _ASM_M68K_SECTIONS_H
-#define _ASM_M68K_SECTIONS_H
-
-#include <asm-generic/sections.h>
-
-extern char _sbss[], _ebss[];
-
-#endif /* _ASM_M68K_SECTIONS_H */
diff --git a/arch/m68k/include/asm/shm.h b/arch/m68k/include/asm/shm.h
deleted file mode 100644
index fa56ec8..0000000
--- a/arch/m68k/include/asm/shm.h
+++ /dev/null
@@ -1,31 +0,0 @@
-#ifndef _M68K_SHM_H
-#define _M68K_SHM_H
-
-
-/* format of page table entries that correspond to shared memory pages
- currently out in swap space (see also mm/swap.c):
- bits 0-1 (PAGE_PRESENT) is = 0
- bits 8..2 (SWP_TYPE) are = SHM_SWP_TYPE
- bits 31..9 are used like this:
- bits 15..9 (SHM_ID) the id of the shared memory segment
- bits 30..16 (SHM_IDX) the index of the page within the shared memory segment
- (actually only bits 25..16 get used since SHMMAX is so low)
- bit 31 (SHM_READ_ONLY) flag whether the page belongs to a read-only attach
-*/
-/* on the m68k both bits 0 and 1 must be zero */
-/* format on the sun3 is similar, but bits 30, 31 are set to zero and all
- others are reduced by 2. --m */
-
-#ifndef CONFIG_SUN3
-#define SHM_ID_SHIFT 9
-#else
-#define SHM_ID_SHIFT 7
-#endif
-#define _SHM_ID_BITS 7
-#define SHM_ID_MASK ((1<<_SHM_ID_BITS)-1)
-
-#define SHM_IDX_SHIFT (SHM_ID_SHIFT+_SHM_ID_BITS)
-#define _SHM_IDX_BITS 15
-#define SHM_IDX_MASK ((1<<_SHM_IDX_BITS)-1)
-
-#endif /* _M68K_SHM_H */
diff --git a/arch/m68k/include/asm/siginfo.h b/arch/m68k/include/asm/siginfo.h
deleted file mode 100644
index 851d3d7..0000000
--- a/arch/m68k/include/asm/siginfo.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_SIGINFO_H
-#define _M68K_SIGINFO_H
-
-#include <asm-generic/siginfo.h>
-
-#endif
diff --git a/arch/m68k/include/asm/statfs.h b/arch/m68k/include/asm/statfs.h
deleted file mode 100644
index 08d93f1..0000000
--- a/arch/m68k/include/asm/statfs.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _M68K_STATFS_H
-#define _M68K_STATFS_H
-
-#include <asm-generic/statfs.h>
-
-#endif /* _M68K_STATFS_H */
diff --git a/arch/m68k/include/asm/topology.h b/arch/m68k/include/asm/topology.h
deleted file mode 100644
index ca173e9..0000000
--- a/arch/m68k/include/asm/topology.h
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _ASM_M68K_TOPOLOGY_H
-#define _ASM_M68K_TOPOLOGY_H
-
-#include <asm-generic/topology.h>
-
-#endif /* _ASM_M68K_TOPOLOGY_H */
diff --git a/arch/m68k/include/asm/types.h b/arch/m68k/include/asm/types.h
deleted file mode 100644
index 89705ad..0000000
--- a/arch/m68k/include/asm/types.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef _M68K_TYPES_H
-#define _M68K_TYPES_H
-
-/*
- * This file is never included by application software unless
- * explicitly requested (e.g., via linux/types.h) in which case the
- * application is Linux specific so (user-) name space pollution is
- * not a major issue. However, for interoperability, libraries still
- * need to be careful to avoid a name clashes.
- */
-#include <asm-generic/int-ll64.h>
-
-/*
- * These aren't exported outside the kernel to avoid name space clashes
- */
-#ifdef __KERNEL__
-
-#define BITS_PER_LONG 32
-
-#endif /* __KERNEL__ */
-
-#endif /* _M68K_TYPES_H */
diff --git a/arch/m68k/include/asm/unaligned.h b/arch/m68k/include/asm/unaligned.h
index f4043ae..2b3ca0b 100644
--- a/arch/m68k/include/asm/unaligned.h
+++ b/arch/m68k/include/asm/unaligned.h
@@ -2,7 +2,7 @@
#define _ASM_M68K_UNALIGNED_H
-#if defined(CONFIG_COLDFIRE) || defined(CONFIG_M68000)
+#ifdef CONFIG_CPU_HAS_NO_UNALIGNED
#include <linux/unaligned/be_struct.h>
#include <linux/unaligned/le_byteshift.h>
#include <linux/unaligned/generic.h>
@@ -12,7 +12,7 @@
#else
/*
- * The m68k can do unaligned accesses itself.
+ * The m68k can do unaligned accesses itself.
*/
#include <linux/unaligned/access_ok.h>
#include <linux/unaligned/generic.h>
diff --git a/arch/m68k/include/asm/xor.h b/arch/m68k/include/asm/xor.h
deleted file mode 100644
index c82eb12..0000000
--- a/arch/m68k/include/asm/xor.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <asm-generic/xor.h>
diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c
index 7dc186b..71fb299 100644
--- a/arch/m68k/kernel/setup_no.c
+++ b/arch/m68k/kernel/setup_no.c
@@ -218,13 +218,10 @@
printk(KERN_INFO "Motorola M5235EVB support (C)2005 Syn-tech Systems, Inc. (Jate Sujjavanich)\n");
#endif
- pr_debug("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x "
- "BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext,
- (int) &_sdata, (int) &_edata,
- (int) &_sbss, (int) &_ebss);
- pr_debug("MEMORY -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x\n ",
- (int) &_ebss, (int) memory_start,
- (int) memory_start, (int) memory_end);
+ pr_debug("KERNEL -> TEXT=0x%p-0x%p DATA=0x%p-0x%p BSS=0x%p-0x%p\n",
+ _stext, _etext, _sdata, _edata, __bss_start, __bss_stop);
+ pr_debug("MEMORY -> ROMFS=0x%p-0x%06lx MEM=0x%06lx-0x%06lx\n ",
+ __bss_stop, memory_start, memory_start, memory_end);
/* Keep a copy of command line */
*cmdline_p = &command_line[0];
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c
index 8623f8d..9a5932e 100644
--- a/arch/m68k/kernel/sys_m68k.c
+++ b/arch/m68k/kernel/sys_m68k.c
@@ -479,9 +479,13 @@
goto bad_access;
}
- mem_value = *mem;
+ /*
+ * No need to check for EFAULT; we know that the page is
+ * present and writable.
+ */
+ __get_user(mem_value, mem);
if (mem_value == oldval)
- *mem = newval;
+ __put_user(newval, mem);
pte_unmap_unlock(pte, ptl);
up_read(&mm->mmap_sem);
diff --git a/arch/m68k/kernel/vmlinux-nommu.lds b/arch/m68k/kernel/vmlinux-nommu.lds
index 40e02d9..06a763f 100644
--- a/arch/m68k/kernel/vmlinux-nommu.lds
+++ b/arch/m68k/kernel/vmlinux-nommu.lds
@@ -78,9 +78,7 @@
__init_end = .;
}
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_end = .;
diff --git a/arch/m68k/kernel/vmlinux-std.lds b/arch/m68k/kernel/vmlinux-std.lds
index 63407c8..d099359 100644
--- a/arch/m68k/kernel/vmlinux-std.lds
+++ b/arch/m68k/kernel/vmlinux-std.lds
@@ -31,9 +31,7 @@
RW_DATA_SECTION(16, PAGE_SIZE, THREAD_SIZE)
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_edata = .; /* End of data section */
diff --git a/arch/m68k/kernel/vmlinux-sun3.lds b/arch/m68k/kernel/vmlinux-sun3.lds
index ad0f46d..8080469 100644
--- a/arch/m68k/kernel/vmlinux-sun3.lds
+++ b/arch/m68k/kernel/vmlinux-sun3.lds
@@ -44,9 +44,7 @@
. = ALIGN(PAGE_SIZE);
__init_end = .;
- _sbss = .;
BSS_SECTION(0, 0, 0)
- _ebss = .;
_end = . ;
diff --git a/arch/m68k/lib/muldi3.c b/arch/m68k/lib/muldi3.c
index 79e928a..ee5f0b1 100644
--- a/arch/m68k/lib/muldi3.c
+++ b/arch/m68k/lib/muldi3.c
@@ -19,7 +19,7 @@
the Free Software Foundation, 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA. */
-#if defined(CONFIG_M68000) || defined(CONFIG_COLDFIRE)
+#ifdef CONFIG_CPU_HAS_NO_MULDIV64
#define SI_TYPE_SIZE 32
#define __BITS4 (SI_TYPE_SIZE / 4)
diff --git a/arch/m68k/mm/init_mm.c b/arch/m68k/mm/init_mm.c
index f77f258..282f9de 100644
--- a/arch/m68k/mm/init_mm.c
+++ b/arch/m68k/mm/init_mm.c
@@ -104,7 +104,7 @@
MLK_ROUNDUP(__init_begin, __init_end),
MLK_ROUNDUP(_stext, _etext),
MLK_ROUNDUP(_sdata, _edata),
- MLK_ROUNDUP(_sbss, _ebss));
+ MLK_ROUNDUP(__bss_start, __bss_stop));
}
void __init mem_init(void)
diff --git a/arch/m68k/mm/init_no.c b/arch/m68k/mm/init_no.c
index 345ec0d..688e366 100644
--- a/arch/m68k/mm/init_no.c
+++ b/arch/m68k/mm/init_no.c
@@ -91,7 +91,7 @@
totalram_pages = free_all_bootmem();
codek = (_etext - _stext) >> 10;
- datak = (_ebss - _sdata) >> 10;
+ datak = (__bss_stop - _sdata) >> 10;
initk = (__init_begin - __init_end) >> 10;
tmp = nr_free_pages() << PAGE_SHIFT;
diff --git a/arch/m68k/platform/68328/head-de2.S b/arch/m68k/platform/68328/head-de2.S
index f632fdc..537d324 100644
--- a/arch/m68k/platform/68328/head-de2.S
+++ b/arch/m68k/platform/68328/head-de2.S
@@ -60,8 +60,8 @@
* Move ROM filesystem above bss :-)
*/
- moveal #_sbss, %a0 /* romfs at the start of bss */
- moveal #_ebss, %a1 /* Set up destination */
+ moveal #__bss_start, %a0 /* romfs at the start of bss */
+ moveal #__bss_stop, %a1 /* Set up destination */
movel %a0, %a2 /* Copy of bss start */
movel 8(%a0), %d1 /* Get size of ROMFS */
@@ -84,8 +84,8 @@
* Initialize BSS segment to 0
*/
- lea _sbss, %a0
- lea _ebss, %a1
+ lea __bss_start, %a0
+ lea __bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
2: cmpal %a0, %a1
diff --git a/arch/m68k/platform/68328/head-pilot.S b/arch/m68k/platform/68328/head-pilot.S
index 2ebfd64..45a9dad 100644
--- a/arch/m68k/platform/68328/head-pilot.S
+++ b/arch/m68k/platform/68328/head-pilot.S
@@ -110,7 +110,7 @@
movel #CONFIG_VECTORBASE, %d7
addl #16, %d7
moveal %d7, %a0
- moveal #_ebss, %a1
+ moveal #__bss_stop, %a1
lea %a1@(512), %a2
DBG_PUTC('C')
@@ -138,8 +138,8 @@
DBG_PUTC('E')
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -150,7 +150,7 @@
DBG_PUTC('F')
/* Copy command line from end of bss to command line */
- moveal #_ebss, %a0
+ moveal #__bss_stop, %a0
moveal #command_line, %a1
lea %a1@(512), %a2
@@ -165,7 +165,7 @@
movel #_sdata, %d0
movel %d0, _rambase
- movel #_ebss, %d0
+ movel #__bss_stop, %d0
movel %d0, _ramstart
movel %a4, %d0
diff --git a/arch/m68k/platform/68328/head-ram.S b/arch/m68k/platform/68328/head-ram.S
index 7f1aeea..5189ef9 100644
--- a/arch/m68k/platform/68328/head-ram.S
+++ b/arch/m68k/platform/68328/head-ram.S
@@ -76,8 +76,8 @@
beq pclp3
#endif /* DEBUG */
moveal #0x007ffff0, %ssp
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 >= %a1 */
L1:
diff --git a/arch/m68k/platform/68328/head-rom.S b/arch/m68k/platform/68328/head-rom.S
index a5ff96d..3dff98b 100644
--- a/arch/m68k/platform/68328/head-rom.S
+++ b/arch/m68k/platform/68328/head-rom.S
@@ -59,8 +59,8 @@
cmpal %a1, %a2
bhi 1b
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
1:
@@ -70,7 +70,7 @@
movel #_sdata, %d0
movel %d0, _rambase
- movel #_ebss, %d0
+ movel #__bss_stop, %d0
movel %d0, _ramstart
movel #RAMEND-CONFIG_MEMORY_RESERVE*0x100000, %d0
movel %d0, _ramend
diff --git a/arch/m68k/platform/68360/head-ram.S b/arch/m68k/platform/68360/head-ram.S
index 8eb94fb..acd2131 100644
--- a/arch/m68k/platform/68360/head-ram.S
+++ b/arch/m68k/platform/68360/head-ram.S
@@ -219,8 +219,8 @@
cmp.l #_edata, %a1
blt LD1
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -234,7 +234,7 @@
store_ram_size:
/* Set ram size information */
move.l #_sdata, _rambase
- move.l #_ebss, _ramstart
+ move.l #__bss_stop, _ramstart
move.l #RAMEND, %d0
sub.l #0x1000, %d0 /* Reserve 4K for stack space.*/
move.l %d0, _ramend /* Different from RAMEND.*/
diff --git a/arch/m68k/platform/68360/head-rom.S b/arch/m68k/platform/68360/head-rom.S
index 97510e5..dfc756d 100644
--- a/arch/m68k/platform/68360/head-rom.S
+++ b/arch/m68k/platform/68360/head-rom.S
@@ -13,7 +13,7 @@
*/
.global _stext
-.global _sbss
+.global __bss_start
.global _start
.global _rambase
@@ -229,8 +229,8 @@
cmp.l #_edata, %a1
blt LD1
- moveal #_sbss, %a0
- moveal #_ebss, %a1
+ moveal #__bss_start, %a0
+ moveal #__bss_stop, %a1
/* Copy 0 to %a0 until %a0 == %a1 */
L1:
@@ -244,7 +244,7 @@
store_ram_size:
/* Set ram size information */
move.l #_sdata, _rambase
- move.l #_ebss, _ramstart
+ move.l #__bss_stop, _ramstart
move.l #RAMEND, %d0
sub.l #0x1000, %d0 /* Reserve 4K for stack space.*/
move.l %d0, _ramend /* Different from RAMEND.*/
diff --git a/arch/m68k/platform/coldfire/head.S b/arch/m68k/platform/coldfire/head.S
index 4e0c9eb..b88f571 100644
--- a/arch/m68k/platform/coldfire/head.S
+++ b/arch/m68k/platform/coldfire/head.S
@@ -230,8 +230,8 @@
/*
* Move ROM filesystem above bss :-)
*/
- lea _sbss,%a0 /* get start of bss */
- lea _ebss,%a1 /* set up destination */
+ lea __bss_start,%a0 /* get start of bss */
+ lea __bss_stop,%a1 /* set up destination */
movel %a0,%a2 /* copy of bss start */
movel 8(%a0),%d0 /* get size of ROMFS */
@@ -249,7 +249,7 @@
bne _copy_romfs
#else /* CONFIG_ROMFS_FS */
- lea _ebss,%a1
+ lea __bss_stop,%a1
movel %a1,_ramstart
#endif /* CONFIG_ROMFS_FS */
@@ -257,8 +257,8 @@
/*
* Zero out the bss region.
*/
- lea _sbss,%a0 /* get start of bss */
- lea _ebss,%a1 /* get end of bss */
+ lea __bss_start,%a0 /* get start of bss */
+ lea __bss_stop,%a1 /* get end of bss */
clrl %d0 /* set value */
_clear_bss:
movel %d0,(%a0)+ /* clear each word */
diff --git a/arch/m68k/sun3/prom/init.c b/arch/m68k/sun3/prom/init.c
index d8e6349..eeba067 100644
--- a/arch/m68k/sun3/prom/init.c
+++ b/arch/m68k/sun3/prom/init.c
@@ -22,57 +22,13 @@
struct linux_nodeops *prom_nodeops;
/* You must call prom_init() before you attempt to use any of the
- * routines in the prom library. It returns 0 on success, 1 on
- * failure. It gets passed the pointer to the PROM vector.
+ * routines in the prom library.
+ * It gets passed the pointer to the PROM vector.
*/
-extern void prom_meminit(void);
-extern void prom_ranges_init(void);
-
void __init prom_init(struct linux_romvec *rp)
{
romvec = rp;
-#ifndef CONFIG_SUN3
- switch(romvec->pv_romvers) {
- case 0:
- prom_vers = PROM_V0;
- break;
- case 2:
- prom_vers = PROM_V2;
- break;
- case 3:
- prom_vers = PROM_V3;
- break;
- case 4:
- prom_vers = PROM_P1275;
- prom_printf("PROMLIB: Sun IEEE Prom not supported yet\n");
- prom_halt();
- break;
- default:
- prom_printf("PROMLIB: Bad PROM version %d\n",
- romvec->pv_romvers);
- prom_halt();
- break;
- };
-
- prom_rev = romvec->pv_plugin_revision;
- prom_prev = romvec->pv_printrev;
- prom_nodeops = romvec->pv_nodeops;
-
- prom_root_node = prom_getsibling(0);
- if((prom_root_node == 0) || (prom_root_node == -1))
- prom_halt();
-
- if((((unsigned long) prom_nodeops) == 0) ||
- (((unsigned long) prom_nodeops) == -1))
- prom_halt();
-
- prom_meminit();
-
- prom_ranges_init();
-#endif
-// printk("PROMLIB: Sun Boot Prom Version %d Revision %d\n",
-// romvec->pv_romvers, prom_rev);
/* Initialization successful. */
return;
diff --git a/arch/microblaze/include/asm/sections.h b/arch/microblaze/include/asm/sections.h
index 4487e15..c07ed5d 100644
--- a/arch/microblaze/include/asm/sections.h
+++ b/arch/microblaze/include/asm/sections.h
@@ -18,10 +18,6 @@
extern unsigned long __ivt_start[], __ivt_end[];
extern char _etext[], _stext[];
-# ifdef CONFIG_MTD_UCLINUX
-extern char *_ebss;
-# endif
-
extern u32 _fdt_start[], _fdt_end[];
# endif /* !__ASSEMBLY__ */
diff --git a/arch/microblaze/kernel/microblaze_ksyms.c b/arch/microblaze/kernel/microblaze_ksyms.c
index bb4907c..2b25bcf 100644
--- a/arch/microblaze/kernel/microblaze_ksyms.c
+++ b/arch/microblaze/kernel/microblaze_ksyms.c
@@ -21,9 +21,6 @@
#include <linux/ftrace.h>
#include <linux/uaccess.h>
-extern char *_ebss;
-EXPORT_SYMBOL_GPL(_ebss);
-
#ifdef CONFIG_FUNCTION_TRACER
extern void _mcount(void);
EXPORT_SYMBOL(_mcount);
diff --git a/arch/microblaze/kernel/setup.c b/arch/microblaze/kernel/setup.c
index 16d8dfd..4da971d 100644
--- a/arch/microblaze/kernel/setup.c
+++ b/arch/microblaze/kernel/setup.c
@@ -121,7 +121,7 @@
/* Move ROMFS out of BSS before clearing it */
if (romfs_size > 0) {
- memmove(&_ebss, (int *)romfs_base, romfs_size);
+ memmove(&__bss_stop, (int *)romfs_base, romfs_size);
klimit += romfs_size;
}
#endif
@@ -165,7 +165,7 @@
BUG_ON(romfs_size < 0); /* What else can we do? */
printk("Moved 0x%08x bytes from 0x%08x to 0x%08x\n",
- romfs_size, romfs_base, (unsigned)&_ebss);
+ romfs_size, romfs_base, (unsigned)&__bss_stop);
printk("New klimit: 0x%08x\n", (unsigned)klimit);
#endif
diff --git a/arch/microblaze/kernel/vmlinux.lds.S b/arch/microblaze/kernel/vmlinux.lds.S
index 109e9d8..936d01a 100644
--- a/arch/microblaze/kernel/vmlinux.lds.S
+++ b/arch/microblaze/kernel/vmlinux.lds.S
@@ -131,7 +131,6 @@
*(COMMON)
. = ALIGN (4) ;
__bss_stop = . ;
- _ebss = . ;
}
. = ALIGN(PAGE_SIZE);
_end = .;
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 76de6b6..107610e 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -124,6 +124,7 @@
select GENERIC_TIME_VSYSCALL
select GENERIC_CLOCKEVENTS
select KTIME_SCALAR if 32BIT
+ select HAVE_ARCH_SECCOMP_FILTER
config SCHED_OMIT_FRAME_POINTER
def_bool y
diff --git a/arch/s390/include/asm/sparsemem.h b/arch/s390/include/asm/sparsemem.h
index 0fb3402..a60d085 100644
--- a/arch/s390/include/asm/sparsemem.h
+++ b/arch/s390/include/asm/sparsemem.h
@@ -4,13 +4,11 @@
#ifdef CONFIG_64BIT
#define SECTION_SIZE_BITS 28
-#define MAX_PHYSADDR_BITS 46
#define MAX_PHYSMEM_BITS 46
#else
#define SECTION_SIZE_BITS 25
-#define MAX_PHYSADDR_BITS 31
#define MAX_PHYSMEM_BITS 31
#endif /* CONFIG_64BIT */
diff --git a/arch/s390/include/asm/syscall.h b/arch/s390/include/asm/syscall.h
index fb214dd..fe7b997 100644
--- a/arch/s390/include/asm/syscall.h
+++ b/arch/s390/include/asm/syscall.h
@@ -12,6 +12,7 @@
#ifndef _ASM_SYSCALL_H
#define _ASM_SYSCALL_H 1
+#include <linux/audit.h>
#include <linux/sched.h>
#include <linux/err.h>
#include <asm/ptrace.h>
@@ -87,4 +88,13 @@
regs->orig_gpr2 = args[0];
}
+static inline int syscall_get_arch(struct task_struct *task,
+ struct pt_regs *regs)
+{
+#ifdef CONFIG_COMPAT
+ if (test_tsk_thread_flag(task, TIF_31BIT))
+ return AUDIT_ARCH_S390;
+#endif
+ return sizeof(long) == 8 ? AUDIT_ARCH_S390X : AUDIT_ARCH_S390;
+}
#endif /* _ASM_SYSCALL_H */
diff --git a/arch/s390/kernel/compat_linux.c b/arch/s390/kernel/compat_linux.c
index d122508..f606d93 100644
--- a/arch/s390/kernel/compat_linux.c
+++ b/arch/s390/kernel/compat_linux.c
@@ -620,7 +620,6 @@
return -EFAULT;
if (a.offset & ~PAGE_MASK)
return -EINVAL;
- a.addr = (unsigned long) compat_ptr(a.addr);
return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
a.offset >> PAGE_SHIFT);
}
@@ -631,7 +630,6 @@
if (copy_from_user(&a, arg, sizeof(a)))
return -EFAULT;
- a.addr = (unsigned long) compat_ptr(a.addr);
return sys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd, a.offset);
}
diff --git a/arch/s390/kernel/compat_wrapper.S b/arch/s390/kernel/compat_wrapper.S
index e835d6d..2d82cfc 100644
--- a/arch/s390/kernel/compat_wrapper.S
+++ b/arch/s390/kernel/compat_wrapper.S
@@ -1635,7 +1635,7 @@
llgfr %r6,%r6 # unsigned long
llgf %r0,164(%r15) # unsigned long
stg %r0,160(%r15)
- jg sys_process_vm_readv
+ jg compat_sys_process_vm_readv
ENTRY(compat_sys_process_vm_writev_wrapper)
lgfr %r2,%r2 # compat_pid_t
@@ -1645,4 +1645,4 @@
llgfr %r6,%r6 # unsigned long
llgf %r0,164(%r15) # unsigned long
stg %r0,160(%r15)
- jg sys_process_vm_writev
+ jg compat_sys_process_vm_writev
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index f4eb376..e4be113 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -719,7 +719,11 @@
long ret = 0;
/* Do the secure computing check first. */
- secure_computing_strict(regs->gprs[2]);
+ if (secure_computing(regs->gprs[2])) {
+ /* seccomp failures shouldn't expose any additional code. */
+ ret = -1;
+ goto out;
+ }
/*
* The sysc_tracesys code in entry.S stored the system
@@ -745,6 +749,7 @@
regs->gprs[2], regs->orig_gpr2,
regs->gprs[3], regs->gprs[4],
regs->gprs[5]);
+out:
return ret ?: regs->gprs[2];
}
diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c
index b4a29ee..d0964d2 100644
--- a/arch/s390/kernel/sys_s390.c
+++ b/arch/s390/kernel/sys_s390.c
@@ -81,11 +81,12 @@
{
unsigned int ret;
- if (current->personality == PER_LINUX32 && personality == PER_LINUX)
- personality = PER_LINUX32;
+ if (personality(current->personality) == PER_LINUX32 &&
+ personality(personality) == PER_LINUX)
+ personality |= PER_LINUX32;
ret = sys_personality(personality);
- if (ret == PER_LINUX32)
- ret = PER_LINUX;
+ if (personality(ret) == PER_LINUX32)
+ ret &= ~PER_LINUX32;
return ret;
}
diff --git a/arch/sh/drivers/dma/dma-sh.c b/arch/sh/drivers/dma/dma-sh.c
index 4c171f1..b225656 100644
--- a/arch/sh/drivers/dma/dma-sh.c
+++ b/arch/sh/drivers/dma/dma-sh.c
@@ -335,7 +335,7 @@
for (n = 0; n < NR_DMAE; n++) {
int i = request_irq(get_dma_error_irq(n), dma_err,
- IRQF_SHARED, dmae_name[n], NULL);
+ IRQF_SHARED, dmae_name[n], (void *)dmae_name[n]);
if (unlikely(i < 0)) {
printk(KERN_ERR "%s request_irq fail\n", dmae_name[n]);
return i;
diff --git a/arch/sh/include/asm/sections.h b/arch/sh/include/asm/sections.h
index 4a53500..1b61997 100644
--- a/arch/sh/include/asm/sections.h
+++ b/arch/sh/include/asm/sections.h
@@ -6,7 +6,6 @@
extern long __nosave_begin, __nosave_end;
extern long __machvec_start, __machvec_end;
extern char __uncached_start, __uncached_end;
-extern char _ebss[];
extern char __start_eh_frame[], __stop_eh_frame[];
#endif /* __ASM_SH_SECTIONS_H */
diff --git a/arch/sh/include/cpu-sh2a/cpu/sh7269.h b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
index 48d1449..2a0ca87 100644
--- a/arch/sh/include/cpu-sh2a/cpu/sh7269.h
+++ b/arch/sh/include/cpu-sh2a/cpu/sh7269.h
@@ -183,18 +183,30 @@
GPIO_FN_DV_DATA1, GPIO_FN_DV_DATA0,
GPIO_FN_LCD_CLK, GPIO_FN_LCD_EXTCLK,
GPIO_FN_LCD_VSYNC, GPIO_FN_LCD_HSYNC, GPIO_FN_LCD_DE,
- GPIO_FN_LCD_DATA23, GPIO_FN_LCD_DATA22,
- GPIO_FN_LCD_DATA21, GPIO_FN_LCD_DATA20,
- GPIO_FN_LCD_DATA19, GPIO_FN_LCD_DATA18,
- GPIO_FN_LCD_DATA17, GPIO_FN_LCD_DATA16,
- GPIO_FN_LCD_DATA15, GPIO_FN_LCD_DATA14,
- GPIO_FN_LCD_DATA13, GPIO_FN_LCD_DATA12,
- GPIO_FN_LCD_DATA11, GPIO_FN_LCD_DATA10,
- GPIO_FN_LCD_DATA9, GPIO_FN_LCD_DATA8,
- GPIO_FN_LCD_DATA7, GPIO_FN_LCD_DATA6,
- GPIO_FN_LCD_DATA5, GPIO_FN_LCD_DATA4,
- GPIO_FN_LCD_DATA3, GPIO_FN_LCD_DATA2,
- GPIO_FN_LCD_DATA1, GPIO_FN_LCD_DATA0,
+ GPIO_FN_LCD_DATA23_PG23, GPIO_FN_LCD_DATA22_PG22,
+ GPIO_FN_LCD_DATA21_PG21, GPIO_FN_LCD_DATA20_PG20,
+ GPIO_FN_LCD_DATA19_PG19, GPIO_FN_LCD_DATA18_PG18,
+ GPIO_FN_LCD_DATA17_PG17, GPIO_FN_LCD_DATA16_PG16,
+ GPIO_FN_LCD_DATA15_PG15, GPIO_FN_LCD_DATA14_PG14,
+ GPIO_FN_LCD_DATA13_PG13, GPIO_FN_LCD_DATA12_PG12,
+ GPIO_FN_LCD_DATA11_PG11, GPIO_FN_LCD_DATA10_PG10,
+ GPIO_FN_LCD_DATA9_PG9, GPIO_FN_LCD_DATA8_PG8,
+ GPIO_FN_LCD_DATA7_PG7, GPIO_FN_LCD_DATA6_PG6,
+ GPIO_FN_LCD_DATA5_PG5, GPIO_FN_LCD_DATA4_PG4,
+ GPIO_FN_LCD_DATA3_PG3, GPIO_FN_LCD_DATA2_PG2,
+ GPIO_FN_LCD_DATA1_PG1, GPIO_FN_LCD_DATA0_PG0,
+ GPIO_FN_LCD_DATA23_PJ23, GPIO_FN_LCD_DATA22_PJ22,
+ GPIO_FN_LCD_DATA21_PJ21, GPIO_FN_LCD_DATA20_PJ20,
+ GPIO_FN_LCD_DATA19_PJ19, GPIO_FN_LCD_DATA18_PJ18,
+ GPIO_FN_LCD_DATA17_PJ17, GPIO_FN_LCD_DATA16_PJ16,
+ GPIO_FN_LCD_DATA15_PJ15, GPIO_FN_LCD_DATA14_PJ14,
+ GPIO_FN_LCD_DATA13_PJ13, GPIO_FN_LCD_DATA12_PJ12,
+ GPIO_FN_LCD_DATA11_PJ11, GPIO_FN_LCD_DATA10_PJ10,
+ GPIO_FN_LCD_DATA9_PJ9, GPIO_FN_LCD_DATA8_PJ8,
+ GPIO_FN_LCD_DATA7_PJ7, GPIO_FN_LCD_DATA6_PJ6,
+ GPIO_FN_LCD_DATA5_PJ5, GPIO_FN_LCD_DATA4_PJ4,
+ GPIO_FN_LCD_DATA3_PJ3, GPIO_FN_LCD_DATA2_PJ2,
+ GPIO_FN_LCD_DATA1_PJ1, GPIO_FN_LCD_DATA0_PJ0,
GPIO_FN_LCD_M_DISP,
};
diff --git a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
index f25127c..039e458 100644
--- a/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
+++ b/arch/sh/kernel/cpu/sh2a/pinmux-sh7269.c
@@ -758,12 +758,22 @@
DV_DATA3_MARK, DV_DATA2_MARK, DV_DATA1_MARK, DV_DATA0_MARK,
LCD_CLK_MARK, LCD_EXTCLK_MARK,
LCD_VSYNC_MARK, LCD_HSYNC_MARK, LCD_DE_MARK,
- LCD_DATA23_MARK, LCD_DATA22_MARK, LCD_DATA21_MARK, LCD_DATA20_MARK,
- LCD_DATA19_MARK, LCD_DATA18_MARK, LCD_DATA17_MARK, LCD_DATA16_MARK,
- LCD_DATA15_MARK, LCD_DATA14_MARK, LCD_DATA13_MARK, LCD_DATA12_MARK,
- LCD_DATA11_MARK, LCD_DATA10_MARK, LCD_DATA9_MARK, LCD_DATA8_MARK,
- LCD_DATA7_MARK, LCD_DATA6_MARK, LCD_DATA5_MARK, LCD_DATA4_MARK,
- LCD_DATA3_MARK, LCD_DATA2_MARK, LCD_DATA1_MARK, LCD_DATA0_MARK,
+ LCD_DATA23_PG23_MARK, LCD_DATA22_PG22_MARK, LCD_DATA21_PG21_MARK,
+ LCD_DATA20_PG20_MARK, LCD_DATA19_PG19_MARK, LCD_DATA18_PG18_MARK,
+ LCD_DATA17_PG17_MARK, LCD_DATA16_PG16_MARK, LCD_DATA15_PG15_MARK,
+ LCD_DATA14_PG14_MARK, LCD_DATA13_PG13_MARK, LCD_DATA12_PG12_MARK,
+ LCD_DATA11_PG11_MARK, LCD_DATA10_PG10_MARK, LCD_DATA9_PG9_MARK,
+ LCD_DATA8_PG8_MARK, LCD_DATA7_PG7_MARK, LCD_DATA6_PG6_MARK,
+ LCD_DATA5_PG5_MARK, LCD_DATA4_PG4_MARK, LCD_DATA3_PG3_MARK,
+ LCD_DATA2_PG2_MARK, LCD_DATA1_PG1_MARK, LCD_DATA0_PG0_MARK,
+ LCD_DATA23_PJ23_MARK, LCD_DATA22_PJ22_MARK, LCD_DATA21_PJ21_MARK,
+ LCD_DATA20_PJ20_MARK, LCD_DATA19_PJ19_MARK, LCD_DATA18_PJ18_MARK,
+ LCD_DATA17_PJ17_MARK, LCD_DATA16_PJ16_MARK, LCD_DATA15_PJ15_MARK,
+ LCD_DATA14_PJ14_MARK, LCD_DATA13_PJ13_MARK, LCD_DATA12_PJ12_MARK,
+ LCD_DATA11_PJ11_MARK, LCD_DATA10_PJ10_MARK, LCD_DATA9_PJ9_MARK,
+ LCD_DATA8_PJ8_MARK, LCD_DATA7_PJ7_MARK, LCD_DATA6_PJ6_MARK,
+ LCD_DATA5_PJ5_MARK, LCD_DATA4_PJ4_MARK, LCD_DATA3_PJ3_MARK,
+ LCD_DATA2_PJ2_MARK, LCD_DATA1_PJ1_MARK, LCD_DATA0_PJ0_MARK,
LCD_TCON6_MARK, LCD_TCON5_MARK, LCD_TCON4_MARK,
LCD_TCON3_MARK, LCD_TCON2_MARK, LCD_TCON1_MARK, LCD_TCON0_MARK,
LCD_M_DISP_MARK,
@@ -1036,6 +1046,7 @@
PINMUX_DATA(PF1_DATA, PF1MD_000),
PINMUX_DATA(BACK_MARK, PF1MD_001),
+ PINMUX_DATA(SSL10_MARK, PF1MD_011),
PINMUX_DATA(TIOC4B_MARK, PF1MD_100),
PINMUX_DATA(DACK0_MARK, PF1MD_101),
@@ -1049,47 +1060,50 @@
PINMUX_DATA(PG27_DATA, PG27MD_00),
PINMUX_DATA(LCD_TCON2_MARK, PG27MD_10),
PINMUX_DATA(LCD_EXTCLK_MARK, PG27MD_11),
+ PINMUX_DATA(LCD_DE_MARK, PG27MD_11),
PINMUX_DATA(PG26_DATA, PG26MD_00),
PINMUX_DATA(LCD_TCON1_MARK, PG26MD_10),
+ PINMUX_DATA(LCD_HSYNC_MARK, PG26MD_10),
PINMUX_DATA(PG25_DATA, PG25MD_00),
PINMUX_DATA(LCD_TCON0_MARK, PG25MD_10),
+ PINMUX_DATA(LCD_VSYNC_MARK, PG25MD_10),
PINMUX_DATA(PG24_DATA, PG24MD_00),
PINMUX_DATA(LCD_CLK_MARK, PG24MD_10),
PINMUX_DATA(PG23_DATA, PG23MD_000),
- PINMUX_DATA(LCD_DATA23_MARK, PG23MD_010),
+ PINMUX_DATA(LCD_DATA23_PG23_MARK, PG23MD_010),
PINMUX_DATA(LCD_TCON6_MARK, PG23MD_011),
PINMUX_DATA(TXD5_MARK, PG23MD_100),
PINMUX_DATA(PG22_DATA, PG22MD_000),
- PINMUX_DATA(LCD_DATA22_MARK, PG22MD_010),
+ PINMUX_DATA(LCD_DATA22_PG22_MARK, PG22MD_010),
PINMUX_DATA(LCD_TCON5_MARK, PG22MD_011),
PINMUX_DATA(RXD5_MARK, PG22MD_100),
PINMUX_DATA(PG21_DATA, PG21MD_000),
PINMUX_DATA(DV_DATA7_MARK, PG21MD_001),
- PINMUX_DATA(LCD_DATA21_MARK, PG21MD_010),
+ PINMUX_DATA(LCD_DATA21_PG21_MARK, PG21MD_010),
PINMUX_DATA(LCD_TCON4_MARK, PG21MD_011),
PINMUX_DATA(TXD4_MARK, PG21MD_100),
PINMUX_DATA(PG20_DATA, PG20MD_000),
PINMUX_DATA(DV_DATA6_MARK, PG20MD_001),
- PINMUX_DATA(LCD_DATA20_MARK, PG21MD_010),
+ PINMUX_DATA(LCD_DATA20_PG20_MARK, PG21MD_010),
PINMUX_DATA(LCD_TCON3_MARK, PG20MD_011),
PINMUX_DATA(RXD4_MARK, PG20MD_100),
PINMUX_DATA(PG19_DATA, PG19MD_000),
PINMUX_DATA(DV_DATA5_MARK, PG19MD_001),
- PINMUX_DATA(LCD_DATA19_MARK, PG19MD_010),
+ PINMUX_DATA(LCD_DATA19_PG19_MARK, PG19MD_010),
PINMUX_DATA(SPDIF_OUT_MARK, PG19MD_011),
PINMUX_DATA(SCK5_MARK, PG19MD_100),
PINMUX_DATA(PG18_DATA, PG18MD_000),
PINMUX_DATA(DV_DATA4_MARK, PG18MD_001),
- PINMUX_DATA(LCD_DATA18_MARK, PG18MD_010),
+ PINMUX_DATA(LCD_DATA18_PG18_MARK, PG18MD_010),
PINMUX_DATA(SPDIF_IN_MARK, PG18MD_011),
PINMUX_DATA(SCK4_MARK, PG18MD_100),
@@ -1097,103 +1111,103 @@
// we're going with 2 bits
PINMUX_DATA(PG17_DATA, PG17MD_00),
PINMUX_DATA(WE3ICIOWRAHDQMUU_MARK, PG17MD_01),
- PINMUX_DATA(LCD_DATA17_MARK, PG17MD_10),
+ PINMUX_DATA(LCD_DATA17_PG17_MARK, PG17MD_10),
// TODO hardware manual has PG16 3 bits wide in reg picture and 2 bits in description
// we're going with 2 bits
PINMUX_DATA(PG16_DATA, PG16MD_00),
PINMUX_DATA(WE2ICIORDDQMUL_MARK, PG16MD_01),
- PINMUX_DATA(LCD_DATA16_MARK, PG16MD_10),
+ PINMUX_DATA(LCD_DATA16_PG16_MARK, PG16MD_10),
PINMUX_DATA(PG15_DATA, PG15MD_00),
PINMUX_DATA(D31_MARK, PG15MD_01),
- PINMUX_DATA(LCD_DATA15_MARK, PG15MD_10),
+ PINMUX_DATA(LCD_DATA15_PG15_MARK, PG15MD_10),
PINMUX_DATA(PINT7_PG_MARK, PG15MD_11),
PINMUX_DATA(PG14_DATA, PG14MD_00),
PINMUX_DATA(D30_MARK, PG14MD_01),
- PINMUX_DATA(LCD_DATA14_MARK, PG14MD_10),
+ PINMUX_DATA(LCD_DATA14_PG14_MARK, PG14MD_10),
PINMUX_DATA(PINT6_PG_MARK, PG14MD_11),
PINMUX_DATA(PG13_DATA, PG13MD_00),
PINMUX_DATA(D29_MARK, PG13MD_01),
- PINMUX_DATA(LCD_DATA13_MARK, PG13MD_10),
+ PINMUX_DATA(LCD_DATA13_PG13_MARK, PG13MD_10),
PINMUX_DATA(PINT5_PG_MARK, PG13MD_11),
PINMUX_DATA(PG12_DATA, PG12MD_00),
PINMUX_DATA(D28_MARK, PG12MD_01),
- PINMUX_DATA(LCD_DATA12_MARK, PG12MD_10),
+ PINMUX_DATA(LCD_DATA12_PG12_MARK, PG12MD_10),
PINMUX_DATA(PINT4_PG_MARK, PG12MD_11),
PINMUX_DATA(PG11_DATA, PG11MD_000),
PINMUX_DATA(D27_MARK, PG11MD_001),
- PINMUX_DATA(LCD_DATA11_MARK, PG11MD_010),
+ PINMUX_DATA(LCD_DATA11_PG11_MARK, PG11MD_010),
PINMUX_DATA(PINT3_PG_MARK, PG11MD_011),
PINMUX_DATA(TIOC3D_MARK, PG11MD_100),
PINMUX_DATA(PG10_DATA, PG10MD_000),
PINMUX_DATA(D26_MARK, PG10MD_001),
- PINMUX_DATA(LCD_DATA10_MARK, PG10MD_010),
+ PINMUX_DATA(LCD_DATA10_PG10_MARK, PG10MD_010),
PINMUX_DATA(PINT2_PG_MARK, PG10MD_011),
PINMUX_DATA(TIOC3C_MARK, PG10MD_100),
PINMUX_DATA(PG9_DATA, PG9MD_000),
PINMUX_DATA(D25_MARK, PG9MD_001),
- PINMUX_DATA(LCD_DATA9_MARK, PG9MD_010),
+ PINMUX_DATA(LCD_DATA9_PG9_MARK, PG9MD_010),
PINMUX_DATA(PINT1_PG_MARK, PG9MD_011),
PINMUX_DATA(TIOC3B_MARK, PG9MD_100),
PINMUX_DATA(PG8_DATA, PG8MD_000),
PINMUX_DATA(D24_MARK, PG8MD_001),
- PINMUX_DATA(LCD_DATA8_MARK, PG8MD_010),
+ PINMUX_DATA(LCD_DATA8_PG8_MARK, PG8MD_010),
PINMUX_DATA(PINT0_PG_MARK, PG8MD_011),
PINMUX_DATA(TIOC3A_MARK, PG8MD_100),
PINMUX_DATA(PG7_DATA, PG7MD_000),
PINMUX_DATA(D23_MARK, PG7MD_001),
- PINMUX_DATA(LCD_DATA7_MARK, PG7MD_010),
+ PINMUX_DATA(LCD_DATA7_PG7_MARK, PG7MD_010),
PINMUX_DATA(IRQ7_PG_MARK, PG7MD_011),
PINMUX_DATA(TIOC2B_MARK, PG7MD_100),
PINMUX_DATA(PG6_DATA, PG6MD_000),
PINMUX_DATA(D22_MARK, PG6MD_001),
- PINMUX_DATA(LCD_DATA6_MARK, PG6MD_010),
+ PINMUX_DATA(LCD_DATA6_PG6_MARK, PG6MD_010),
PINMUX_DATA(IRQ6_PG_MARK, PG6MD_011),
PINMUX_DATA(TIOC2A_MARK, PG6MD_100),
PINMUX_DATA(PG5_DATA, PG5MD_000),
PINMUX_DATA(D21_MARK, PG5MD_001),
- PINMUX_DATA(LCD_DATA5_MARK, PG5MD_010),
+ PINMUX_DATA(LCD_DATA5_PG5_MARK, PG5MD_010),
PINMUX_DATA(IRQ5_PG_MARK, PG5MD_011),
PINMUX_DATA(TIOC1B_MARK, PG5MD_100),
PINMUX_DATA(PG4_DATA, PG4MD_000),
PINMUX_DATA(D20_MARK, PG4MD_001),
- PINMUX_DATA(LCD_DATA4_MARK, PG4MD_010),
+ PINMUX_DATA(LCD_DATA4_PG4_MARK, PG4MD_010),
PINMUX_DATA(IRQ4_PG_MARK, PG4MD_011),
PINMUX_DATA(TIOC1A_MARK, PG4MD_100),
PINMUX_DATA(PG3_DATA, PG3MD_000),
PINMUX_DATA(D19_MARK, PG3MD_001),
- PINMUX_DATA(LCD_DATA3_MARK, PG3MD_010),
+ PINMUX_DATA(LCD_DATA3_PG3_MARK, PG3MD_010),
PINMUX_DATA(IRQ3_PG_MARK, PG3MD_011),
PINMUX_DATA(TIOC0D_MARK, PG3MD_100),
PINMUX_DATA(PG2_DATA, PG2MD_000),
PINMUX_DATA(D18_MARK, PG2MD_001),
- PINMUX_DATA(LCD_DATA2_MARK, PG2MD_010),
+ PINMUX_DATA(LCD_DATA2_PG2_MARK, PG2MD_010),
PINMUX_DATA(IRQ2_PG_MARK, PG2MD_011),
PINMUX_DATA(TIOC0C_MARK, PG2MD_100),
PINMUX_DATA(PG1_DATA, PG1MD_000),
PINMUX_DATA(D17_MARK, PG1MD_001),
- PINMUX_DATA(LCD_DATA1_MARK, PG1MD_010),
+ PINMUX_DATA(LCD_DATA1_PG1_MARK, PG1MD_010),
PINMUX_DATA(IRQ1_PG_MARK, PG1MD_011),
PINMUX_DATA(TIOC0B_MARK, PG1MD_100),
PINMUX_DATA(PG0_DATA, PG0MD_000),
PINMUX_DATA(D16_MARK, PG0MD_001),
- PINMUX_DATA(LCD_DATA0_MARK, PG0MD_010),
+ PINMUX_DATA(LCD_DATA0_PG0_MARK, PG0MD_010),
PINMUX_DATA(IRQ0_PG_MARK, PG0MD_011),
PINMUX_DATA(TIOC0A_MARK, PG0MD_100),
@@ -1275,14 +1289,14 @@
PINMUX_DATA(PJ23_DATA, PJ23MD_000),
PINMUX_DATA(DV_DATA23_MARK, PJ23MD_001),
- PINMUX_DATA(LCD_DATA23_MARK, PJ23MD_010),
+ PINMUX_DATA(LCD_DATA23_PJ23_MARK, PJ23MD_010),
PINMUX_DATA(LCD_TCON6_MARK, PJ23MD_011),
PINMUX_DATA(IRQ3_PJ_MARK, PJ23MD_100),
PINMUX_DATA(CTX1_MARK, PJ23MD_101),
PINMUX_DATA(PJ22_DATA, PJ22MD_000),
PINMUX_DATA(DV_DATA22_MARK, PJ22MD_001),
- PINMUX_DATA(LCD_DATA22_MARK, PJ22MD_010),
+ PINMUX_DATA(LCD_DATA22_PJ22_MARK, PJ22MD_010),
PINMUX_DATA(LCD_TCON5_MARK, PJ22MD_011),
PINMUX_DATA(IRQ2_PJ_MARK, PJ22MD_100),
PINMUX_DATA(CRX1_MARK, PJ22MD_101),
@@ -1290,14 +1304,14 @@
PINMUX_DATA(PJ21_DATA, PJ21MD_000),
PINMUX_DATA(DV_DATA21_MARK, PJ21MD_001),
- PINMUX_DATA(LCD_DATA21_MARK, PJ21MD_010),
+ PINMUX_DATA(LCD_DATA21_PJ21_MARK, PJ21MD_010),
PINMUX_DATA(LCD_TCON4_MARK, PJ21MD_011),
PINMUX_DATA(IRQ1_PJ_MARK, PJ21MD_100),
PINMUX_DATA(CTX2_MARK, PJ21MD_101),
PINMUX_DATA(PJ20_DATA, PJ20MD_000),
PINMUX_DATA(DV_DATA20_MARK, PJ20MD_001),
- PINMUX_DATA(LCD_DATA20_MARK, PJ20MD_010),
+ PINMUX_DATA(LCD_DATA20_PJ20_MARK, PJ20MD_010),
PINMUX_DATA(LCD_TCON3_MARK, PJ20MD_011),
PINMUX_DATA(IRQ0_PJ_MARK, PJ20MD_100),
PINMUX_DATA(CRX2_MARK, PJ20MD_101),
@@ -1305,7 +1319,7 @@
PINMUX_DATA(PJ19_DATA, PJ19MD_000),
PINMUX_DATA(DV_DATA19_MARK, PJ19MD_001),
- PINMUX_DATA(LCD_DATA19_MARK, PJ19MD_010),
+ PINMUX_DATA(LCD_DATA19_PJ19_MARK, PJ19MD_010),
PINMUX_DATA(MISO0_PJ19_MARK, PJ19MD_011),
PINMUX_DATA(TIOC0D_MARK, PJ19MD_100),
PINMUX_DATA(SIOFRXD_MARK, PJ19MD_101),
@@ -1313,126 +1327,126 @@
PINMUX_DATA(PJ18_DATA, PJ18MD_000),
PINMUX_DATA(DV_DATA18_MARK, PJ18MD_001),
- PINMUX_DATA(LCD_DATA18_MARK, PJ18MD_010),
+ PINMUX_DATA(LCD_DATA18_PJ18_MARK, PJ18MD_010),
PINMUX_DATA(MOSI0_PJ18_MARK, PJ18MD_011),
PINMUX_DATA(TIOC0C_MARK, PJ18MD_100),
PINMUX_DATA(SIOFTXD_MARK, PJ18MD_101),
PINMUX_DATA(PJ17_DATA, PJ17MD_000),
PINMUX_DATA(DV_DATA17_MARK, PJ17MD_001),
- PINMUX_DATA(LCD_DATA17_MARK, PJ17MD_010),
+ PINMUX_DATA(LCD_DATA17_PJ17_MARK, PJ17MD_010),
PINMUX_DATA(SSL00_PJ17_MARK, PJ17MD_011),
PINMUX_DATA(TIOC0B_MARK, PJ17MD_100),
PINMUX_DATA(SIOFSYNC_MARK, PJ17MD_101),
PINMUX_DATA(PJ16_DATA, PJ16MD_000),
PINMUX_DATA(DV_DATA16_MARK, PJ16MD_001),
- PINMUX_DATA(LCD_DATA16_MARK, PJ16MD_010),
+ PINMUX_DATA(LCD_DATA16_PJ16_MARK, PJ16MD_010),
PINMUX_DATA(RSPCK0_PJ16_MARK, PJ16MD_011),
PINMUX_DATA(TIOC0A_MARK, PJ16MD_100),
PINMUX_DATA(SIOFSCK_MARK, PJ16MD_101),
PINMUX_DATA(PJ15_DATA, PJ15MD_000),
PINMUX_DATA(DV_DATA15_MARK, PJ15MD_001),
- PINMUX_DATA(LCD_DATA15_MARK, PJ15MD_010),
+ PINMUX_DATA(LCD_DATA15_PJ15_MARK, PJ15MD_010),
PINMUX_DATA(PINT7_PJ_MARK, PJ15MD_011),
PINMUX_DATA(PWM2H_MARK, PJ15MD_100),
PINMUX_DATA(TXD7_MARK, PJ15MD_101),
PINMUX_DATA(PJ14_DATA, PJ14MD_000),
PINMUX_DATA(DV_DATA14_MARK, PJ14MD_001),
- PINMUX_DATA(LCD_DATA14_MARK, PJ14MD_010),
+ PINMUX_DATA(LCD_DATA14_PJ14_MARK, PJ14MD_010),
PINMUX_DATA(PINT6_PJ_MARK, PJ14MD_011),
PINMUX_DATA(PWM2G_MARK, PJ14MD_100),
PINMUX_DATA(TXD6_MARK, PJ14MD_101),
PINMUX_DATA(PJ13_DATA, PJ13MD_000),
PINMUX_DATA(DV_DATA13_MARK, PJ13MD_001),
- PINMUX_DATA(LCD_DATA13_MARK, PJ13MD_010),
+ PINMUX_DATA(LCD_DATA13_PJ13_MARK, PJ13MD_010),
PINMUX_DATA(PINT5_PJ_MARK, PJ13MD_011),
PINMUX_DATA(PWM2F_MARK, PJ13MD_100),
PINMUX_DATA(TXD5_MARK, PJ13MD_101),
PINMUX_DATA(PJ12_DATA, PJ12MD_000),
PINMUX_DATA(DV_DATA12_MARK, PJ12MD_001),
- PINMUX_DATA(LCD_DATA12_MARK, PJ12MD_010),
+ PINMUX_DATA(LCD_DATA12_PJ12_MARK, PJ12MD_010),
PINMUX_DATA(PINT4_PJ_MARK, PJ12MD_011),
PINMUX_DATA(PWM2E_MARK, PJ12MD_100),
PINMUX_DATA(SCK7_MARK, PJ12MD_101),
PINMUX_DATA(PJ11_DATA, PJ11MD_000),
PINMUX_DATA(DV_DATA11_MARK, PJ11MD_001),
- PINMUX_DATA(LCD_DATA11_MARK, PJ11MD_010),
+ PINMUX_DATA(LCD_DATA11_PJ11_MARK, PJ11MD_010),
PINMUX_DATA(PINT3_PJ_MARK, PJ11MD_011),
PINMUX_DATA(PWM2D_MARK, PJ11MD_100),
PINMUX_DATA(SCK6_MARK, PJ11MD_101),
PINMUX_DATA(PJ10_DATA, PJ10MD_000),
PINMUX_DATA(DV_DATA10_MARK, PJ10MD_001),
- PINMUX_DATA(LCD_DATA10_MARK, PJ10MD_010),
+ PINMUX_DATA(LCD_DATA10_PJ10_MARK, PJ10MD_010),
PINMUX_DATA(PINT2_PJ_MARK, PJ10MD_011),
PINMUX_DATA(PWM2C_MARK, PJ10MD_100),
PINMUX_DATA(SCK5_MARK, PJ10MD_101),
PINMUX_DATA(PJ9_DATA, PJ9MD_000),
PINMUX_DATA(DV_DATA9_MARK, PJ9MD_001),
- PINMUX_DATA(LCD_DATA9_MARK, PJ9MD_010),
+ PINMUX_DATA(LCD_DATA9_PJ9_MARK, PJ9MD_010),
PINMUX_DATA(PINT1_PJ_MARK, PJ9MD_011),
PINMUX_DATA(PWM2B_MARK, PJ9MD_100),
PINMUX_DATA(RTS5_MARK, PJ9MD_101),
PINMUX_DATA(PJ8_DATA, PJ8MD_000),
PINMUX_DATA(DV_DATA8_MARK, PJ8MD_001),
- PINMUX_DATA(LCD_DATA8_MARK, PJ8MD_010),
+ PINMUX_DATA(LCD_DATA8_PJ8_MARK, PJ8MD_010),
PINMUX_DATA(PINT0_PJ_MARK, PJ8MD_011),
PINMUX_DATA(PWM2A_MARK, PJ8MD_100),
PINMUX_DATA(CTS5_MARK, PJ8MD_101),
PINMUX_DATA(PJ7_DATA, PJ7MD_000),
PINMUX_DATA(DV_DATA7_MARK, PJ7MD_001),
- PINMUX_DATA(LCD_DATA7_MARK, PJ7MD_010),
+ PINMUX_DATA(LCD_DATA7_PJ7_MARK, PJ7MD_010),
PINMUX_DATA(SD_D2_MARK, PJ7MD_011),
PINMUX_DATA(PWM1H_MARK, PJ7MD_100),
PINMUX_DATA(PJ6_DATA, PJ6MD_000),
PINMUX_DATA(DV_DATA6_MARK, PJ6MD_001),
- PINMUX_DATA(LCD_DATA6_MARK, PJ6MD_010),
+ PINMUX_DATA(LCD_DATA6_PJ6_MARK, PJ6MD_010),
PINMUX_DATA(SD_D3_MARK, PJ6MD_011),
PINMUX_DATA(PWM1G_MARK, PJ6MD_100),
PINMUX_DATA(PJ5_DATA, PJ5MD_000),
PINMUX_DATA(DV_DATA5_MARK, PJ5MD_001),
- PINMUX_DATA(LCD_DATA5_MARK, PJ5MD_010),
+ PINMUX_DATA(LCD_DATA5_PJ5_MARK, PJ5MD_010),
PINMUX_DATA(SD_CMD_MARK, PJ5MD_011),
PINMUX_DATA(PWM1F_MARK, PJ5MD_100),
PINMUX_DATA(PJ4_DATA, PJ4MD_000),
PINMUX_DATA(DV_DATA4_MARK, PJ4MD_001),
- PINMUX_DATA(LCD_DATA4_MARK, PJ4MD_010),
+ PINMUX_DATA(LCD_DATA4_PJ4_MARK, PJ4MD_010),
PINMUX_DATA(SD_CLK_MARK, PJ4MD_011),
PINMUX_DATA(PWM1E_MARK, PJ4MD_100),
PINMUX_DATA(PJ3_DATA, PJ3MD_000),
PINMUX_DATA(DV_DATA3_MARK, PJ3MD_001),
- PINMUX_DATA(LCD_DATA3_MARK, PJ3MD_010),
+ PINMUX_DATA(LCD_DATA3_PJ3_MARK, PJ3MD_010),
PINMUX_DATA(SD_D0_MARK, PJ3MD_011),
PINMUX_DATA(PWM1D_MARK, PJ3MD_100),
PINMUX_DATA(PJ2_DATA, PJ2MD_000),
PINMUX_DATA(DV_DATA2_MARK, PJ2MD_001),
- PINMUX_DATA(LCD_DATA2_MARK, PJ2MD_010),
+ PINMUX_DATA(LCD_DATA2_PJ2_MARK, PJ2MD_010),
PINMUX_DATA(SD_D1_MARK, PJ2MD_011),
PINMUX_DATA(PWM1C_MARK, PJ2MD_100),
PINMUX_DATA(PJ1_DATA, PJ1MD_000),
PINMUX_DATA(DV_DATA1_MARK, PJ1MD_001),
- PINMUX_DATA(LCD_DATA1_MARK, PJ1MD_010),
+ PINMUX_DATA(LCD_DATA1_PJ1_MARK, PJ1MD_010),
PINMUX_DATA(SD_WP_MARK, PJ1MD_011),
PINMUX_DATA(PWM1B_MARK, PJ1MD_100),
PINMUX_DATA(PJ0_DATA, PJ0MD_000),
PINMUX_DATA(DV_DATA0_MARK, PJ0MD_001),
- PINMUX_DATA(LCD_DATA0_MARK, PJ0MD_010),
+ PINMUX_DATA(LCD_DATA0_PJ0_MARK, PJ0MD_010),
PINMUX_DATA(SD_CD_MARK, PJ0MD_011),
PINMUX_DATA(PWM1A_MARK, PJ0MD_100),
};
@@ -1877,30 +1891,55 @@
PINMUX_GPIO(GPIO_FN_LCD_HSYNC, LCD_HSYNC_MARK),
PINMUX_GPIO(GPIO_FN_LCD_DE, LCD_DE_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA23, LCD_DATA23_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA22, LCD_DATA22_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA21, LCD_DATA21_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA20, LCD_DATA20_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA19, LCD_DATA19_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA18, LCD_DATA18_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA17, LCD_DATA17_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA16, LCD_DATA16_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA15, LCD_DATA15_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA14, LCD_DATA14_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA13, LCD_DATA13_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA12, LCD_DATA12_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA11, LCD_DATA11_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA10, LCD_DATA10_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA9, LCD_DATA9_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA8, LCD_DATA8_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA7, LCD_DATA7_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA6, LCD_DATA6_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA5, LCD_DATA5_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA4, LCD_DATA4_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA3, LCD_DATA3_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA2, LCD_DATA2_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA1, LCD_DATA1_MARK),
- PINMUX_GPIO(GPIO_FN_LCD_DATA0, LCD_DATA0_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA23_PG23, LCD_DATA23_PG23_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA22_PG22, LCD_DATA22_PG22_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA21_PG21, LCD_DATA21_PG21_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA20_PG20, LCD_DATA20_PG20_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA19_PG19, LCD_DATA19_PG19_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA18_PG18, LCD_DATA18_PG18_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA17_PG17, LCD_DATA17_PG17_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA16_PG16, LCD_DATA16_PG16_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA15_PG15, LCD_DATA15_PG15_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA14_PG14, LCD_DATA14_PG14_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA13_PG13, LCD_DATA13_PG13_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA12_PG12, LCD_DATA12_PG12_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA11_PG11, LCD_DATA11_PG11_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA10_PG10, LCD_DATA10_PG10_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA9_PG9, LCD_DATA9_PG9_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA8_PG8, LCD_DATA8_PG8_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA7_PG7, LCD_DATA7_PG7_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA6_PG6, LCD_DATA6_PG6_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA5_PG5, LCD_DATA5_PG5_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA4_PG4, LCD_DATA4_PG4_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA3_PG3, LCD_DATA3_PG3_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA2_PG2, LCD_DATA2_PG2_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA1_PG1, LCD_DATA1_PG1_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA0_PG0, LCD_DATA0_PG0_MARK),
+
+ PINMUX_GPIO(GPIO_FN_LCD_DATA23_PJ23, LCD_DATA23_PJ23_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA22_PJ22, LCD_DATA22_PJ22_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA21_PJ21, LCD_DATA21_PJ21_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA20_PJ20, LCD_DATA20_PJ20_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA19_PJ19, LCD_DATA19_PJ19_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA18_PJ18, LCD_DATA18_PJ18_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA17_PJ17, LCD_DATA17_PJ17_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA16_PJ16, LCD_DATA16_PJ16_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA15_PJ15, LCD_DATA15_PJ15_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA14_PJ14, LCD_DATA14_PJ14_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA13_PJ13, LCD_DATA13_PJ13_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA12_PJ12, LCD_DATA12_PJ12_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA11_PJ11, LCD_DATA11_PJ11_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA10_PJ10, LCD_DATA10_PJ10_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA9_PJ9, LCD_DATA9_PJ9_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA8_PJ8, LCD_DATA8_PJ8_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA7_PJ7, LCD_DATA7_PJ7_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA6_PJ6, LCD_DATA6_PJ6_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA5_PJ5, LCD_DATA5_PJ5_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA4_PJ4, LCD_DATA4_PJ4_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA3_PJ3, LCD_DATA3_PJ3_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA2_PJ2, LCD_DATA2_PJ2_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA1_PJ1, LCD_DATA1_PJ1_MARK),
+ PINMUX_GPIO(GPIO_FN_LCD_DATA0_PJ0, LCD_DATA0_PJ0_MARK),
PINMUX_GPIO(GPIO_FN_LCD_M_DISP, LCD_M_DISP_MARK),
};
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 7b57bf1..ebe7a7d 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -273,7 +273,7 @@
data_resource.start = virt_to_phys(_etext);
data_resource.end = virt_to_phys(_edata)-1;
bss_resource.start = virt_to_phys(__bss_start);
- bss_resource.end = virt_to_phys(_ebss)-1;
+ bss_resource.end = virt_to_phys(__bss_stop)-1;
#ifdef CONFIG_CMDLINE_OVERWRITE
strlcpy(command_line, CONFIG_CMDLINE, sizeof(command_line));
diff --git a/arch/sh/kernel/sh_ksyms_32.c b/arch/sh/kernel/sh_ksyms_32.c
index 3896f26..2a0a596 100644
--- a/arch/sh/kernel/sh_ksyms_32.c
+++ b/arch/sh/kernel/sh_ksyms_32.c
@@ -19,7 +19,6 @@
EXPORT_SYMBOL(csum_partial_copy_generic);
EXPORT_SYMBOL(copy_page);
EXPORT_SYMBOL(__clear_user);
-EXPORT_SYMBOL(_ebss);
EXPORT_SYMBOL(empty_zero_page);
#define DECLARE_EXPORT(name) \
diff --git a/arch/sh/kernel/vmlinux.lds.S b/arch/sh/kernel/vmlinux.lds.S
index c98905f..db88cbf 100644
--- a/arch/sh/kernel/vmlinux.lds.S
+++ b/arch/sh/kernel/vmlinux.lds.S
@@ -78,7 +78,6 @@
. = ALIGN(PAGE_SIZE);
__init_end = .;
BSS_SECTION(0, PAGE_SIZE, 4)
- _ebss = .; /* uClinux MTD sucks */
_end = . ;
STABS_DEBUG
diff --git a/arch/sh/lib/mcount.S b/arch/sh/lib/mcount.S
index 84a5776..60164e6 100644
--- a/arch/sh/lib/mcount.S
+++ b/arch/sh/lib/mcount.S
@@ -39,7 +39,7 @@
*
* Make sure the stack pointer contains a valid address. Valid
* addresses for kernel stacks are anywhere after the bss
- * (after _ebss) and anywhere in init_thread_union (init_stack).
+ * (after __bss_stop) and anywhere in init_thread_union (init_stack).
*/
#define STACK_CHECK() \
mov #(THREAD_SIZE >> 10), r0; \
@@ -60,7 +60,7 @@
cmp/hi r2, r1; \
bf stack_panic; \
\
- /* If sp > _ebss then we're OK. */ \
+ /* If sp > __bss_stop then we're OK. */ \
mov.l .L_ebss, r1; \
cmp/hi r1, r15; \
bt 1f; \
@@ -70,7 +70,7 @@
cmp/hs r1, r15; \
bf stack_panic; \
\
- /* If sp > init_stack && sp < _ebss, not OK. */ \
+ /* If sp > init_stack && sp < __bss_stop, not OK. */ \
add r0, r1; \
cmp/hs r1, r15; \
bt stack_panic; \
@@ -292,8 +292,6 @@
nop
.align 2
-.L_ebss:
- .long _ebss
.L_init_thread_union:
.long init_thread_union
.Lpanic:
diff --git a/arch/sparc/kernel/sys_sparc_64.c b/arch/sparc/kernel/sys_sparc_64.c
index 0dc1f57..11c6c96 100644
--- a/arch/sparc/kernel/sys_sparc_64.c
+++ b/arch/sparc/kernel/sys_sparc_64.c
@@ -502,12 +502,12 @@
{
int ret;
- if (current->personality == PER_LINUX32 &&
- personality == PER_LINUX)
- personality = PER_LINUX32;
+ if (personality(current->personality) == PER_LINUX32 &&
+ personality(personality) == PER_LINUX)
+ personality |= PER_LINUX32;
ret = sys_personality(personality);
- if (ret == PER_LINUX32)
- ret = PER_LINUX;
+ if (personality(ret) == PER_LINUX32)
+ ret &= ~PER_LINUX32;
return ret;
}
diff --git a/arch/sparc/mm/init_64.c b/arch/sparc/mm/init_64.c
index 6026fdd..d58edf5 100644
--- a/arch/sparc/mm/init_64.c
+++ b/arch/sparc/mm/init_64.c
@@ -2020,6 +2020,9 @@
#ifdef CONFIG_SPARSEMEM_VMEMMAP
unsigned long vmemmap_table[VMEMMAP_SIZE];
+static long __meminitdata addr_start, addr_end;
+static int __meminitdata node_start;
+
int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
{
unsigned long vstart = (unsigned long) start;
@@ -2050,15 +2053,30 @@
*vmem_pp = pte_base | __pa(block);
- printk(KERN_INFO "[%p-%p] page_structs=%lu "
- "node=%d entry=%lu/%lu\n", start, block, nr,
- node,
- addr >> VMEMMAP_CHUNK_SHIFT,
- VMEMMAP_SIZE);
+ /* check to see if we have contiguous blocks */
+ if (addr_end != addr || node_start != node) {
+ if (addr_start)
+ printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
+ addr_start, addr_end-1, node_start);
+ addr_start = addr;
+ node_start = node;
+ }
+ addr_end = addr + VMEMMAP_CHUNK;
}
}
return 0;
}
+
+void __meminit vmemmap_populate_print_last(void)
+{
+ if (addr_start) {
+ printk(KERN_DEBUG " [%lx-%lx] on node %d\n",
+ addr_start, addr_end-1, node_start);
+ addr_start = 0;
+ addr_end = 0;
+ node_start = 0;
+ }
+}
#endif /* CONFIG_SPARSEMEM_VMEMMAP */
static void prot_init_common(unsigned long page_none,
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index ba2657c..8ec3a1a 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1527,7 +1527,7 @@
If unsure, say Y. Only embedded should say N here.
config CC_STACKPROTECTOR
- bool "Enable -fstack-protector buffer overflow detection (EXPERIMENTAL)"
+ bool "Enable -fstack-protector buffer overflow detection"
---help---
This option turns on the -fstack-protector GCC feature. This
feature puts, at the beginning of functions, a canary value on
diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index b0c5276..682e9c2 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -27,6 +27,10 @@
KBUILD_CFLAGS += -msoft-float -mregparm=3 -freg-struct-return
+ # Never want PIC in a 32-bit kernel, prevent breakage with GCC built
+ # with nonstandard options
+ KBUILD_CFLAGS += -fno-pic
+
# prevent gcc from keeping the stack 16 byte aligned
KBUILD_CFLAGS += $(call cc-option,-mpreferred-stack-boundary=2)
diff --git a/arch/x86/boot/Makefile b/arch/x86/boot/Makefile
index 5a747dd..f7535be 100644
--- a/arch/x86/boot/Makefile
+++ b/arch/x86/boot/Makefile
@@ -57,7 +57,7 @@
-Wall -Wstrict-prototypes \
-march=i386 -mregparm=3 \
-include $(srctree)/$(src)/code16gcc.h \
- -fno-strict-aliasing -fomit-frame-pointer \
+ -fno-strict-aliasing -fomit-frame-pointer -fno-pic \
$(call cc-option, -ffreestanding) \
$(call cc-option, -fno-toplevel-reorder,\
$(call cc-option, -fno-unit-at-a-time)) \
diff --git a/arch/x86/include/asm/mce.h b/arch/x86/include/asm/mce.h
index 441520e..a3ac52b 100644
--- a/arch/x86/include/asm/mce.h
+++ b/arch/x86/include/asm/mce.h
@@ -33,6 +33,14 @@
#define MCI_STATUS_PCC (1ULL<<57) /* processor context corrupt */
#define MCI_STATUS_S (1ULL<<56) /* Signaled machine check */
#define MCI_STATUS_AR (1ULL<<55) /* Action required */
+#define MCACOD 0xffff /* MCA Error Code */
+
+/* Architecturally defined codes from SDM Vol. 3B Chapter 15 */
+#define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */
+#define MCACOD_SCRUBMSK 0xfff0
+#define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */
+#define MCACOD_DATA 0x0134 /* Data Load */
+#define MCACOD_INSTR 0x0150 /* Instruction Fetch */
/* MCi_MISC register defines */
#define MCI_MISC_ADDR_LSB(m) ((m) & 0x3f)
diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h
index dab3935..cb4e43b 100644
--- a/arch/x86/include/asm/perf_event.h
+++ b/arch/x86/include/asm/perf_event.h
@@ -196,11 +196,16 @@
extern void perf_events_lapic_init(void);
/*
- * Abuse bit 3 of the cpu eflags register to indicate proper PEBS IP fixups.
- * This flag is otherwise unused and ABI specified to be 0, so nobody should
- * care what we do with it.
+ * Abuse bits {3,5} of the cpu eflags register. These flags are otherwise
+ * unused and ABI specified to be 0, so nobody should care what we do with
+ * them.
+ *
+ * EXACT - the IP points to the exact instruction that triggered the
+ * event (HW bugs exempt).
+ * VM - original X86_VM_MASK; see set_linear_ip().
*/
#define PERF_EFLAGS_EXACT (1UL << 3)
+#define PERF_EFLAGS_VM (1UL << 5)
struct pt_regs;
extern unsigned long perf_instruction_pointer(struct pt_regs *regs);
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index 95bf99d..1b8e5a0 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -25,10 +25,6 @@
static char temp_stack[4096];
#endif
-asmlinkage void acpi_enter_s3(void)
-{
- acpi_enter_sleep_state(3, wake_sleep_flags);
-}
/**
* acpi_suspend_lowlevel - save kernel state
*
diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h
index 5653a57..67f59f8 100644
--- a/arch/x86/kernel/acpi/sleep.h
+++ b/arch/x86/kernel/acpi/sleep.h
@@ -2,7 +2,6 @@
* Variables and functions used by the code in sleep.c
*/
-#include <linux/linkage.h>
#include <asm/realmode.h>
extern unsigned long saved_video_mode;
@@ -11,7 +10,6 @@
extern int wakeup_pmode_return;
extern u8 wake_sleep_flags;
-extern asmlinkage void acpi_enter_s3(void);
extern unsigned long acpi_copy_wakeup_routine(unsigned long);
extern void wakeup_long64(void);
diff --git a/arch/x86/kernel/acpi/wakeup_32.S b/arch/x86/kernel/acpi/wakeup_32.S
index 7261083..13ab7205 100644
--- a/arch/x86/kernel/acpi/wakeup_32.S
+++ b/arch/x86/kernel/acpi/wakeup_32.S
@@ -74,7 +74,9 @@
ENTRY(do_suspend_lowlevel)
call save_processor_state
call save_registers
- call acpi_enter_s3
+ pushl $3
+ call acpi_enter_sleep_state
+ addl $4, %esp
# In case of S3 failure, we'll emerge here. Jump
# to ret_point to recover
diff --git a/arch/x86/kernel/acpi/wakeup_64.S b/arch/x86/kernel/acpi/wakeup_64.S
index 014d1d2..8ea5164 100644
--- a/arch/x86/kernel/acpi/wakeup_64.S
+++ b/arch/x86/kernel/acpi/wakeup_64.S
@@ -71,7 +71,9 @@
movq %rsi, saved_rsi
addq $8, %rsp
- call acpi_enter_s3
+ movl $3, %edi
+ xorl %eax, %eax
+ call acpi_enter_sleep_state
/* in case something went wrong, restore the machine status and go on */
jmp resume_point
diff --git a/arch/x86/kernel/alternative.c b/arch/x86/kernel/alternative.c
index 931280f..afb7ff7 100644
--- a/arch/x86/kernel/alternative.c
+++ b/arch/x86/kernel/alternative.c
@@ -224,7 +224,7 @@
ideal_nops = intel_nops;
#endif
}
-
+ break;
default:
#ifdef CONFIG_X86_64
ideal_nops = k8_nops;
diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 406eee7..c265593 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1204,7 +1204,7 @@
BUG_ON(!cfg->vector);
vector = cfg->vector;
- for_each_cpu(cpu, cfg->domain)
+ for_each_cpu_and(cpu, cfg->domain, cpu_online_mask)
per_cpu(vector_irq, cpu)[vector] = -1;
cfg->vector = 0;
@@ -1212,7 +1212,7 @@
if (likely(!cfg->move_in_progress))
return;
- for_each_cpu(cpu, cfg->old_domain) {
+ for_each_cpu_and(cpu, cfg->old_domain, cpu_online_mask) {
for (vector = FIRST_EXTERNAL_VECTOR; vector < NR_VECTORS;
vector++) {
if (per_cpu(vector_irq, cpu)[vector] != irq)
@@ -1356,6 +1356,16 @@
if (!IO_APIC_IRQ(irq))
return;
+ /*
+ * For legacy irqs, cfg->domain starts with cpu 0. Now that IO-APIC
+ * can handle this irq and the apic driver is finialized at this point,
+ * update the cfg->domain.
+ */
+ if (irq < legacy_pic->nr_legacy_irqs &&
+ cpumask_equal(cfg->domain, cpumask_of(0)))
+ apic->vector_allocation_domain(0, cfg->domain,
+ apic->target_cpus());
+
if (assign_irq_vector(irq, cfg, apic->target_cpus()))
return;
diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
index 46d8786..a5fbc3c 100644
--- a/arch/x86/kernel/cpu/common.c
+++ b/arch/x86/kernel/cpu/common.c
@@ -144,6 +144,8 @@
{
setup_clear_cpu_cap(X86_FEATURE_XSAVE);
setup_clear_cpu_cap(X86_FEATURE_XSAVEOPT);
+ setup_clear_cpu_cap(X86_FEATURE_AVX);
+ setup_clear_cpu_cap(X86_FEATURE_AVX2);
return 1;
}
__setup("noxsave", x86_xsave_setup);
diff --git a/arch/x86/kernel/cpu/mcheck/mce-severity.c b/arch/x86/kernel/cpu/mcheck/mce-severity.c
index 413c2ce..1301762 100644
--- a/arch/x86/kernel/cpu/mcheck/mce-severity.c
+++ b/arch/x86/kernel/cpu/mcheck/mce-severity.c
@@ -55,13 +55,6 @@
#define MCI_UC_S (MCI_STATUS_UC|MCI_STATUS_S)
#define MCI_UC_SAR (MCI_STATUS_UC|MCI_STATUS_S|MCI_STATUS_AR)
#define MCI_ADDR (MCI_STATUS_ADDRV|MCI_STATUS_MISCV)
-#define MCACOD 0xffff
-/* Architecturally defined codes from SDM Vol. 3B Chapter 15 */
-#define MCACOD_SCRUB 0x00C0 /* 0xC0-0xCF Memory Scrubbing */
-#define MCACOD_SCRUBMSK 0xfff0
-#define MCACOD_L3WB 0x017A /* L3 Explicit Writeback */
-#define MCACOD_DATA 0x0134 /* Data Load */
-#define MCACOD_INSTR 0x0150 /* Instruction Fetch */
MCESEV(
NO, "Invalid",
diff --git a/arch/x86/kernel/cpu/mcheck/mce.c b/arch/x86/kernel/cpu/mcheck/mce.c
index 5e095f8..292d025 100644
--- a/arch/x86/kernel/cpu/mcheck/mce.c
+++ b/arch/x86/kernel/cpu/mcheck/mce.c
@@ -103,6 +103,8 @@
static DEFINE_PER_CPU(struct work_struct, mce_work);
+static void (*quirk_no_way_out)(int bank, struct mce *m, struct pt_regs *regs);
+
/*
* CPU/chipset specific EDAC code can register a notifier call here to print
* MCE errors in a human-readable form.
@@ -650,14 +652,18 @@
* Do a quick check if any of the events requires a panic.
* This decides if we keep the events around or clear them.
*/
-static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp)
+static int mce_no_way_out(struct mce *m, char **msg, unsigned long *validp,
+ struct pt_regs *regs)
{
int i, ret = 0;
for (i = 0; i < banks; i++) {
m->status = mce_rdmsrl(MSR_IA32_MCx_STATUS(i));
- if (m->status & MCI_STATUS_VAL)
+ if (m->status & MCI_STATUS_VAL) {
__set_bit(i, validp);
+ if (quirk_no_way_out)
+ quirk_no_way_out(i, m, regs);
+ }
if (mce_severity(m, tolerant, msg) >= MCE_PANIC_SEVERITY)
ret = 1;
}
@@ -1040,7 +1046,7 @@
*final = m;
memset(valid_banks, 0, sizeof(valid_banks));
- no_way_out = mce_no_way_out(&m, &msg, valid_banks);
+ no_way_out = mce_no_way_out(&m, &msg, valid_banks, regs);
barrier();
@@ -1418,6 +1424,34 @@
}
}
+/*
+ * During IFU recovery Sandy Bridge -EP4S processors set the RIPV and
+ * EIPV bits in MCG_STATUS to zero on the affected logical processor (SDM
+ * Vol 3B Table 15-20). But this confuses both the code that determines
+ * whether the machine check occurred in kernel or user mode, and also
+ * the severity assessment code. Pretend that EIPV was set, and take the
+ * ip/cs values from the pt_regs that mce_gather_info() ignored earlier.
+ */
+static void quirk_sandybridge_ifu(int bank, struct mce *m, struct pt_regs *regs)
+{
+ if (bank != 0)
+ return;
+ if ((m->mcgstatus & (MCG_STATUS_EIPV|MCG_STATUS_RIPV)) != 0)
+ return;
+ if ((m->status & (MCI_STATUS_OVER|MCI_STATUS_UC|
+ MCI_STATUS_EN|MCI_STATUS_MISCV|MCI_STATUS_ADDRV|
+ MCI_STATUS_PCC|MCI_STATUS_S|MCI_STATUS_AR|
+ MCACOD)) !=
+ (MCI_STATUS_UC|MCI_STATUS_EN|
+ MCI_STATUS_MISCV|MCI_STATUS_ADDRV|MCI_STATUS_S|
+ MCI_STATUS_AR|MCACOD_INSTR))
+ return;
+
+ m->mcgstatus |= MCG_STATUS_EIPV;
+ m->ip = regs->ip;
+ m->cs = regs->cs;
+}
+
/* Add per CPU specific workarounds here */
static int __cpuinit __mcheck_cpu_apply_quirks(struct cpuinfo_x86 *c)
{
@@ -1515,6 +1549,9 @@
*/
if (c->x86 == 6 && c->x86_model <= 13 && mce_bootlog < 0)
mce_bootlog = 0;
+
+ if (c->x86 == 6 && c->x86_model == 45)
+ quirk_no_way_out = quirk_sandybridge_ifu;
}
if (monarch_timeout < 0)
monarch_timeout = 0;
diff --git a/arch/x86/kernel/cpu/perf_event.c b/arch/x86/kernel/cpu/perf_event.c
index 29557aa..915b876 100644
--- a/arch/x86/kernel/cpu/perf_event.c
+++ b/arch/x86/kernel/cpu/perf_event.c
@@ -32,6 +32,8 @@
#include <asm/smp.h>
#include <asm/alternative.h>
#include <asm/timer.h>
+#include <asm/desc.h>
+#include <asm/ldt.h>
#include "perf_event.h"
@@ -1738,6 +1740,29 @@
return (__range_not_ok(fp, size, TASK_SIZE) == 0);
}
+static unsigned long get_segment_base(unsigned int segment)
+{
+ struct desc_struct *desc;
+ int idx = segment >> 3;
+
+ if ((segment & SEGMENT_TI_MASK) == SEGMENT_LDT) {
+ if (idx > LDT_ENTRIES)
+ return 0;
+
+ if (idx > current->active_mm->context.size)
+ return 0;
+
+ desc = current->active_mm->context.ldt;
+ } else {
+ if (idx > GDT_ENTRIES)
+ return 0;
+
+ desc = __this_cpu_ptr(&gdt_page.gdt[0]);
+ }
+
+ return get_desc_base(desc + idx);
+}
+
#ifdef CONFIG_COMPAT
#include <asm/compat.h>
@@ -1746,13 +1771,17 @@
perf_callchain_user32(struct pt_regs *regs, struct perf_callchain_entry *entry)
{
/* 32-bit process in 64-bit kernel. */
+ unsigned long ss_base, cs_base;
struct stack_frame_ia32 frame;
const void __user *fp;
if (!test_thread_flag(TIF_IA32))
return 0;
- fp = compat_ptr(regs->bp);
+ cs_base = get_segment_base(regs->cs);
+ ss_base = get_segment_base(regs->ss);
+
+ fp = compat_ptr(ss_base + regs->bp);
while (entry->nr < PERF_MAX_STACK_DEPTH) {
unsigned long bytes;
frame.next_frame = 0;
@@ -1765,8 +1794,8 @@
if (!valid_user_frame(fp, sizeof(frame)))
break;
- perf_callchain_store(entry, frame.return_address);
- fp = compat_ptr(frame.next_frame);
+ perf_callchain_store(entry, cs_base + frame.return_address);
+ fp = compat_ptr(ss_base + frame.next_frame);
}
return 1;
}
@@ -1789,6 +1818,12 @@
return;
}
+ /*
+ * We don't know what to do with VM86 stacks.. ignore them for now.
+ */
+ if (regs->flags & (X86_VM_MASK | PERF_EFLAGS_VM))
+ return;
+
fp = (void __user *)regs->bp;
perf_callchain_store(entry, regs->ip);
@@ -1816,16 +1851,50 @@
}
}
+/*
+ * Deal with code segment offsets for the various execution modes:
+ *
+ * VM86 - the good olde 16 bit days, where the linear address is
+ * 20 bits and we use regs->ip + 0x10 * regs->cs.
+ *
+ * IA32 - Where we need to look at GDT/LDT segment descriptor tables
+ * to figure out what the 32bit base address is.
+ *
+ * X32 - has TIF_X32 set, but is running in x86_64
+ *
+ * X86_64 - CS,DS,SS,ES are all zero based.
+ */
+static unsigned long code_segment_base(struct pt_regs *regs)
+{
+ /*
+ * If we are in VM86 mode, add the segment offset to convert to a
+ * linear address.
+ */
+ if (regs->flags & X86_VM_MASK)
+ return 0x10 * regs->cs;
+
+ /*
+ * For IA32 we look at the GDT/LDT segment base to convert the
+ * effective IP to a linear address.
+ */
+#ifdef CONFIG_X86_32
+ if (user_mode(regs) && regs->cs != __USER_CS)
+ return get_segment_base(regs->cs);
+#else
+ if (test_thread_flag(TIF_IA32)) {
+ if (user_mode(regs) && regs->cs != __USER32_CS)
+ return get_segment_base(regs->cs);
+ }
+#endif
+ return 0;
+}
+
unsigned long perf_instruction_pointer(struct pt_regs *regs)
{
- unsigned long ip;
-
if (perf_guest_cbs && perf_guest_cbs->is_in_guest())
- ip = perf_guest_cbs->get_guest_ip();
- else
- ip = instruction_pointer(regs);
+ return perf_guest_cbs->get_guest_ip();
- return ip;
+ return regs->ip + code_segment_base(regs);
}
unsigned long perf_misc_flags(struct pt_regs *regs)
@@ -1838,7 +1907,7 @@
else
misc |= PERF_RECORD_MISC_GUEST_KERNEL;
} else {
- if (!kernel_ip(regs->ip))
+ if (user_mode(regs))
misc |= PERF_RECORD_MISC_USER;
else
misc |= PERF_RECORD_MISC_KERNEL;
diff --git a/arch/x86/kernel/cpu/perf_event.h b/arch/x86/kernel/cpu/perf_event.h
index 821d53b..6605a81 100644
--- a/arch/x86/kernel/cpu/perf_event.h
+++ b/arch/x86/kernel/cpu/perf_event.h
@@ -516,6 +516,26 @@
#endif
}
+/*
+ * Not all PMUs provide the right context information to place the reported IP
+ * into full context. Specifically segment registers are typically not
+ * supplied.
+ *
+ * Assuming the address is a linear address (it is for IBS), we fake the CS and
+ * vm86 mode using the known zero-based code segment and 'fix up' the registers
+ * to reflect this.
+ *
+ * Intel PEBS/LBR appear to typically provide the effective address, nothing
+ * much we can do about that but pray and treat it like a linear address.
+ */
+static inline void set_linear_ip(struct pt_regs *regs, unsigned long ip)
+{
+ regs->cs = kernel_ip(ip) ? __KERNEL_CS : __USER_CS;
+ if (regs->flags & X86_VM_MASK)
+ regs->flags ^= (PERF_EFLAGS_VM | X86_VM_MASK);
+ regs->ip = ip;
+}
+
#ifdef CONFIG_CPU_SUP_AMD
int amd_pmu_init(void);
diff --git a/arch/x86/kernel/cpu/perf_event_amd_ibs.c b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
index da9bcdc..7bfb5be 100644
--- a/arch/x86/kernel/cpu/perf_event_amd_ibs.c
+++ b/arch/x86/kernel/cpu/perf_event_amd_ibs.c
@@ -13,6 +13,8 @@
#include <asm/apic.h>
+#include "perf_event.h"
+
static u32 ibs_caps;
#if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_AMD)
@@ -536,7 +538,7 @@
if (check_rip && (ibs_data.regs[2] & IBS_RIP_INVALID)) {
regs.flags &= ~PERF_EFLAGS_EXACT;
} else {
- instruction_pointer_set(®s, ibs_data.regs[1]);
+ set_linear_ip(®s, ibs_data.regs[1]);
regs.flags |= PERF_EFLAGS_EXACT;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c
index 3823669..7f2739e 100644
--- a/arch/x86/kernel/cpu/perf_event_intel.c
+++ b/arch/x86/kernel/cpu/perf_event_intel.c
@@ -1522,8 +1522,16 @@
arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL;
arr[0].host = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_guest_mask;
arr[0].guest = x86_pmu.intel_ctrl & ~cpuc->intel_ctrl_host_mask;
+ /*
+ * If PMU counter has PEBS enabled it is not enough to disable counter
+ * on a guest entry since PEBS memory write can overshoot guest entry
+ * and corrupt guest memory. Disabling PEBS solves the problem.
+ */
+ arr[1].msr = MSR_IA32_PEBS_ENABLE;
+ arr[1].host = cpuc->pebs_enabled;
+ arr[1].guest = 0;
- *nr = 1;
+ *nr = 2;
return arr;
}
diff --git a/arch/x86/kernel/cpu/perf_event_intel_ds.c b/arch/x86/kernel/cpu/perf_event_intel_ds.c
index 629ae0b..e38d97b 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_ds.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_ds.c
@@ -499,7 +499,7 @@
* We sampled a branch insn, rewind using the LBR stack
*/
if (ip == to) {
- regs->ip = from;
+ set_linear_ip(regs, from);
return 1;
}
@@ -529,7 +529,7 @@
} while (to < ip);
if (to == ip) {
- regs->ip = old_to;
+ set_linear_ip(regs, old_to);
return 1;
}
@@ -569,7 +569,8 @@
* A possible PERF_SAMPLE_REGS will have to transfer all regs.
*/
regs = *iregs;
- regs.ip = pebs->ip;
+ regs.flags = pebs->flags;
+ set_linear_ip(®s, pebs->ip);
regs.bp = pebs->bp;
regs.sp = pebs->sp;
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.c b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
index 7563fda..0a55710 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.c
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.c
@@ -796,7 +796,6 @@
DEFINE_UNCORE_FORMAT_ATTR(event5, event, "config:1-5");
DEFINE_UNCORE_FORMAT_ATTR(counter, counter, "config:6-7");
-DEFINE_UNCORE_FORMAT_ATTR(mm_cfg, mm_cfg, "config:63");
DEFINE_UNCORE_FORMAT_ATTR(match, match, "config1:0-63");
DEFINE_UNCORE_FORMAT_ATTR(mask, mask, "config2:0-63");
@@ -902,16 +901,21 @@
.attrs = nhmex_uncore_cbox_formats_attr,
};
+/* msr offset for each instance of cbox */
+static unsigned nhmex_cbox_msr_offsets[] = {
+ 0x0, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x240, 0x2c0,
+};
+
static struct intel_uncore_type nhmex_uncore_cbox = {
.name = "cbox",
.num_counters = 6,
- .num_boxes = 8,
+ .num_boxes = 10,
.perf_ctr_bits = 48,
.event_ctl = NHMEX_C0_MSR_PMON_EV_SEL0,
.perf_ctr = NHMEX_C0_MSR_PMON_CTR0,
.event_mask = NHMEX_PMON_RAW_EVENT_MASK,
.box_ctl = NHMEX_C0_MSR_PMON_GLOBAL_CTL,
- .msr_offset = NHMEX_C_MSR_OFFSET,
+ .msr_offsets = nhmex_cbox_msr_offsets,
.pair_ctr_ctl = 1,
.ops = &nhmex_uncore_ops,
.format_group = &nhmex_uncore_cbox_format_group
@@ -1032,24 +1036,22 @@
static int nhmex_sbox_hw_config(struct intel_uncore_box *box, struct perf_event *event)
{
- struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
- struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
+ struct hw_perf_event *hwc = &event->hw;
+ struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
+ struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- if (event->attr.config & NHMEX_S_PMON_MM_CFG_EN) {
- reg1->config = event->attr.config1;
- reg2->config = event->attr.config2;
- } else {
- reg1->config = ~0ULL;
- reg2->config = ~0ULL;
- }
+ /* only TO_R_PROG_EV event uses the match/mask register */
+ if ((hwc->config & NHMEX_PMON_CTL_EV_SEL_MASK) !=
+ NHMEX_S_EVENT_TO_R_PROG_EV)
+ return 0;
if (box->pmu->pmu_idx == 0)
reg1->reg = NHMEX_S0_MSR_MM_CFG;
else
reg1->reg = NHMEX_S1_MSR_MM_CFG;
-
reg1->idx = 0;
-
+ reg1->config = event->attr.config1;
+ reg2->config = event->attr.config2;
return 0;
}
@@ -1059,8 +1061,8 @@
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- wrmsrl(reg1->reg, 0);
- if (reg1->config != ~0ULL || reg2->config != ~0ULL) {
+ if (reg1->idx != EXTRA_REG_NONE) {
+ wrmsrl(reg1->reg, 0);
wrmsrl(reg1->reg + 1, reg1->config);
wrmsrl(reg1->reg + 2, reg2->config);
wrmsrl(reg1->reg, NHMEX_S_PMON_MM_CFG_EN);
@@ -1074,7 +1076,6 @@
&format_attr_edge.attr,
&format_attr_inv.attr,
&format_attr_thresh8.attr,
- &format_attr_mm_cfg.attr,
&format_attr_match.attr,
&format_attr_mask.attr,
NULL,
@@ -1142,6 +1143,9 @@
EVENT_EXTRA_END
};
+/* Nehalem-EX or Westmere-EX ? */
+bool uncore_nhmex;
+
static bool nhmex_mbox_get_shared_reg(struct intel_uncore_box *box, int idx, u64 config)
{
struct intel_uncore_extra_reg *er;
@@ -1171,18 +1175,29 @@
return false;
/* mask of the shared fields */
- mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK;
+ if (uncore_nhmex)
+ mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK;
+ else
+ mask = WSMEX_M_PMON_ZDP_CTL_FVC_MASK;
er = &box->shared_regs[EXTRA_REG_NHMEX_M_ZDP_CTL_FVC];
raw_spin_lock_irqsave(&er->lock, flags);
/* add mask of the non-shared field if it's in use */
- if (__BITS_VALUE(atomic_read(&er->ref), idx, 8))
- mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (__BITS_VALUE(atomic_read(&er->ref), idx, 8)) {
+ if (uncore_nhmex)
+ mask |= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ mask |= WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ }
if (!atomic_read(&er->ref) || !((er->config ^ config) & mask)) {
atomic_add(1 << (idx * 8), &er->ref);
- mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK |
- NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (uncore_nhmex)
+ mask = NHMEX_M_PMON_ZDP_CTL_FVC_MASK |
+ NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ mask = WSMEX_M_PMON_ZDP_CTL_FVC_MASK |
+ WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
er->config &= ~mask;
er->config |= (config & mask);
ret = true;
@@ -1216,7 +1231,10 @@
/* get the non-shared control bits and shift them */
idx = orig_idx - EXTRA_REG_NHMEX_M_ZDP_CTL_FVC;
- config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ if (uncore_nhmex)
+ config &= NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
+ else
+ config &= WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(idx);
if (new_idx > orig_idx) {
idx = new_idx - orig_idx;
config <<= 3 * idx;
@@ -1226,6 +1244,10 @@
}
/* add the shared control bits back */
+ if (uncore_nhmex)
+ config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
+ else
+ config |= WSMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
config |= NHMEX_M_PMON_ZDP_CTL_FVC_MASK & reg1->config;
if (modify) {
/* adjust the main event selector */
@@ -1264,7 +1286,8 @@
}
/* for the match/mask registers */
- if ((uncore_box_is_fake(box) || !reg2->alloc) &&
+ if (reg2->idx != EXTRA_REG_NONE &&
+ (uncore_box_is_fake(box) || !reg2->alloc) &&
!nhmex_mbox_get_shared_reg(box, reg2->idx, reg2->config))
goto fail;
@@ -1278,7 +1301,8 @@
if (idx[0] != 0xff && idx[0] != __BITS_VALUE(reg1->idx, 0, 8))
nhmex_mbox_alter_er(event, idx[0], true);
reg1->alloc |= alloc;
- reg2->alloc = 1;
+ if (reg2->idx != EXTRA_REG_NONE)
+ reg2->alloc = 1;
}
return NULL;
fail:
@@ -1342,9 +1366,6 @@
struct extra_reg *er;
unsigned msr;
int reg_idx = 0;
-
- if (WARN_ON_ONCE(reg1->idx != -1))
- return -EINVAL;
/*
* The mbox events may require 2 extra MSRs at the most. But only
* the lower 32 bits in these MSRs are significant, so we can use
@@ -1355,11 +1376,6 @@
continue;
if (event->attr.config1 & ~er->valid_mask)
return -EINVAL;
- if (er->idx == __BITS_VALUE(reg1->idx, 0, 8) ||
- er->idx == __BITS_VALUE(reg1->idx, 1, 8))
- continue;
- if (WARN_ON_ONCE(reg_idx >= 2))
- return -EINVAL;
msr = er->msr + type->msr_offset * box->pmu->pmu_idx;
if (WARN_ON_ONCE(msr >= 0xffff || er->idx >= 0xff))
@@ -1368,6 +1384,8 @@
/* always use the 32~63 bits to pass the PLD config */
if (er->idx == EXTRA_REG_NHMEX_M_PLD)
reg_idx = 1;
+ else if (WARN_ON_ONCE(reg_idx > 0))
+ return -EINVAL;
reg1->idx &= ~(0xff << (reg_idx * 8));
reg1->reg &= ~(0xffff << (reg_idx * 16));
@@ -1376,17 +1394,21 @@
reg1->config = event->attr.config1;
reg_idx++;
}
- /* use config2 to pass the filter config */
- reg2->idx = EXTRA_REG_NHMEX_M_FILTER;
- if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN)
- reg2->config = event->attr.config2;
- else
- reg2->config = ~0ULL;
- if (box->pmu->pmu_idx == 0)
- reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG;
- else
- reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG;
-
+ /*
+ * The mbox only provides ability to perform address matching
+ * for the PLD events.
+ */
+ if (reg_idx == 2) {
+ reg2->idx = EXTRA_REG_NHMEX_M_FILTER;
+ if (event->attr.config2 & NHMEX_M_PMON_MM_CFG_EN)
+ reg2->config = event->attr.config2;
+ else
+ reg2->config = ~0ULL;
+ if (box->pmu->pmu_idx == 0)
+ reg2->reg = NHMEX_M0_MSR_PMU_MM_CFG;
+ else
+ reg2->reg = NHMEX_M1_MSR_PMU_MM_CFG;
+ }
return 0;
}
@@ -1422,34 +1444,36 @@
wrmsrl(__BITS_VALUE(reg1->reg, 1, 16),
nhmex_mbox_shared_reg_config(box, idx));
- wrmsrl(reg2->reg, 0);
- if (reg2->config != ~0ULL) {
- wrmsrl(reg2->reg + 1,
- reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
- wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
- (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
- wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
+ if (reg2->idx != EXTRA_REG_NONE) {
+ wrmsrl(reg2->reg, 0);
+ if (reg2->config != ~0ULL) {
+ wrmsrl(reg2->reg + 1,
+ reg2->config & NHMEX_M_PMON_ADDR_MATCH_MASK);
+ wrmsrl(reg2->reg + 2, NHMEX_M_PMON_ADDR_MASK_MASK &
+ (reg2->config >> NHMEX_M_PMON_ADDR_MASK_SHIFT));
+ wrmsrl(reg2->reg, NHMEX_M_PMON_MM_CFG_EN);
+ }
}
wrmsrl(hwc->config_base, hwc->config | NHMEX_PMON_CTL_EN_BIT0);
}
-DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
-DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode, "config:4-5");
-DEFINE_UNCORE_FORMAT_ATTR(wrap_mode, wrap_mode, "config:6");
-DEFINE_UNCORE_FORMAT_ATTR(flag_mode, flag_mode, "config:7");
-DEFINE_UNCORE_FORMAT_ATTR(inc_sel, inc_sel, "config:9-13");
-DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel, set_flag_sel, "config:19-21");
-DEFINE_UNCORE_FORMAT_ATTR(filter_cfg, filter_cfg, "config2:63");
-DEFINE_UNCORE_FORMAT_ATTR(filter_match, filter_match, "config2:0-33");
-DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask, "config2:34-61");
-DEFINE_UNCORE_FORMAT_ATTR(dsp, dsp, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(thr, thr, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(fvc, fvc, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(pgt, pgt, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(map, map, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(iss, iss, "config1:0-31");
-DEFINE_UNCORE_FORMAT_ATTR(pld, pld, "config1:32-63");
+DEFINE_UNCORE_FORMAT_ATTR(count_mode, count_mode, "config:2-3");
+DEFINE_UNCORE_FORMAT_ATTR(storage_mode, storage_mode, "config:4-5");
+DEFINE_UNCORE_FORMAT_ATTR(wrap_mode, wrap_mode, "config:6");
+DEFINE_UNCORE_FORMAT_ATTR(flag_mode, flag_mode, "config:7");
+DEFINE_UNCORE_FORMAT_ATTR(inc_sel, inc_sel, "config:9-13");
+DEFINE_UNCORE_FORMAT_ATTR(set_flag_sel, set_flag_sel, "config:19-21");
+DEFINE_UNCORE_FORMAT_ATTR(filter_cfg_en, filter_cfg_en, "config2:63");
+DEFINE_UNCORE_FORMAT_ATTR(filter_match, filter_match, "config2:0-33");
+DEFINE_UNCORE_FORMAT_ATTR(filter_mask, filter_mask, "config2:34-61");
+DEFINE_UNCORE_FORMAT_ATTR(dsp, dsp, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(thr, thr, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(fvc, fvc, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pgt, pgt, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(map, map, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(iss, iss, "config1:0-31");
+DEFINE_UNCORE_FORMAT_ATTR(pld, pld, "config1:32-63");
static struct attribute *nhmex_uncore_mbox_formats_attr[] = {
&format_attr_count_mode.attr,
@@ -1458,7 +1482,7 @@
&format_attr_flag_mode.attr,
&format_attr_inc_sel.attr,
&format_attr_set_flag_sel.attr,
- &format_attr_filter_cfg.attr,
+ &format_attr_filter_cfg_en.attr,
&format_attr_filter_match.attr,
&format_attr_filter_mask.attr,
&format_attr_dsp.attr,
@@ -1482,6 +1506,12 @@
{ /* end: all zeroes */ },
};
+static struct uncore_event_desc wsmex_uncore_mbox_events[] = {
+ INTEL_UNCORE_EVENT_DESC(bbox_cmds_read, "inc_sel=0xd,fvc=0x5000"),
+ INTEL_UNCORE_EVENT_DESC(bbox_cmds_write, "inc_sel=0xd,fvc=0x5040"),
+ { /* end: all zeroes */ },
+};
+
static struct intel_uncore_ops nhmex_uncore_mbox_ops = {
NHMEX_UNCORE_OPS_COMMON_INIT(),
.enable_event = nhmex_mbox_msr_enable_event,
@@ -1513,7 +1543,7 @@
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
int port;
- /* adjust the main event selector */
+ /* adjust the main event selector and extra register index */
if (reg1->idx % 2) {
reg1->idx--;
hwc->config -= 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
@@ -1522,29 +1552,17 @@
hwc->config += 1 << NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
}
- /* adjust address or config of extra register */
+ /* adjust extra register config */
port = reg1->idx / 6 + box->pmu->pmu_idx * 4;
switch (reg1->idx % 6) {
- case 0:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
- break;
- case 1:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
- break;
case 2:
- /* the 8~15 bits to the 0~7 bits */
+ /* shift the 8~15 bits to the 0~7 bits */
reg1->config >>= 8;
break;
case 3:
- /* the 0~7 bits to the 8~15 bits */
+ /* shift the 0~7 bits to the 8~15 bits */
reg1->config <<= 8;
break;
- case 4:
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
- break;
- case 5:
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
- break;
};
}
@@ -1671,7 +1689,7 @@
struct hw_perf_event *hwc = &event->hw;
struct hw_perf_event_extra *reg1 = &event->hw.extra_reg;
struct hw_perf_event_extra *reg2 = &event->hw.branch_reg;
- int port, idx;
+ int idx;
idx = (event->hw.config & NHMEX_R_PMON_CTL_EV_SEL_MASK) >>
NHMEX_R_PMON_CTL_EV_SEL_SHIFT;
@@ -1681,27 +1699,11 @@
reg1->idx = idx;
reg1->config = event->attr.config1;
- port = idx / 6 + box->pmu->pmu_idx * 4;
- idx %= 6;
- switch (idx) {
- case 0:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG0(port);
- break;
- case 1:
- reg1->reg = NHMEX_R_MSR_PORTN_IPERF_CFG1(port);
- break;
- case 2:
- case 3:
- reg1->reg = NHMEX_R_MSR_PORTN_QLX_CFG(port);
- break;
+ switch (idx % 6) {
case 4:
case 5:
- if (idx == 4)
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port);
- else
- reg1->reg = NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port);
- reg2->config = event->attr.config2;
hwc->config |= event->attr.config & (~0ULL << 32);
+ reg2->config = event->attr.config2;
break;
};
return 0;
@@ -1727,28 +1729,34 @@
struct hw_perf_event *hwc = &event->hw;
struct hw_perf_event_extra *reg1 = &hwc->extra_reg;
struct hw_perf_event_extra *reg2 = &hwc->branch_reg;
- int idx, er_idx;
+ int idx, port;
- idx = reg1->idx % 6;
- er_idx = idx;
- if (er_idx > 2)
- er_idx--;
- er_idx += (reg1->idx / 6) * 5;
+ idx = reg1->idx;
+ port = idx / 6 + box->pmu->pmu_idx * 4;
- switch (idx) {
+ switch (idx % 6) {
case 0:
+ wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG0(port), reg1->config);
+ break;
case 1:
- wrmsrl(reg1->reg, reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_IPERF_CFG1(port), reg1->config);
break;
case 2:
case 3:
- wrmsrl(reg1->reg, nhmex_rbox_shared_reg_config(box, er_idx));
+ wrmsrl(NHMEX_R_MSR_PORTN_QLX_CFG(port),
+ nhmex_rbox_shared_reg_config(box, 2 + (idx / 6) * 5));
break;
case 4:
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MM_CFG(port),
+ hwc->config >> 32);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MATCH(port), reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET1_MASK(port), reg2->config);
+ break;
case 5:
- wrmsrl(reg1->reg, reg1->config);
- wrmsrl(reg1->reg + 1, hwc->config >> 32);
- wrmsrl(reg1->reg + 2, reg2->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MM_CFG(port),
+ hwc->config >> 32);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MATCH(port), reg1->config);
+ wrmsrl(NHMEX_R_MSR_PORTN_XBR_SET2_MASK(port), reg2->config);
break;
};
@@ -1756,8 +1764,8 @@
(hwc->config & NHMEX_R_PMON_CTL_EV_SEL_MASK));
}
-DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config:32-63");
-DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config1:0-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_mm_cfg, xbr_mm_cfg, "config:32-63");
+DEFINE_UNCORE_FORMAT_ATTR(xbr_match, xbr_match, "config1:0-63");
DEFINE_UNCORE_FORMAT_ATTR(xbr_mask, xbr_mask, "config2:0-63");
DEFINE_UNCORE_FORMAT_ATTR(qlx_cfg, qlx_cfg, "config1:0-15");
DEFINE_UNCORE_FORMAT_ATTR(iperf_cfg, iperf_cfg, "config1:0-31");
@@ -2303,6 +2311,7 @@
event->hw.idx = -1;
event->hw.last_tag = ~0ULL;
event->hw.extra_reg.idx = EXTRA_REG_NONE;
+ event->hw.branch_reg.idx = EXTRA_REG_NONE;
if (event->attr.config == UNCORE_FIXED_EVENT) {
/* no fixed counter */
@@ -2373,7 +2382,7 @@
type->attr_groups[1] = NULL;
}
-static void uncore_types_exit(struct intel_uncore_type **types)
+static void __init uncore_types_exit(struct intel_uncore_type **types)
{
int i;
for (i = 0; types[i]; i++)
@@ -2814,7 +2823,13 @@
snbep_uncore_cbox.num_boxes = max_cores;
msr_uncores = snbep_msr_uncores;
break;
- case 46:
+ case 46: /* Nehalem-EX */
+ uncore_nhmex = true;
+ case 47: /* Westmere-EX aka. Xeon E7 */
+ if (!uncore_nhmex)
+ nhmex_uncore_mbox.event_descs = wsmex_uncore_mbox_events;
+ if (nhmex_uncore_cbox.num_boxes > max_cores)
+ nhmex_uncore_cbox.num_boxes = max_cores;
msr_uncores = nhmex_msr_uncores;
break;
default:
diff --git a/arch/x86/kernel/cpu/perf_event_intel_uncore.h b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
index f385189..5b81c18 100644
--- a/arch/x86/kernel/cpu/perf_event_intel_uncore.h
+++ b/arch/x86/kernel/cpu/perf_event_intel_uncore.h
@@ -5,7 +5,7 @@
#include "perf_event.h"
#define UNCORE_PMU_NAME_LEN 32
-#define UNCORE_PMU_HRTIMER_INTERVAL (60 * NSEC_PER_SEC)
+#define UNCORE_PMU_HRTIMER_INTERVAL (60LL * NSEC_PER_SEC)
#define UNCORE_FIXED_EVENT 0xff
#define UNCORE_PMC_IDX_MAX_GENERIC 8
@@ -230,6 +230,7 @@
#define NHMEX_S1_MSR_MASK 0xe5a
#define NHMEX_S_PMON_MM_CFG_EN (0x1ULL << 63)
+#define NHMEX_S_EVENT_TO_R_PROG_EV 0
/* NHM-EX Mbox */
#define NHMEX_M0_MSR_GLOBAL_CTL 0xca0
@@ -275,18 +276,12 @@
NHMEX_M_PMON_CTL_INC_SEL_MASK | \
NHMEX_M_PMON_CTL_SET_FLAG_SEL_MASK)
-
-#define NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK 0x1f
-#define NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK (0x7 << 5)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK (0x7 << 8)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR (1 << 23)
-#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK \
- (NHMEX_M_PMON_ZDP_CTL_FVC_FVID_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_BCMD_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_RSP_MASK | \
- NHMEX_M_PMON_ZDP_CTL_FVC_PBOX_INIT_ERR)
+#define NHMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 11) - 1) | (1 << 23))
#define NHMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (11 + 3 * (n)))
+#define WSMEX_M_PMON_ZDP_CTL_FVC_MASK (((1 << 12) - 1) | (1 << 24))
+#define WSMEX_M_PMON_ZDP_CTL_FVC_EVENT_MASK(n) (0x7 << (12 + 3 * (n)))
+
/*
* use the 9~13 bits to select event If the 7th bit is not set,
* otherwise use the 19~21 bits to select event.
@@ -368,6 +363,7 @@
unsigned num_shared_regs:8;
unsigned single_fixed:1;
unsigned pair_ctr_ctl:1;
+ unsigned *msr_offsets;
struct event_constraint unconstrainted;
struct event_constraint *constraints;
struct intel_uncore_pmu *pmus;
@@ -485,29 +481,31 @@
return idx * 8 + box->pmu->type->perf_ctr;
}
-static inline
-unsigned uncore_msr_box_ctl(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_box_offset(struct intel_uncore_box *box)
+{
+ struct intel_uncore_pmu *pmu = box->pmu;
+ return pmu->type->msr_offsets ?
+ pmu->type->msr_offsets[pmu->pmu_idx] :
+ pmu->type->msr_offset * pmu->pmu_idx;
+}
+
+static inline unsigned uncore_msr_box_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->box_ctl)
return 0;
- return box->pmu->type->box_ctl +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->box_ctl + uncore_msr_box_offset(box);
}
-static inline
-unsigned uncore_msr_fixed_ctl(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_fixed_ctl(struct intel_uncore_box *box)
{
if (!box->pmu->type->fixed_ctl)
return 0;
- return box->pmu->type->fixed_ctl +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->fixed_ctl + uncore_msr_box_offset(box);
}
-static inline
-unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
+static inline unsigned uncore_msr_fixed_ctr(struct intel_uncore_box *box)
{
- return box->pmu->type->fixed_ctr +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ return box->pmu->type->fixed_ctr + uncore_msr_box_offset(box);
}
static inline
@@ -515,7 +513,7 @@
{
return box->pmu->type->event_ctl +
(box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ uncore_msr_box_offset(box);
}
static inline
@@ -523,7 +521,7 @@
{
return box->pmu->type->perf_ctr +
(box->pmu->type->pair_ctr_ctl ? 2 * idx : idx) +
- box->pmu->type->msr_offset * box->pmu->pmu_idx;
+ uncore_msr_box_offset(box);
}
static inline
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 1f5f1d5..7ad683d 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -328,6 +328,7 @@
chip->irq_retrigger(data);
raw_spin_unlock(&desc->lock);
}
+ __this_cpu_write(vector_irq[vector], -1);
}
}
#endif
diff --git a/arch/x86/kernel/kdebugfs.c b/arch/x86/kernel/kdebugfs.c
index 1d5d31e..dc1404b 100644
--- a/arch/x86/kernel/kdebugfs.c
+++ b/arch/x86/kernel/kdebugfs.c
@@ -107,7 +107,7 @@
{
struct setup_data_node *node;
struct setup_data *data;
- int error = -ENOMEM;
+ int error;
struct dentry *d;
struct page *pg;
u64 pa_data;
@@ -121,8 +121,10 @@
while (pa_data) {
node = kmalloc(sizeof(*node), GFP_KERNEL);
- if (!node)
+ if (!node) {
+ error = -ENOMEM;
goto err_dir;
+ }
pg = pfn_to_page((pa_data+sizeof(*data)-1) >> PAGE_SHIFT);
if (PageHighMem(pg)) {
diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c
index 1df8fb9..e498b18 100644
--- a/arch/x86/kvm/i8259.c
+++ b/arch/x86/kvm/i8259.c
@@ -316,6 +316,11 @@
addr &= 1;
if (addr == 0) {
if (val & 0x10) {
+ u8 edge_irr = s->irr & ~s->elcr;
+ int i;
+ bool found;
+ struct kvm_vcpu *vcpu;
+
s->init4 = val & 1;
s->last_irr = 0;
s->irr &= s->elcr;
@@ -333,6 +338,18 @@
if (val & 0x08)
pr_pic_unimpl(
"level sensitive irq not supported");
+
+ kvm_for_each_vcpu(i, vcpu, s->pics_state->kvm)
+ if (kvm_apic_accept_pic_intr(vcpu)) {
+ found = true;
+ break;
+ }
+
+
+ if (found)
+ for (irq = 0; irq < PIC_NUM_PINS/2; irq++)
+ if (edge_irr & (1 << irq))
+ pic_clear_isr(s, irq);
} else if (val & 0x08) {
if (val & 0x04)
s->poll = 1;
diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c
index c39b607..c00f03d 100644
--- a/arch/x86/kvm/vmx.c
+++ b/arch/x86/kvm/vmx.c
@@ -1488,13 +1488,6 @@
loadsegment(ds, vmx->host_state.ds_sel);
loadsegment(es, vmx->host_state.es_sel);
}
-#else
- /*
- * The sysexit path does not restore ds/es, so we must set them to
- * a reasonable value ourselves.
- */
- loadsegment(ds, __USER_DS);
- loadsegment(es, __USER_DS);
#endif
reload_tss();
#ifdef CONFIG_X86_64
@@ -6370,6 +6363,19 @@
#endif
);
+#ifndef CONFIG_X86_64
+ /*
+ * The sysexit path does not restore ds/es, so we must set them to
+ * a reasonable value ourselves.
+ *
+ * We can't defer this to vmx_load_host_state() since that function
+ * may be executed in interrupt context, which saves and restore segments
+ * around it, nullifying its effect.
+ */
+ loadsegment(ds, __USER_DS);
+ loadsegment(es, __USER_DS);
+#endif
+
vcpu->arch.regs_avail = ~((1 << VCPU_REGS_RIP) | (1 << VCPU_REGS_RSP)
| (1 << VCPU_EXREG_RFLAGS)
| (1 << VCPU_EXREG_CPL)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 59b5950..42bce48 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -925,6 +925,10 @@
*/
getboottime(&boot);
+ if (kvm->arch.kvmclock_offset) {
+ struct timespec ts = ns_to_timespec(kvm->arch.kvmclock_offset);
+ boot = timespec_sub(boot, ts);
+ }
wc.sec = boot.tv_sec;
wc.nsec = boot.tv_nsec;
wc.version = version;
diff --git a/arch/x86/mm/hugetlbpage.c b/arch/x86/mm/hugetlbpage.c
index f6679a7..b91e485 100644
--- a/arch/x86/mm/hugetlbpage.c
+++ b/arch/x86/mm/hugetlbpage.c
@@ -56,9 +56,16 @@
}
/*
- * search for a shareable pmd page for hugetlb.
+ * Search for a shareable pmd page for hugetlb. In any case calls pmd_alloc()
+ * and returns the corresponding pte. While this is not necessary for the
+ * !shared pmd case because we can allocate the pmd later as well, it makes the
+ * code much cleaner. pmd allocation is essential for the shared case because
+ * pud has to be populated inside the same i_mmap_mutex section - otherwise
+ * racing tasks could either miss the sharing (see huge_pte_offset) or select a
+ * bad pmd for sharing.
*/
-static void huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
+static pte_t *
+huge_pmd_share(struct mm_struct *mm, unsigned long addr, pud_t *pud)
{
struct vm_area_struct *vma = find_vma(mm, addr);
struct address_space *mapping = vma->vm_file->f_mapping;
@@ -68,9 +75,10 @@
struct vm_area_struct *svma;
unsigned long saddr;
pte_t *spte = NULL;
+ pte_t *pte;
if (!vma_shareable(vma, addr))
- return;
+ return (pte_t *)pmd_alloc(mm, pud, addr);
mutex_lock(&mapping->i_mmap_mutex);
vma_prio_tree_foreach(svma, &iter, &mapping->i_mmap, idx, idx) {
@@ -97,7 +105,9 @@
put_page(virt_to_page(spte));
spin_unlock(&mm->page_table_lock);
out:
+ pte = (pte_t *)pmd_alloc(mm, pud, addr);
mutex_unlock(&mapping->i_mmap_mutex);
+ return pte;
}
/*
@@ -142,8 +152,9 @@
} else {
BUG_ON(sz != PMD_SIZE);
if (pud_none(*pud))
- huge_pmd_share(mm, addr, pud);
- pte = (pte_t *) pmd_alloc(mm, pud, addr);
+ pte = huge_pmd_share(mm, addr, pud);
+ else
+ pte = (pte_t *)pmd_alloc(mm, pud, addr);
}
}
BUG_ON(pte && !pte_none(*pte) && !pte_huge(*pte));
diff --git a/arch/x86/mm/pageattr.c b/arch/x86/mm/pageattr.c
index 931930a..a718e0d 100644
--- a/arch/x86/mm/pageattr.c
+++ b/arch/x86/mm/pageattr.c
@@ -919,13 +919,11 @@
/*
* On success we use clflush, when the CPU supports it to
- * avoid the wbindv. If the CPU does not support it, in the
- * error case, and during early boot (for EFI) we fall back
- * to cpa_flush_all (which uses wbinvd):
+ * avoid the wbindv. If the CPU does not support it and in the
+ * error case we fall back to cpa_flush_all (which uses
+ * wbindv):
*/
- if (early_boot_irqs_disabled)
- __cpa_flush_all((void *)(long)cache);
- else if (!ret && cpu_has_clflush) {
+ if (!ret && cpu_has_clflush) {
if (cpa.flags & (CPA_PAGES_ARRAY | CPA_ARRAY)) {
cpa_flush_array(addr, numpages, cache,
cpa.flags, pages);
diff --git a/arch/x86/mm/srat.c b/arch/x86/mm/srat.c
index 4599c3e..4ddf497 100644
--- a/arch/x86/mm/srat.c
+++ b/arch/x86/mm/srat.c
@@ -142,23 +142,23 @@
#endif
/* Callback for parsing of the Proximity Domain <-> Memory Area mappings */
-void __init
+int __init
acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
{
u64 start, end;
int node, pxm;
if (srat_disabled())
- return;
+ return -1;
if (ma->header.length != sizeof(struct acpi_srat_mem_affinity)) {
bad_srat();
- return;
+ return -1;
}
if ((ma->flags & ACPI_SRAT_MEM_ENABLED) == 0)
- return;
+ return -1;
if ((ma->flags & ACPI_SRAT_MEM_HOT_PLUGGABLE) && !save_add_info())
- return;
+ return -1;
start = ma->base_address;
end = start + ma->length;
pxm = ma->proximity_domain;
@@ -168,12 +168,12 @@
if (node < 0) {
printk(KERN_ERR "SRAT: Too many proximity domains.\n");
bad_srat();
- return;
+ return -1;
}
if (numa_add_memblk(node, start, end) < 0) {
bad_srat();
- return;
+ return -1;
}
node_set(node, numa_nodes_parsed);
@@ -181,6 +181,7 @@
printk(KERN_INFO "SRAT: Node %u PXM %u [mem %#010Lx-%#010Lx]\n",
node, pxm,
(unsigned long long) start, (unsigned long long) end - 1);
+ return 0;
}
void __init acpi_numa_arch_fixup(void) {}
diff --git a/arch/x86/platform/efi/efi.c b/arch/x86/platform/efi/efi.c
index 2dc29f5..92660eda 100644
--- a/arch/x86/platform/efi/efi.c
+++ b/arch/x86/platform/efi/efi.c
@@ -234,7 +234,22 @@
return status;
}
-static int efi_set_rtc_mmss(unsigned long nowtime)
+static efi_status_t __init phys_efi_get_time(efi_time_t *tm,
+ efi_time_cap_t *tc)
+{
+ unsigned long flags;
+ efi_status_t status;
+
+ spin_lock_irqsave(&rtc_lock, flags);
+ efi_call_phys_prelog();
+ status = efi_call_phys2(efi_phys.get_time, virt_to_phys(tm),
+ virt_to_phys(tc));
+ efi_call_phys_epilog();
+ spin_unlock_irqrestore(&rtc_lock, flags);
+ return status;
+}
+
+int efi_set_rtc_mmss(unsigned long nowtime)
{
int real_seconds, real_minutes;
efi_status_t status;
@@ -263,7 +278,7 @@
return 0;
}
-static unsigned long efi_get_time(void)
+unsigned long efi_get_time(void)
{
efi_status_t status;
efi_time_t eft;
@@ -606,13 +621,18 @@
}
/*
* We will only need *early* access to the following
- * EFI runtime service before set_virtual_address_map
+ * two EFI runtime services before set_virtual_address_map
* is invoked.
*/
+ efi_phys.get_time = (efi_get_time_t *)runtime->get_time;
efi_phys.set_virtual_address_map =
(efi_set_virtual_address_map_t *)
runtime->set_virtual_address_map;
-
+ /*
+ * Make efi_get_time can be called before entering
+ * virtual mode.
+ */
+ efi.get_time = phys_efi_get_time;
early_iounmap(runtime, sizeof(efi_runtime_services_t));
return 0;
@@ -700,10 +720,12 @@
efi_enabled = 0;
return;
}
+#ifdef CONFIG_X86_32
if (efi_native) {
x86_platform.get_wallclock = efi_get_time;
x86_platform.set_wallclock = efi_set_rtc_mmss;
}
+#endif
#if EFI_DEBUG
print_efi_memmap();
diff --git a/arch/x86/realmode/rm/Makefile b/arch/x86/realmode/rm/Makefile
index b2d534c..8869287 100644
--- a/arch/x86/realmode/rm/Makefile
+++ b/arch/x86/realmode/rm/Makefile
@@ -72,7 +72,7 @@
-Wall -Wstrict-prototypes \
-march=i386 -mregparm=3 \
-include $(srctree)/$(src)/../../boot/code16gcc.h \
- -fno-strict-aliasing -fomit-frame-pointer \
+ -fno-strict-aliasing -fomit-frame-pointer -fno-pic \
$(call cc-option, -ffreestanding) \
$(call cc-option, -fno-toplevel-reorder,\
$(call cc-option, -fno-unit-at-a-time)) \
diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl
index 51171ae..a582bfe 100644
--- a/arch/x86/syscalls/syscall_64.tbl
+++ b/arch/x86/syscalls/syscall_64.tbl
@@ -60,8 +60,8 @@
51 common getsockname sys_getsockname
52 common getpeername sys_getpeername
53 common socketpair sys_socketpair
-54 common setsockopt sys_setsockopt
-55 common getsockopt sys_getsockopt
+54 64 setsockopt sys_setsockopt
+55 64 getsockopt sys_getsockopt
56 common clone stub_clone
57 common fork stub_fork
58 common vfork stub_vfork
@@ -318,7 +318,7 @@
309 common getcpu sys_getcpu
310 64 process_vm_readv sys_process_vm_readv
311 64 process_vm_writev sys_process_vm_writev
-312 64 kcmp sys_kcmp
+312 common kcmp sys_kcmp
#
# x32-specific system call numbers start at 512 to avoid cache impact
@@ -353,3 +353,5 @@
538 x32 sendmmsg compat_sys_sendmmsg
539 x32 process_vm_readv compat_sys_process_vm_readv
540 x32 process_vm_writev compat_sys_process_vm_writev
+541 x32 setsockopt compat_sys_setsockopt
+542 x32 getsockopt compat_sys_getsockopt
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c
index 64effdc6..b2e91d4 100644
--- a/arch/x86/xen/p2m.c
+++ b/arch/x86/xen/p2m.c
@@ -194,6 +194,11 @@
* boundary violation will require three middle nodes. */
RESERVE_BRK(p2m_mid_identity, PAGE_SIZE * 2 * 3);
+/* When we populate back during bootup, the amount of pages can vary. The
+ * max we have is seen is 395979, but that does not mean it can't be more.
+ * But some machines can have 3GB I/O holes even. So lets reserve enough
+ * for 4GB of I/O and E820 holes. */
+RESERVE_BRK(p2m_populated, PMD_SIZE * 4);
static inline unsigned p2m_top_index(unsigned long pfn)
{
BUG_ON(pfn >= MAX_P2M_PFN);
diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c
index ac70341..d5fdd36 100644
--- a/drivers/acpi/ac.c
+++ b/drivers/acpi/ac.c
@@ -69,7 +69,9 @@
};
MODULE_DEVICE_TABLE(acpi, ac_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_ac_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_ac_pm, NULL, acpi_ac_resume);
static struct acpi_driver acpi_ac_driver = {
@@ -313,6 +315,7 @@
return result;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_ac_resume(struct device *dev)
{
struct acpi_ac *ac;
@@ -332,6 +335,7 @@
kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE);
return 0;
}
+#endif
static int acpi_ac_remove(struct acpi_device *device, int type)
{
diff --git a/drivers/acpi/acpica/achware.h b/drivers/acpi/acpica/achware.h
index 5ccb99a..5de4ec7 100644
--- a/drivers/acpi/acpica/achware.h
+++ b/drivers/acpi/acpica/achware.h
@@ -83,22 +83,22 @@
/*
* hwsleep - sleep/wake support (Legacy sleep registers)
*/
-acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state);
-acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state);
-acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_legacy_wake(u8 sleep_state);
/*
* hwesleep - sleep/wake support (Extended FADT-V5 sleep registers)
*/
void acpi_hw_execute_sleep_method(char *method_name, u32 integer_argument);
-acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_sleep(u8 sleep_state);
-acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state);
-acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags);
+acpi_status acpi_hw_extended_wake(u8 sleep_state);
/*
* hwvalid - Port I/O with validation
diff --git a/drivers/acpi/acpica/hwesleep.c b/drivers/acpi/acpica/hwesleep.c
index 48518da..94996f9 100644
--- a/drivers/acpi/acpica/hwesleep.c
+++ b/drivers/acpi/acpica/hwesleep.c
@@ -90,7 +90,6 @@
* FUNCTION: acpi_hw_extended_sleep
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -100,7 +99,7 @@
*
******************************************************************************/
-acpi_status acpi_hw_extended_sleep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_sleep(u8 sleep_state)
{
acpi_status status;
u8 sleep_type_value;
@@ -125,12 +124,6 @@
acpi_gbl_system_awake_and_running = FALSE;
- /* Optionally execute _GTS (Going To Sleep) */
-
- if (flags & ACPI_EXECUTE_GTS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
- }
-
/* Flush caches, as per ACPI specification */
ACPI_FLUSH_CPU_CACHE();
@@ -172,7 +165,6 @@
* FUNCTION: acpi_hw_extended_wake_prep
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - ACPI_EXECUTE_BFS to run optional method
*
* RETURN: Status
*
@@ -181,7 +173,7 @@
*
******************************************************************************/
-acpi_status acpi_hw_extended_wake_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_wake_prep(u8 sleep_state)
{
acpi_status status;
u8 sleep_type_value;
@@ -200,11 +192,6 @@
&acpi_gbl_FADT.sleep_control);
}
- /* Optionally execute _BFS (Back From Sleep) */
-
- if (flags & ACPI_EXECUTE_BFS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
- }
return_ACPI_STATUS(AE_OK);
}
@@ -222,7 +209,7 @@
*
******************************************************************************/
-acpi_status acpi_hw_extended_wake(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_extended_wake(u8 sleep_state)
{
ACPI_FUNCTION_TRACE(hw_extended_wake);
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index 9960fe9..3fddde0 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -56,7 +56,6 @@
* FUNCTION: acpi_hw_legacy_sleep
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -64,7 +63,7 @@
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
-acpi_status acpi_hw_legacy_sleep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_sleep(u8 sleep_state)
{
struct acpi_bit_register_info *sleep_type_reg_info;
struct acpi_bit_register_info *sleep_enable_reg_info;
@@ -110,12 +109,6 @@
return_ACPI_STATUS(status);
}
- /* Optionally execute _GTS (Going To Sleep) */
-
- if (flags & ACPI_EXECUTE_GTS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__GTS, sleep_state);
- }
-
/* Get current value of PM1A control */
status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL,
@@ -214,7 +207,6 @@
* FUNCTION: acpi_hw_legacy_wake_prep
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - ACPI_EXECUTE_BFS to run optional method
*
* RETURN: Status
*
@@ -224,7 +216,7 @@
*
******************************************************************************/
-acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state)
{
acpi_status status;
struct acpi_bit_register_info *sleep_type_reg_info;
@@ -275,11 +267,6 @@
}
}
- /* Optionally execute _BFS (Back From Sleep) */
-
- if (flags & ACPI_EXECUTE_BFS) {
- acpi_hw_execute_sleep_method(METHOD_PATHNAME__BFS, sleep_state);
- }
return_ACPI_STATUS(status);
}
@@ -288,7 +275,6 @@
* FUNCTION: acpi_hw_legacy_wake
*
* PARAMETERS: sleep_state - Which sleep state we just exited
- * flags - Reserved, set to zero
*
* RETURN: Status
*
@@ -297,7 +283,7 @@
*
******************************************************************************/
-acpi_status acpi_hw_legacy_wake(u8 sleep_state, u8 flags)
+acpi_status acpi_hw_legacy_wake(u8 sleep_state)
{
acpi_status status;
diff --git a/drivers/acpi/acpica/hwxfsleep.c b/drivers/acpi/acpica/hwxfsleep.c
index f8684bf..1f165a7 100644
--- a/drivers/acpi/acpica/hwxfsleep.c
+++ b/drivers/acpi/acpica/hwxfsleep.c
@@ -50,7 +50,7 @@
/* Local prototypes */
static acpi_status
-acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id);
+acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id);
/*
* Dispatch table used to efficiently branch to the various sleep
@@ -235,7 +235,7 @@
*
******************************************************************************/
static acpi_status
-acpi_hw_sleep_dispatch(u8 sleep_state, u8 flags, u32 function_id)
+acpi_hw_sleep_dispatch(u8 sleep_state, u32 function_id)
{
acpi_status status;
struct acpi_sleep_functions *sleep_functions =
@@ -248,11 +248,11 @@
* use the extended sleep registers
*/
if (acpi_gbl_reduced_hardware || acpi_gbl_FADT.sleep_control.address) {
- status = sleep_functions->extended_function(sleep_state, flags);
+ status = sleep_functions->extended_function(sleep_state);
} else {
/* Legacy sleep */
- status = sleep_functions->legacy_function(sleep_state, flags);
+ status = sleep_functions->legacy_function(sleep_state);
}
return (status);
@@ -262,7 +262,7 @@
* For the case where reduced-hardware-only code is being generated,
* we know that only the extended sleep registers are available
*/
- status = sleep_functions->extended_function(sleep_state, flags);
+ status = sleep_functions->extended_function(sleep_state);
return (status);
#endif /* !ACPI_REDUCED_HARDWARE */
@@ -349,7 +349,6 @@
* FUNCTION: acpi_enter_sleep_state
*
* PARAMETERS: sleep_state - Which sleep state to enter
- * flags - ACPI_EXECUTE_GTS to run optional method
*
* RETURN: Status
*
@@ -357,7 +356,7 @@
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
*
******************************************************************************/
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags)
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
{
acpi_status status;
@@ -371,7 +370,7 @@
}
status =
- acpi_hw_sleep_dispatch(sleep_state, flags, ACPI_SLEEP_FUNCTION_ID);
+ acpi_hw_sleep_dispatch(sleep_state, ACPI_SLEEP_FUNCTION_ID);
return_ACPI_STATUS(status);
}
@@ -391,14 +390,14 @@
* Called with interrupts DISABLED.
*
******************************************************************************/
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags)
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state)
{
acpi_status status;
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state_prep);
status =
- acpi_hw_sleep_dispatch(sleep_state, flags,
+ acpi_hw_sleep_dispatch(sleep_state,
ACPI_WAKE_PREP_FUNCTION_ID);
return_ACPI_STATUS(status);
}
@@ -423,8 +422,7 @@
ACPI_FUNCTION_TRACE(acpi_leave_sleep_state);
-
- status = acpi_hw_sleep_dispatch(sleep_state, 0, ACPI_WAKE_FUNCTION_ID);
+ status = acpi_hw_sleep_dispatch(sleep_state, ACPI_WAKE_FUNCTION_ID);
return_ACPI_STATUS(status);
}
diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c
index ea4c6d5..29e51bc 100644
--- a/drivers/acpi/acpica/tbxface.c
+++ b/drivers/acpi/acpica/tbxface.c
@@ -387,6 +387,7 @@
return (AE_NOT_FOUND);
}
+ACPI_EXPORT_SYMBOL(acpi_get_table_with_size)
acpi_status
acpi_get_table(char *signature,
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c
index ff2c876..45e3e17 100644
--- a/drivers/acpi/battery.c
+++ b/drivers/acpi/battery.c
@@ -1052,6 +1052,7 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
/* this is needed to learn about changes made in suspended state */
static int acpi_battery_resume(struct device *dev)
{
@@ -1068,6 +1069,7 @@
acpi_battery_update(battery);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_battery_pm, NULL, acpi_battery_resume);
diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c
index 79d4c22..314a3b8 100644
--- a/drivers/acpi/button.c
+++ b/drivers/acpi/button.c
@@ -78,7 +78,9 @@
static int acpi_button_remove(struct acpi_device *device, int type);
static void acpi_button_notify(struct acpi_device *device, u32 event);
+#ifdef CONFIG_PM_SLEEP
static int acpi_button_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_button_pm, NULL, acpi_button_resume);
static struct acpi_driver acpi_button_driver = {
@@ -310,6 +312,7 @@
}
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_button_resume(struct device *dev)
{
struct acpi_device *device = to_acpi_device(dev);
@@ -319,6 +322,7 @@
return acpi_lid_send_state(device);
return 0;
}
+#endif
static int acpi_button_add(struct acpi_device *device)
{
diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c
index 669d9ee..bc36a47 100644
--- a/drivers/acpi/fan.c
+++ b/drivers/acpi/fan.c
@@ -53,8 +53,10 @@
};
MODULE_DEVICE_TABLE(acpi, fan_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_fan_suspend(struct device *dev);
static int acpi_fan_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_fan_pm, acpi_fan_suspend, acpi_fan_resume);
static struct acpi_driver acpi_fan_driver = {
@@ -184,6 +186,7 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_fan_suspend(struct device *dev)
{
if (!dev)
@@ -207,6 +210,7 @@
return result;
}
+#endif
static int __init acpi_fan_init(void)
{
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index e56f3be..cb31298 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -237,6 +237,8 @@
return 0;
}
+static int __initdata parsed_numa_memblks;
+
static int __init
acpi_parse_memory_affinity(struct acpi_subtable_header * header,
const unsigned long end)
@@ -250,8 +252,8 @@
acpi_table_print_srat_entry(header);
/* let architecture-dependent part to do it */
- acpi_numa_memory_affinity_init(memory_affinity);
-
+ if (!acpi_numa_memory_affinity_init(memory_affinity))
+ parsed_numa_memblks++;
return 0;
}
@@ -304,8 +306,10 @@
acpi_numa_arch_fixup();
- if (cnt <= 0)
- return cnt ?: -ENOENT;
+ if (cnt < 0)
+ return cnt;
+ else if (!parsed_numa_memblks)
+ return -ENOENT;
return 0;
}
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index ec54014c..72a2c98 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -573,8 +573,15 @@
OSC_CLOCK_PWR_CAPABILITY_SUPPORT;
if (pci_msi_enabled())
flags |= OSC_MSI_SUPPORT;
- if (flags != base_flags)
- acpi_pci_osc_support(root, flags);
+ if (flags != base_flags) {
+ status = acpi_pci_osc_support(root, flags);
+ if (ACPI_FAILURE(status)) {
+ dev_info(root->bus->bridge, "ACPI _OSC support "
+ "notification failed, disabling PCIe ASPM\n");
+ pcie_no_aspm();
+ flags = base_flags;
+ }
+ }
if (!pcie_ports_disabled
&& (flags & ACPI_PCIE_REQ_SUPPORT) == ACPI_PCIE_REQ_SUPPORT) {
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 215ecd0..fc18034 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -67,7 +67,9 @@
};
MODULE_DEVICE_TABLE(acpi, power_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_power_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_power_pm, NULL, acpi_power_resume);
static struct acpi_driver acpi_power_driver = {
@@ -775,6 +777,7 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_power_resume(struct device *dev)
{
int result = 0, state;
@@ -803,6 +806,7 @@
return result;
}
+#endif
int __init acpi_power_init(void)
{
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index ff8e04f..bfc31cb 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -437,7 +437,7 @@
/* Normal CPU soft online event */
} else {
acpi_processor_ppc_has_changed(pr, 0);
- acpi_processor_cst_has_changed(pr);
+ acpi_processor_hotplug(pr);
acpi_processor_reevaluate_tstate(pr, action);
acpi_processor_tstate_has_changed(pr);
}
diff --git a/drivers/acpi/sbs.c b/drivers/acpi/sbs.c
index c0b9aa5..ff0740e 100644
--- a/drivers/acpi/sbs.c
+++ b/drivers/acpi/sbs.c
@@ -988,6 +988,7 @@
#endif
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_sbs_resume(struct device *dev)
{
struct acpi_sbs *sbs;
@@ -997,6 +998,7 @@
acpi_sbs_callback(sbs);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_sbs_pm, NULL, acpi_sbs_resume);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 7a7a9c9..fdcdbb6 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -28,36 +28,7 @@
#include "internal.h"
#include "sleep.h"
-u8 wake_sleep_flags = ACPI_NO_OPTIONAL_METHODS;
-static unsigned int gts, bfs;
-static int set_param_wake_flag(const char *val, struct kernel_param *kp)
-{
- int ret = param_set_int(val, kp);
-
- if (ret)
- return ret;
-
- if (kp->arg == (const char *)>s) {
- if (gts)
- wake_sleep_flags |= ACPI_EXECUTE_GTS;
- else
- wake_sleep_flags &= ~ACPI_EXECUTE_GTS;
- }
- if (kp->arg == (const char *)&bfs) {
- if (bfs)
- wake_sleep_flags |= ACPI_EXECUTE_BFS;
- else
- wake_sleep_flags &= ~ACPI_EXECUTE_BFS;
- }
- return ret;
-}
-module_param_call(gts, set_param_wake_flag, param_get_int, >s, 0644);
-module_param_call(bfs, set_param_wake_flag, param_get_int, &bfs, 0644);
-MODULE_PARM_DESC(gts, "Enable evaluation of _GTS on suspend.");
-MODULE_PARM_DESC(bfs, "Enable evaluation of _BFS on resume".);
-
static u8 sleep_states[ACPI_S_STATE_COUNT];
-static bool pwr_btn_event_pending;
static void acpi_sleep_tts_switch(u32 acpi_state)
{
@@ -110,6 +81,7 @@
#ifdef CONFIG_ACPI_SLEEP
static u32 acpi_target_sleep_state = ACPI_STATE_S0;
+static bool pwr_btn_event_pending;
/*
* The ACPI specification wants us to save NVS memory regions during hibernation
@@ -305,7 +277,7 @@
switch (acpi_state) {
case ACPI_STATE_S1:
barrier();
- status = acpi_enter_sleep_state(acpi_state, wake_sleep_flags);
+ status = acpi_enter_sleep_state(acpi_state);
break;
case ACPI_STATE_S3:
@@ -319,8 +291,8 @@
/* This violates the spec but is required for bug compatibility. */
acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(acpi_state, wake_sleep_flags);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(acpi_state);
/* ACPI 3.0 specs (P62) says that it's the responsibility
* of the OSPM to clear the status bit [ implying that the
@@ -603,9 +575,9 @@
ACPI_FLUSH_CPU_CACHE();
/* This shouldn't return. If it returns, we have a problem */
- status = acpi_enter_sleep_state(ACPI_STATE_S4, wake_sleep_flags);
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags);
+ status = acpi_enter_sleep_state(ACPI_STATE_S4);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
return ACPI_SUCCESS(status) ? 0 : -EFAULT;
}
@@ -617,8 +589,8 @@
* enable it here.
*/
acpi_enable();
- /* Reprogram control registers and execute _BFS */
- acpi_leave_sleep_state_prep(ACPI_STATE_S4, wake_sleep_flags);
+ /* Reprogram control registers */
+ acpi_leave_sleep_state_prep(ACPI_STATE_S4);
/* Check the hardware signature */
if (facs && s4_hardware_signature != facs->hardware_signature) {
printk(KERN_EMERG "ACPI: Hardware changed while hibernated, "
@@ -892,33 +864,7 @@
/* acpi_sleep_prepare(ACPI_STATE_S5) should have already been called */
printk(KERN_DEBUG "%s called\n", __func__);
local_irq_disable();
- acpi_enter_sleep_state(ACPI_STATE_S5, wake_sleep_flags);
-}
-
-/*
- * ACPI 2.0 created the optional _GTS and _BFS,
- * but industry adoption has been neither rapid nor broad.
- *
- * Linux gets into trouble when it executes poorly validated
- * paths through the BIOS, so disable _GTS and _BFS by default,
- * but do speak up and offer the option to enable them.
- */
-static void __init acpi_gts_bfs_check(void)
-{
- acpi_handle dummy;
-
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__GTS, &dummy)))
- {
- printk(KERN_NOTICE PREFIX "BIOS offers _GTS\n");
- printk(KERN_NOTICE PREFIX "If \"acpi.gts=1\" improves suspend, "
- "please notify linux-acpi@vger.kernel.org\n");
- }
- if (ACPI_SUCCESS(acpi_get_handle(ACPI_ROOT_OBJECT, METHOD_PATHNAME__BFS, &dummy)))
- {
- printk(KERN_NOTICE PREFIX "BIOS offers _BFS\n");
- printk(KERN_NOTICE PREFIX "If \"acpi.bfs=1\" improves resume, "
- "please notify linux-acpi@vger.kernel.org\n");
- }
+ acpi_enter_sleep_state(ACPI_STATE_S5);
}
int __init acpi_sleep_init(void)
@@ -979,6 +925,5 @@
* object can also be evaluated when the system enters S5.
*/
register_reboot_notifier(&tts_notifier);
- acpi_gts_bfs_check();
return 0;
}
diff --git a/drivers/acpi/sysfs.c b/drivers/acpi/sysfs.c
index 240a244..7c3f98b 100644
--- a/drivers/acpi/sysfs.c
+++ b/drivers/acpi/sysfs.c
@@ -173,7 +173,7 @@
{
int result = 0;
- if (!strncmp(val, "enable", strlen("enable"))) {
+ if (!strncmp(val, "enable", sizeof("enable") - 1)) {
result = acpi_debug_trace(trace_method_name, trace_debug_level,
trace_debug_layer, 0);
if (result)
@@ -181,7 +181,7 @@
goto exit;
}
- if (!strncmp(val, "disable", strlen("disable"))) {
+ if (!strncmp(val, "disable", sizeof("disable") - 1)) {
int name = 0;
result = acpi_debug_trace((char *)&name, trace_debug_level,
trace_debug_layer, 0);
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c
index 9fe90e9..edda74a 100644
--- a/drivers/acpi/thermal.c
+++ b/drivers/acpi/thermal.c
@@ -106,7 +106,9 @@
};
MODULE_DEVICE_TABLE(acpi, thermal_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_thermal_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_thermal_pm, NULL, acpi_thermal_resume);
static struct acpi_driver acpi_thermal_driver = {
@@ -1041,6 +1043,7 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_thermal_resume(struct device *dev)
{
struct acpi_thermal *tz;
@@ -1075,6 +1078,7 @@
return AE_OK;
}
+#endif
static int thermal_act(const struct dmi_system_id *d) {
diff --git a/drivers/atm/iphase.c b/drivers/atm/iphase.c
index d438601..96cce6d 100644
--- a/drivers/atm/iphase.c
+++ b/drivers/atm/iphase.c
@@ -2362,7 +2362,7 @@
{
printk(DEV_LABEL " (itf %d): can't set up page mapping\n",
dev->number);
- return error;
+ return -ENOMEM;
}
IF_INIT(printk(DEV_LABEL " (itf %d): rev.%d,base=%p,irq=%d\n",
dev->number, iadev->pci->revision, base, iadev->irq);)
diff --git a/drivers/base/core.c b/drivers/base/core.c
index f338037..5e6e00b 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -1865,6 +1865,7 @@
struct va_format *vaf)
{
char dict[128];
+ const char *level_extra = "";
size_t dictlen = 0;
const char *subsys;
@@ -1911,10 +1912,14 @@
"DEVICE=+%s:%s", subsys, dev_name(dev));
}
skip:
+ if (level[2])
+ level_extra = &level[2]; /* skip past KERN_SOH "L" */
+
return printk_emit(0, level[1] - '0',
dictlen ? dict : NULL, dictlen,
- "%s %s: %pV",
- dev_driver_string(dev), dev_name(dev), vaf);
+ "%s %s: %s%pV",
+ dev_driver_string(dev), dev_name(dev),
+ level_extra, vaf);
}
EXPORT_SYMBOL(__dev_printk);
diff --git a/drivers/base/power/clock_ops.c b/drivers/base/power/clock_ops.c
index 869d7ff..eb78e96 100644
--- a/drivers/base/power/clock_ops.c
+++ b/drivers/base/power/clock_ops.c
@@ -169,8 +169,7 @@
*/
int pm_clk_create(struct device *dev)
{
- int ret = dev_pm_get_subsys_data(dev);
- return ret < 0 ? ret : 0;
+ return dev_pm_get_subsys_data(dev);
}
/**
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index a14085c..39c3252 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -24,7 +24,6 @@
int dev_pm_get_subsys_data(struct device *dev)
{
struct pm_subsys_data *psd;
- int ret = 0;
psd = kzalloc(sizeof(*psd), GFP_KERNEL);
if (!psd)
@@ -40,7 +39,6 @@
dev->power.subsys_data = psd;
pm_clk_init(dev);
psd = NULL;
- ret = 1;
}
spin_unlock_irq(&dev->power.lock);
@@ -48,7 +46,7 @@
/* kfree() verifies that its argument is nonzero. */
kfree(psd);
- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(dev_pm_get_subsys_data);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 5989487..7d9c1cb 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -147,6 +147,8 @@
|| (dev->power.request_pending
&& dev->power.request == RPM_REQ_RESUME))
retval = -EAGAIN;
+ else if (__dev_pm_qos_read_value(dev) < 0)
+ retval = -EPERM;
else if (dev->power.runtime_status == RPM_SUSPENDED)
retval = 1;
@@ -388,7 +390,6 @@
goto repeat;
}
- dev->power.deferred_resume = false;
if (dev->power.no_callbacks)
goto no_callback; /* Assume success. */
@@ -403,12 +404,6 @@
goto out;
}
- if (__dev_pm_qos_read_value(dev) < 0) {
- /* Negative PM QoS constraint means "never suspend". */
- retval = -EPERM;
- goto out;
- }
-
__update_runtime_status(dev, RPM_SUSPENDING);
if (dev->pm_domain)
@@ -440,6 +435,7 @@
wake_up_all(&dev->power.wait_queue);
if (dev->power.deferred_resume) {
+ dev->power.deferred_resume = false;
rpm_resume(dev, 0);
retval = -EAGAIN;
goto out;
@@ -584,6 +580,7 @@
|| dev->parent->power.runtime_status == RPM_ACTIVE) {
atomic_inc(&dev->parent->power.child_count);
spin_unlock(&dev->parent->power.lock);
+ retval = 1;
goto no_callback; /* Assume success. */
}
spin_unlock(&dev->parent->power.lock);
@@ -664,7 +661,7 @@
}
wake_up_all(&dev->power.wait_queue);
- if (!retval)
+ if (retval >= 0)
rpm_idle(dev, RPM_ASYNC);
out:
diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c
index 11b32d2..a6e5672 100644
--- a/drivers/bcma/host_pci.c
+++ b/drivers/bcma/host_pci.c
@@ -272,6 +272,7 @@
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4331) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4353) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4357) },
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) },
{ PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) },
{ 0, },
};
diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c
index 26823d9..9ea4627 100644
--- a/drivers/bcma/sprom.c
+++ b/drivers/bcma/sprom.c
@@ -507,7 +507,9 @@
/* for these chips OTP is always available */
present = true;
break;
-
+ case BCMA_CHIP_ID_BCM43228:
+ present = chip_status & BCMA_CC_CHIPST_43228_OTP_PRESENT;
+ break;
default:
present = false;
break;
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index a796407..f529407 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -353,18 +353,6 @@
Use devices /dev/sx8/$N and /dev/sx8/$Np$M.
-config BLK_DEV_UB
- tristate "Low Performance USB Block driver (deprecated)"
- depends on USB
- help
- This driver supports certain USB attached storage devices
- such as flash keys.
-
- If you enable this driver, it is recommended to avoid conflicts
- with usb-storage by enabling USB_LIBUSUAL.
-
- If unsure, say N.
-
config BLK_DEV_RAM
tristate "RAM block device support"
---help---
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 5b79505..17e82df 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -33,7 +33,6 @@
obj-$(CONFIG_VIODASD) += viodasd.o
obj-$(CONFIG_BLK_DEV_SX8) += sx8.o
-obj-$(CONFIG_BLK_DEV_UB) += ub.o
obj-$(CONFIG_BLK_DEV_HD) += hd.o
obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index acda773..38aa6dd 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -763,16 +763,7 @@
{
case CMD_TARGET_STATUS:
/* Pass it up to the upper layers... */
- if( ei->ScsiStatus)
- {
-#if 0
- printk(KERN_WARNING "cciss: cmd %p "
- "has SCSI Status = %x\n",
- c, ei->ScsiStatus);
-#endif
- cmd->result |= (ei->ScsiStatus << 1);
- }
- else { /* scsi status is zero??? How??? */
+ if (!ei->ScsiStatus) {
/* Ordinarily, this case should never happen, but there is a bug
in some released firmware revisions that allows it to happen
diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c
index 2e0e7fc..dbe6135 100644
--- a/drivers/block/drbd/drbd_main.c
+++ b/drivers/block/drbd/drbd_main.c
@@ -3537,9 +3537,9 @@
}
/**
- * drbd_congested() - Callback for pdflush
+ * drbd_congested() - Callback for the flusher thread
* @congested_data: User data
- * @bdi_bits: Bits pdflush is currently interested in
+ * @bdi_bits: Bits the BDI flusher thread is currently interested in
*
* Returns 1<<BDI_async_congested and/or 1<<BDI_sync_congested if we are congested.
*/
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
deleted file mode 100644
index fcec022..0000000
--- a/drivers/block/ub.c
+++ /dev/null
@@ -1,2474 +0,0 @@
-/*
- * The low performance USB storage driver (ub).
- *
- * Copyright (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
- * Copyright (C) 2004 Pete Zaitcev (zaitcev@yahoo.com)
- *
- * This work is a part of Linux kernel, is derived from it,
- * and is not licensed separately. See file COPYING for details.
- *
- * TODO (sorted by decreasing priority)
- * -- Return sense now that rq allows it (we always auto-sense anyway).
- * -- set readonly flag for CDs, set removable flag for CF readers
- * -- do inquiry and verify we got a disk and not a tape (for LUN mismatch)
- * -- verify the 13 conditions and do bulk resets
- * -- highmem
- * -- move top_sense and work_bcs into separate allocations (if they survive)
- * for cache purists and esoteric architectures.
- * -- Allocate structure for LUN 0 before the first ub_sync_tur, avoid NULL. ?
- * -- prune comments, they are too volumnous
- * -- Resove XXX's
- * -- CLEAR, CLR2STS, CLRRS seem to be ripe for refactoring.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/usb_usual.h>
-#include <linux/blkdev.h>
-#include <linux/timer.h>
-#include <linux/scatterlist.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <scsi/scsi.h>
-
-#define DRV_NAME "ub"
-
-#define UB_MAJOR 180
-
-/*
- * The command state machine is the key model for understanding of this driver.
- *
- * The general rule is that all transitions are done towards the bottom
- * of the diagram, thus preventing any loops.
- *
- * An exception to that is how the STAT state is handled. A counter allows it
- * to be re-entered along the path marked with [C].
- *
- * +--------+
- * ! INIT !
- * +--------+
- * !
- * ub_scsi_cmd_start fails ->--------------------------------------\
- * ! !
- * V !
- * +--------+ !
- * ! CMD ! !
- * +--------+ !
- * ! +--------+ !
- * was -EPIPE -->-------------------------------->! CLEAR ! !
- * ! +--------+ !
- * ! ! !
- * was error -->------------------------------------- ! --------->\
- * ! ! !
- * /--<-- cmd->dir == NONE ? ! !
- * ! ! ! !
- * ! V ! !
- * ! +--------+ ! !
- * ! ! DATA ! ! !
- * ! +--------+ ! !
- * ! ! +---------+ ! !
- * ! was -EPIPE -->--------------->! CLR2STS ! ! !
- * ! ! +---------+ ! !
- * ! ! ! ! !
- * ! ! was error -->---- ! --------->\
- * ! was error -->--------------------- ! ------------- ! --------->\
- * ! ! ! ! !
- * ! V ! ! !
- * \--->+--------+ ! ! !
- * ! STAT !<--------------------------/ ! !
- * /--->+--------+ ! !
- * ! ! ! !
- * [C] was -EPIPE -->-----------\ ! !
- * ! ! ! ! !
- * +<---- len == 0 ! ! !
- * ! ! ! ! !
- * ! was error -->--------------------------------------!---------->\
- * ! ! ! ! !
- * +<---- bad CSW ! ! !
- * +<---- bad tag ! ! !
- * ! ! V ! !
- * ! ! +--------+ ! !
- * ! ! ! CLRRS ! ! !
- * ! ! +--------+ ! !
- * ! ! ! ! !
- * \------- ! --------------------[C]--------\ ! !
- * ! ! ! !
- * cmd->error---\ +--------+ ! !
- * ! +--------------->! SENSE !<----------/ !
- * STAT_FAIL----/ +--------+ !
- * ! ! V
- * ! V +--------+
- * \--------------------------------\--------------------->! DONE !
- * +--------+
- */
-
-/*
- * This many LUNs per USB device.
- * Every one of them takes a host, see UB_MAX_HOSTS.
- */
-#define UB_MAX_LUNS 9
-
-/*
- */
-
-#define UB_PARTS_PER_LUN 8
-
-#define UB_MAX_CDB_SIZE 16 /* Corresponds to Bulk */
-
-#define UB_SENSE_SIZE 18
-
-/*
- */
-struct ub_dev;
-
-#define UB_MAX_REQ_SG 9 /* cdrecord requires 32KB and maybe a header */
-#define UB_MAX_SECTORS 64
-
-/*
- * A second is more than enough for a 32K transfer (UB_MAX_SECTORS)
- * even if a webcam hogs the bus, but some devices need time to spin up.
- */
-#define UB_URB_TIMEOUT (HZ*2)
-#define UB_DATA_TIMEOUT (HZ*5) /* ZIP does spin-ups in the data phase */
-#define UB_STAT_TIMEOUT (HZ*5) /* Same spinups and eject for a dataless cmd. */
-#define UB_CTRL_TIMEOUT (HZ/2) /* 500ms ought to be enough to clear a stall */
-
-/*
- * An instance of a SCSI command in transit.
- */
-#define UB_DIR_NONE 0
-#define UB_DIR_READ 1
-#define UB_DIR_ILLEGAL2 2
-#define UB_DIR_WRITE 3
-
-#define UB_DIR_CHAR(c) (((c)==UB_DIR_WRITE)? 'w': \
- (((c)==UB_DIR_READ)? 'r': 'n'))
-
-enum ub_scsi_cmd_state {
- UB_CMDST_INIT, /* Initial state */
- UB_CMDST_CMD, /* Command submitted */
- UB_CMDST_DATA, /* Data phase */
- UB_CMDST_CLR2STS, /* Clearing before requesting status */
- UB_CMDST_STAT, /* Status phase */
- UB_CMDST_CLEAR, /* Clearing a stall (halt, actually) */
- UB_CMDST_CLRRS, /* Clearing before retrying status */
- UB_CMDST_SENSE, /* Sending Request Sense */
- UB_CMDST_DONE /* Final state */
-};
-
-struct ub_scsi_cmd {
- unsigned char cdb[UB_MAX_CDB_SIZE];
- unsigned char cdb_len;
-
- unsigned char dir; /* 0 - none, 1 - read, 3 - write. */
- enum ub_scsi_cmd_state state;
- unsigned int tag;
- struct ub_scsi_cmd *next;
-
- int error; /* Return code - valid upon done */
- unsigned int act_len; /* Return size */
- unsigned char key, asc, ascq; /* May be valid if error==-EIO */
-
- int stat_count; /* Retries getting status. */
- unsigned int timeo; /* jiffies until rq->timeout changes */
-
- unsigned int len; /* Requested length */
- unsigned int current_sg;
- unsigned int nsg; /* sgv[nsg] */
- struct scatterlist sgv[UB_MAX_REQ_SG];
-
- struct ub_lun *lun;
- void (*done)(struct ub_dev *, struct ub_scsi_cmd *);
- void *back;
-};
-
-struct ub_request {
- struct request *rq;
- unsigned int current_try;
- unsigned int nsg; /* sgv[nsg] */
- struct scatterlist sgv[UB_MAX_REQ_SG];
-};
-
-/*
- */
-struct ub_capacity {
- unsigned long nsec; /* Linux size - 512 byte sectors */
- unsigned int bsize; /* Linux hardsect_size */
- unsigned int bshift; /* Shift between 512 and hard sects */
-};
-
-/*
- * This is a direct take-off from linux/include/completion.h
- * The difference is that I do not wait on this thing, just poll.
- * When I want to wait (ub_probe), I just use the stock completion.
- *
- * Note that INIT_COMPLETION takes no lock. It is correct. But why
- * in the bloody hell that thing takes struct instead of pointer to struct
- * is quite beyond me. I just copied it from the stock completion.
- */
-struct ub_completion {
- unsigned int done;
- spinlock_t lock;
-};
-
-static DEFINE_MUTEX(ub_mutex);
-static inline void ub_init_completion(struct ub_completion *x)
-{
- x->done = 0;
- spin_lock_init(&x->lock);
-}
-
-#define UB_INIT_COMPLETION(x) ((x).done = 0)
-
-static void ub_complete(struct ub_completion *x)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&x->lock, flags);
- x->done++;
- spin_unlock_irqrestore(&x->lock, flags);
-}
-
-static int ub_is_completed(struct ub_completion *x)
-{
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&x->lock, flags);
- ret = x->done;
- spin_unlock_irqrestore(&x->lock, flags);
- return ret;
-}
-
-/*
- */
-struct ub_scsi_cmd_queue {
- int qlen, qmax;
- struct ub_scsi_cmd *head, *tail;
-};
-
-/*
- * The block device instance (one per LUN).
- */
-struct ub_lun {
- struct ub_dev *udev;
- struct list_head link;
- struct gendisk *disk;
- int id; /* Host index */
- int num; /* LUN number */
- char name[16];
-
- int changed; /* Media was changed */
- int removable;
- int readonly;
-
- struct ub_request urq;
-
- /* Use Ingo's mempool if or when we have more than one command. */
- /*
- * Currently we never need more than one command for the whole device.
- * However, giving every LUN a command is a cheap and automatic way
- * to enforce fairness between them.
- */
- int cmda[1];
- struct ub_scsi_cmd cmdv[1];
-
- struct ub_capacity capacity;
-};
-
-/*
- * The USB device instance.
- */
-struct ub_dev {
- spinlock_t *lock;
- atomic_t poison; /* The USB device is disconnected */
- int openc; /* protected by ub_lock! */
- /* kref is too implicit for our taste */
- int reset; /* Reset is running */
- int bad_resid;
- unsigned int tagcnt;
- char name[12];
- struct usb_device *dev;
- struct usb_interface *intf;
-
- struct list_head luns;
-
- unsigned int send_bulk_pipe; /* cached pipe values */
- unsigned int recv_bulk_pipe;
- unsigned int send_ctrl_pipe;
- unsigned int recv_ctrl_pipe;
-
- struct tasklet_struct tasklet;
-
- struct ub_scsi_cmd_queue cmd_queue;
- struct ub_scsi_cmd top_rqs_cmd; /* REQUEST SENSE */
- unsigned char top_sense[UB_SENSE_SIZE];
-
- struct ub_completion work_done;
- struct urb work_urb;
- struct timer_list work_timer;
- int last_pipe; /* What might need clearing */
- __le32 signature; /* Learned signature */
- struct bulk_cb_wrap work_bcb;
- struct bulk_cs_wrap work_bcs;
- struct usb_ctrlrequest work_cr;
-
- struct work_struct reset_work;
- wait_queue_head_t reset_wait;
-};
-
-/*
- */
-static void ub_cleanup(struct ub_dev *sc);
-static int ub_request_fn_1(struct ub_lun *lun, struct request *rq);
-static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq);
-static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq);
-static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, unsigned int status);
-static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_request *urq, struct ub_scsi_cmd *cmd);
-static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_urb_complete(struct urb *urb);
-static void ub_scsi_action(unsigned long _dev);
-static void ub_scsi_dispatch(struct ub_dev *sc);
-static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc);
-static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
- int stalled_pipe);
-static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd);
-static void ub_reset_enter(struct ub_dev *sc, int try);
-static void ub_reset_task(struct work_struct *work);
-static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun);
-static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_capacity *ret);
-static int ub_sync_reset(struct ub_dev *sc);
-static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe);
-static int ub_probe_lun(struct ub_dev *sc, int lnum);
-
-/*
- */
-#ifdef CONFIG_USB_LIBUSUAL
-
-#define ub_usb_ids usb_storage_usb_ids
-#else
-
-static const struct usb_device_id ub_usb_ids[] = {
- { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, ub_usb_ids);
-#endif /* CONFIG_USB_LIBUSUAL */
-
-/*
- * Find me a way to identify "next free minor" for add_disk(),
- * and the array disappears the next day. However, the number of
- * hosts has something to do with the naming and /proc/partitions.
- * This has to be thought out in detail before changing.
- * If UB_MAX_HOST was 1000, we'd use a bitmap. Or a better data structure.
- */
-#define UB_MAX_HOSTS 26
-static char ub_hostv[UB_MAX_HOSTS];
-
-#define UB_QLOCK_NUM 5
-static spinlock_t ub_qlockv[UB_QLOCK_NUM];
-static int ub_qlock_next = 0;
-
-static DEFINE_SPINLOCK(ub_lock); /* Locks globals and ->openc */
-
-/*
- * The id allocator.
- *
- * This also stores the host for indexing by minor, which is somewhat dirty.
- */
-static int ub_id_get(void)
-{
- unsigned long flags;
- int i;
-
- spin_lock_irqsave(&ub_lock, flags);
- for (i = 0; i < UB_MAX_HOSTS; i++) {
- if (ub_hostv[i] == 0) {
- ub_hostv[i] = 1;
- spin_unlock_irqrestore(&ub_lock, flags);
- return i;
- }
- }
- spin_unlock_irqrestore(&ub_lock, flags);
- return -1;
-}
-
-static void ub_id_put(int id)
-{
- unsigned long flags;
-
- if (id < 0 || id >= UB_MAX_HOSTS) {
- printk(KERN_ERR DRV_NAME ": bad host ID %d\n", id);
- return;
- }
-
- spin_lock_irqsave(&ub_lock, flags);
- if (ub_hostv[id] == 0) {
- spin_unlock_irqrestore(&ub_lock, flags);
- printk(KERN_ERR DRV_NAME ": freeing free host ID %d\n", id);
- return;
- }
- ub_hostv[id] = 0;
- spin_unlock_irqrestore(&ub_lock, flags);
-}
-
-/*
- * This is necessitated by the fact that blk_cleanup_queue does not
- * necesserily destroy the queue. Instead, it may merely decrease q->refcnt.
- * Since our blk_init_queue() passes a spinlock common with ub_dev,
- * we have life time issues when ub_cleanup frees ub_dev.
- */
-static spinlock_t *ub_next_lock(void)
-{
- unsigned long flags;
- spinlock_t *ret;
-
- spin_lock_irqsave(&ub_lock, flags);
- ret = &ub_qlockv[ub_qlock_next];
- ub_qlock_next = (ub_qlock_next + 1) % UB_QLOCK_NUM;
- spin_unlock_irqrestore(&ub_lock, flags);
- return ret;
-}
-
-/*
- * Downcount for deallocation. This rides on two assumptions:
- * - once something is poisoned, its refcount cannot grow
- * - opens cannot happen at this time (del_gendisk was done)
- * If the above is true, we can drop the lock, which we need for
- * blk_cleanup_queue(): the silly thing may attempt to sleep.
- * [Actually, it never needs to sleep for us, but it calls might_sleep()]
- */
-static void ub_put(struct ub_dev *sc)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&ub_lock, flags);
- --sc->openc;
- if (sc->openc == 0 && atomic_read(&sc->poison)) {
- spin_unlock_irqrestore(&ub_lock, flags);
- ub_cleanup(sc);
- } else {
- spin_unlock_irqrestore(&ub_lock, flags);
- }
-}
-
-/*
- * Final cleanup and deallocation.
- */
-static void ub_cleanup(struct ub_dev *sc)
-{
- struct list_head *p;
- struct ub_lun *lun;
- struct request_queue *q;
-
- while (!list_empty(&sc->luns)) {
- p = sc->luns.next;
- lun = list_entry(p, struct ub_lun, link);
- list_del(p);
-
- /* I don't think queue can be NULL. But... Stolen from sx8.c */
- if ((q = lun->disk->queue) != NULL)
- blk_cleanup_queue(q);
- /*
- * If we zero disk->private_data BEFORE put_disk, we have
- * to check for NULL all over the place in open, release,
- * check_media and revalidate, because the block level
- * semaphore is well inside the put_disk.
- * But we cannot zero after the call, because *disk is gone.
- * The sd.c is blatantly racy in this area.
- */
- /* disk->private_data = NULL; */
- put_disk(lun->disk);
- lun->disk = NULL;
-
- ub_id_put(lun->id);
- kfree(lun);
- }
-
- usb_set_intfdata(sc->intf, NULL);
- usb_put_intf(sc->intf);
- usb_put_dev(sc->dev);
- kfree(sc);
-}
-
-/*
- * The "command allocator".
- */
-static struct ub_scsi_cmd *ub_get_cmd(struct ub_lun *lun)
-{
- struct ub_scsi_cmd *ret;
-
- if (lun->cmda[0])
- return NULL;
- ret = &lun->cmdv[0];
- lun->cmda[0] = 1;
- return ret;
-}
-
-static void ub_put_cmd(struct ub_lun *lun, struct ub_scsi_cmd *cmd)
-{
- if (cmd != &lun->cmdv[0]) {
- printk(KERN_WARNING "%s: releasing a foreign cmd %p\n",
- lun->name, cmd);
- return;
- }
- if (!lun->cmda[0]) {
- printk(KERN_WARNING "%s: releasing a free cmd\n", lun->name);
- return;
- }
- lun->cmda[0] = 0;
-}
-
-/*
- * The command queue.
- */
-static void ub_cmdq_add(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
-
- if (t->qlen++ == 0) {
- t->head = cmd;
- t->tail = cmd;
- } else {
- t->tail->next = cmd;
- t->tail = cmd;
- }
-
- if (t->qlen > t->qmax)
- t->qmax = t->qlen;
-}
-
-static void ub_cmdq_insert(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
-
- if (t->qlen++ == 0) {
- t->head = cmd;
- t->tail = cmd;
- } else {
- cmd->next = t->head;
- t->head = cmd;
- }
-
- if (t->qlen > t->qmax)
- t->qmax = t->qlen;
-}
-
-static struct ub_scsi_cmd *ub_cmdq_pop(struct ub_dev *sc)
-{
- struct ub_scsi_cmd_queue *t = &sc->cmd_queue;
- struct ub_scsi_cmd *cmd;
-
- if (t->qlen == 0)
- return NULL;
- if (--t->qlen == 0)
- t->tail = NULL;
- cmd = t->head;
- t->head = cmd->next;
- cmd->next = NULL;
- return cmd;
-}
-
-#define ub_cmdq_peek(sc) ((sc)->cmd_queue.head)
-
-/*
- * The request function is our main entry point
- */
-
-static void ub_request_fn(struct request_queue *q)
-{
- struct ub_lun *lun = q->queuedata;
- struct request *rq;
-
- while ((rq = blk_peek_request(q)) != NULL) {
- if (ub_request_fn_1(lun, rq) != 0) {
- blk_stop_queue(q);
- break;
- }
- }
-}
-
-static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
-{
- struct ub_dev *sc = lun->udev;
- struct ub_scsi_cmd *cmd;
- struct ub_request *urq;
- int n_elem;
-
- if (atomic_read(&sc->poison)) {
- blk_start_request(rq);
- ub_end_rq(rq, DID_NO_CONNECT << 16);
- return 0;
- }
-
- if (lun->changed && rq->cmd_type != REQ_TYPE_BLOCK_PC) {
- blk_start_request(rq);
- ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
- return 0;
- }
-
- if (lun->urq.rq != NULL)
- return -1;
- if ((cmd = ub_get_cmd(lun)) == NULL)
- return -1;
- memset(cmd, 0, sizeof(struct ub_scsi_cmd));
-
- blk_start_request(rq);
-
- urq = &lun->urq;
- memset(urq, 0, sizeof(struct ub_request));
- urq->rq = rq;
-
- /*
- * get scatterlist from block layer
- */
- sg_init_table(&urq->sgv[0], UB_MAX_REQ_SG);
- n_elem = blk_rq_map_sg(lun->disk->queue, rq, &urq->sgv[0]);
- if (n_elem < 0) {
- /* Impossible, because blk_rq_map_sg should not hit ENOMEM. */
- printk(KERN_INFO "%s: failed request map (%d)\n",
- lun->name, n_elem);
- goto drop;
- }
- if (n_elem > UB_MAX_REQ_SG) { /* Paranoia */
- printk(KERN_WARNING "%s: request with %d segments\n",
- lun->name, n_elem);
- goto drop;
- }
- urq->nsg = n_elem;
-
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- ub_cmd_build_packet(sc, lun, cmd, urq);
- } else {
- ub_cmd_build_block(sc, lun, cmd, urq);
- }
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun;
- cmd->done = ub_rw_cmd_done;
- cmd->back = urq;
-
- cmd->tag = sc->tagcnt++;
- if (ub_submit_scsi(sc, cmd) != 0)
- goto drop;
-
- return 0;
-
-drop:
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, DID_ERROR << 16);
- return 0;
-}
-
-static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq)
-{
- struct request *rq = urq->rq;
- unsigned int block, nblks;
-
- if (rq_data_dir(rq) == WRITE)
- cmd->dir = UB_DIR_WRITE;
- else
- cmd->dir = UB_DIR_READ;
-
- cmd->nsg = urq->nsg;
- memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
-
- /*
- * build the command
- *
- * The call to blk_queue_logical_block_size() guarantees that request
- * is aligned, but it is given in terms of 512 byte units, always.
- */
- block = blk_rq_pos(rq) >> lun->capacity.bshift;
- nblks = blk_rq_sectors(rq) >> lun->capacity.bshift;
-
- cmd->cdb[0] = (cmd->dir == UB_DIR_READ)? READ_10: WRITE_10;
- /* 10-byte uses 4 bytes of LBA: 2147483648KB, 2097152MB, 2048GB */
- cmd->cdb[2] = block >> 24;
- cmd->cdb[3] = block >> 16;
- cmd->cdb[4] = block >> 8;
- cmd->cdb[5] = block;
- cmd->cdb[7] = nblks >> 8;
- cmd->cdb[8] = nblks;
- cmd->cdb_len = 10;
-
- cmd->len = blk_rq_bytes(rq);
-}
-
-static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_scsi_cmd *cmd, struct ub_request *urq)
-{
- struct request *rq = urq->rq;
-
- if (blk_rq_bytes(rq) == 0) {
- cmd->dir = UB_DIR_NONE;
- } else {
- if (rq_data_dir(rq) == WRITE)
- cmd->dir = UB_DIR_WRITE;
- else
- cmd->dir = UB_DIR_READ;
- }
-
- cmd->nsg = urq->nsg;
- memcpy(cmd->sgv, urq->sgv, sizeof(struct scatterlist) * cmd->nsg);
-
- memcpy(&cmd->cdb, rq->cmd, rq->cmd_len);
- cmd->cdb_len = rq->cmd_len;
-
- cmd->len = blk_rq_bytes(rq);
-
- /*
- * To reapply this to every URB is not as incorrect as it looks.
- * In return, we avoid any complicated tracking calculations.
- */
- cmd->timeo = rq->timeout;
-}
-
-static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_lun *lun = cmd->lun;
- struct ub_request *urq = cmd->back;
- struct request *rq;
- unsigned int scsi_status;
-
- rq = urq->rq;
-
- if (cmd->error == 0) {
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- if (cmd->act_len >= rq->resid_len)
- rq->resid_len = 0;
- else
- rq->resid_len -= cmd->act_len;
- scsi_status = 0;
- } else {
- if (cmd->act_len != cmd->len) {
- scsi_status = SAM_STAT_CHECK_CONDITION;
- } else {
- scsi_status = 0;
- }
- }
- } else {
- if (rq->cmd_type == REQ_TYPE_BLOCK_PC) {
- /* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
- memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
- rq->sense_len = UB_SENSE_SIZE;
- if (sc->top_sense[0] != 0)
- scsi_status = SAM_STAT_CHECK_CONDITION;
- else
- scsi_status = DID_ERROR << 16;
- } else {
- if (cmd->error == -EIO &&
- (cmd->key == 0 ||
- cmd->key == MEDIUM_ERROR ||
- cmd->key == UNIT_ATTENTION)) {
- if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
- return;
- }
- scsi_status = SAM_STAT_CHECK_CONDITION;
- }
- }
-
- urq->rq = NULL;
-
- ub_put_cmd(lun, cmd);
- ub_end_rq(rq, scsi_status);
- blk_start_queue(lun->disk->queue);
-}
-
-static void ub_end_rq(struct request *rq, unsigned int scsi_status)
-{
- int error;
-
- if (scsi_status == 0) {
- error = 0;
- } else {
- error = -EIO;
- rq->errors = scsi_status;
- }
- __blk_end_request_all(rq, error);
-}
-
-static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_request *urq, struct ub_scsi_cmd *cmd)
-{
-
- if (atomic_read(&sc->poison))
- return -ENXIO;
-
- ub_reset_enter(sc, urq->current_try);
-
- if (urq->current_try >= 3)
- return -EIO;
- urq->current_try++;
-
- /* Remove this if anyone complains of flooding. */
- printk(KERN_DEBUG "%s: dir %c len/act %d/%d "
- "[sense %x %02x %02x] retry %d\n",
- sc->name, UB_DIR_CHAR(cmd->dir), cmd->len, cmd->act_len,
- cmd->key, cmd->asc, cmd->ascq, urq->current_try);
-
- memset(cmd, 0, sizeof(struct ub_scsi_cmd));
- ub_cmd_build_block(sc, lun, cmd, urq);
-
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun;
- cmd->done = ub_rw_cmd_done;
- cmd->back = urq;
-
- cmd->tag = sc->tagcnt++;
-
-#if 0 /* Wasteful */
- return ub_submit_scsi(sc, cmd);
-#else
- ub_cmdq_add(sc, cmd);
- return 0;
-#endif
-}
-
-/*
- * Submit a regular SCSI operation (not an auto-sense).
- *
- * The Iron Law of Good Submit Routine is:
- * Zero return - callback is done, Nonzero return - callback is not done.
- * No exceptions.
- *
- * Host is assumed locked.
- */
-static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (cmd->state != UB_CMDST_INIT ||
- (cmd->dir != UB_DIR_NONE && cmd->len == 0)) {
- return -EINVAL;
- }
-
- ub_cmdq_add(sc, cmd);
- /*
- * We can call ub_scsi_dispatch(sc) right away here, but it's a little
- * safer to jump to a tasklet, in case upper layers do something silly.
- */
- tasklet_schedule(&sc->tasklet);
- return 0;
-}
-
-/*
- * Submit the first URB for the queued command.
- * This function does not deal with queueing in any way.
- */
-static int ub_scsi_cmd_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct bulk_cb_wrap *bcb;
- int rc;
-
- bcb = &sc->work_bcb;
-
- /*
- * ``If the allocation length is eighteen or greater, and a device
- * server returns less than eithteen bytes of data, the application
- * client should assume that the bytes not transferred would have been
- * zeroes had the device server returned those bytes.''
- *
- * We zero sense for all commands so that when a packet request
- * fails it does not return a stale sense.
- */
- memset(&sc->top_sense, 0, UB_SENSE_SIZE);
-
- /* set up the command wrapper */
- bcb->Signature = cpu_to_le32(US_BULK_CB_SIGN);
- bcb->Tag = cmd->tag; /* Endianness is not important */
- bcb->DataTransferLength = cpu_to_le32(cmd->len);
- bcb->Flags = (cmd->dir == UB_DIR_READ) ? 0x80 : 0;
- bcb->Lun = (cmd->lun != NULL) ? cmd->lun->num : 0;
- bcb->Length = cmd->cdb_len;
-
- /* copy the command payload */
- memcpy(bcb->CDB, cmd->cdb, UB_MAX_CDB_SIZE);
-
- UB_INIT_COMPLETION(sc->work_done);
-
- sc->last_pipe = sc->send_bulk_pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->send_bulk_pipe,
- bcb, US_BULK_CB_WRAP_LEN, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- return rc;
- }
-
- sc->work_timer.expires = jiffies + UB_URB_TIMEOUT;
- add_timer(&sc->work_timer);
-
- cmd->state = UB_CMDST_CMD;
- return 0;
-}
-
-/*
- * Timeout handler.
- */
-static void ub_urb_timeout(unsigned long arg)
-{
- struct ub_dev *sc = (struct ub_dev *) arg;
- unsigned long flags;
-
- spin_lock_irqsave(sc->lock, flags);
- if (!ub_is_completed(&sc->work_done))
- usb_unlink_urb(&sc->work_urb);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-/*
- * Completion routine for the work URB.
- *
- * This can be called directly from usb_submit_urb (while we have
- * the sc->lock taken) and from an interrupt (while we do NOT have
- * the sc->lock taken). Therefore, bounce this off to a tasklet.
- */
-static void ub_urb_complete(struct urb *urb)
-{
- struct ub_dev *sc = urb->context;
-
- ub_complete(&sc->work_done);
- tasklet_schedule(&sc->tasklet);
-}
-
-static void ub_scsi_action(unsigned long _dev)
-{
- struct ub_dev *sc = (struct ub_dev *) _dev;
- unsigned long flags;
-
- spin_lock_irqsave(sc->lock, flags);
- ub_scsi_dispatch(sc);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-static void ub_scsi_dispatch(struct ub_dev *sc)
-{
- struct ub_scsi_cmd *cmd;
- int rc;
-
- while (!sc->reset && (cmd = ub_cmdq_peek(sc)) != NULL) {
- if (cmd->state == UB_CMDST_DONE) {
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
- } else if (cmd->state == UB_CMDST_INIT) {
- if ((rc = ub_scsi_cmd_start(sc, cmd)) == 0)
- break;
- cmd->error = rc;
- cmd->state = UB_CMDST_DONE;
- } else {
- if (!ub_is_completed(&sc->work_done))
- break;
- del_timer(&sc->work_timer);
- ub_scsi_urb_compl(sc, cmd);
- }
- }
-}
-
-static void ub_scsi_urb_compl(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct urb *urb = &sc->work_urb;
- struct bulk_cs_wrap *bcs;
- int endp;
- int len;
- int rc;
-
- if (atomic_read(&sc->poison)) {
- ub_state_done(sc, cmd, -ENODEV);
- return;
- }
-
- endp = usb_pipeendpoint(sc->last_pipe);
- if (usb_pipein(sc->last_pipe))
- endp |= USB_DIR_IN;
-
- if (cmd->state == UB_CMDST_CLEAR) {
- if (urb->status == -EPIPE) {
- /*
- * STALL while clearning STALL.
- * The control pipe clears itself - nothing to do.
- */
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_sense(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CLR2STS) {
- if (urb->status == -EPIPE) {
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_stat(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CLRRS) {
- if (urb->status == -EPIPE) {
- printk(KERN_NOTICE "%s: stall on control pipe\n",
- sc->name);
- goto Bad_End;
- }
-
- /*
- * We ignore the result for the halt clear.
- */
-
- usb_reset_endpoint(sc->dev, endp);
-
- ub_state_stat_counted(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_CMD) {
- switch (urb->status) {
- case 0:
- break;
- case -EOVERFLOW:
- goto Bad_End;
- case -EPIPE:
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- /*
- * This is typically ENOMEM or some other such shit.
- * Retrying is pointless. Just do Bad End on it...
- */
- ub_state_done(sc, cmd, rc);
- return;
- }
- cmd->state = UB_CMDST_CLEAR;
- return;
- case -ESHUTDOWN: /* unplug */
- case -EILSEQ: /* unplug timeout on uhci */
- ub_state_done(sc, cmd, -ENODEV);
- return;
- default:
- goto Bad_End;
- }
- if (urb->actual_length != US_BULK_CB_WRAP_LEN) {
- goto Bad_End;
- }
-
- if (cmd->dir == UB_DIR_NONE || cmd->nsg < 1) {
- ub_state_stat(sc, cmd);
- return;
- }
-
- // udelay(125); // usb-storage has this
- ub_data_start(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_DATA) {
- if (urb->status == -EPIPE) {
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- ub_state_done(sc, cmd, rc);
- return;
- }
- cmd->state = UB_CMDST_CLR2STS;
- return;
- }
- if (urb->status == -EOVERFLOW) {
- /*
- * A babble? Failure, but we must transfer CSW now.
- */
- cmd->error = -EOVERFLOW; /* A cheap trick... */
- ub_state_stat(sc, cmd);
- return;
- }
-
- if (cmd->dir == UB_DIR_WRITE) {
- /*
- * Do not continue writes in case of a failure.
- * Doing so would cause sectors to be mixed up,
- * which is worse than sectors lost.
- *
- * We must try to read the CSW, or many devices
- * get confused.
- */
- len = urb->actual_length;
- if (urb->status != 0 ||
- len != cmd->sgv[cmd->current_sg].length) {
- cmd->act_len += len;
-
- cmd->error = -EIO;
- ub_state_stat(sc, cmd);
- return;
- }
-
- } else {
- /*
- * If an error occurs on read, we record it, and
- * continue to fetch data in order to avoid bubble.
- *
- * As a small shortcut, we stop if we detect that
- * a CSW mixed into data.
- */
- if (urb->status != 0)
- cmd->error = -EIO;
-
- len = urb->actual_length;
- if (urb->status != 0 ||
- len != cmd->sgv[cmd->current_sg].length) {
- if ((len & 0x1FF) == US_BULK_CS_WRAP_LEN)
- goto Bad_End;
- }
- }
-
- cmd->act_len += urb->actual_length;
-
- if (++cmd->current_sg < cmd->nsg) {
- ub_data_start(sc, cmd);
- return;
- }
- ub_state_stat(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_STAT) {
- if (urb->status == -EPIPE) {
- rc = ub_submit_clear_stall(sc, cmd, sc->last_pipe);
- if (rc != 0) {
- printk(KERN_NOTICE "%s: "
- "unable to submit clear (%d)\n",
- sc->name, rc);
- ub_state_done(sc, cmd, rc);
- return;
- }
-
- /*
- * Having a stall when getting CSW is an error, so
- * make sure uppper levels are not oblivious to it.
- */
- cmd->error = -EIO; /* A cheap trick... */
-
- cmd->state = UB_CMDST_CLRRS;
- return;
- }
-
- /* Catch everything, including -EOVERFLOW and other nasties. */
- if (urb->status != 0)
- goto Bad_End;
-
- if (urb->actual_length == 0) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
-
- /*
- * Check the returned Bulk protocol status.
- * The status block has to be validated first.
- */
-
- bcs = &sc->work_bcs;
-
- if (sc->signature == cpu_to_le32(0)) {
- /*
- * This is the first reply, so do not perform the check.
- * Instead, remember the signature the device uses
- * for future checks. But do not allow a nul.
- */
- sc->signature = bcs->Signature;
- if (sc->signature == cpu_to_le32(0)) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
- } else {
- if (bcs->Signature != sc->signature) {
- ub_state_stat_counted(sc, cmd);
- return;
- }
- }
-
- if (bcs->Tag != cmd->tag) {
- /*
- * This usually happens when we disagree with the
- * device's microcode about something. For instance,
- * a few of them throw this after timeouts. They buffer
- * commands and reply at commands we timed out before.
- * Without flushing these replies we loop forever.
- */
- ub_state_stat_counted(sc, cmd);
- return;
- }
-
- if (!sc->bad_resid) {
- len = le32_to_cpu(bcs->Residue);
- if (len != cmd->len - cmd->act_len) {
- /*
- * Only start ignoring if this cmd ended well.
- */
- if (cmd->len == cmd->act_len) {
- printk(KERN_NOTICE "%s: "
- "bad residual %d of %d, ignoring\n",
- sc->name, len, cmd->len);
- sc->bad_resid = 1;
- }
- }
- }
-
- switch (bcs->Status) {
- case US_BULK_STAT_OK:
- break;
- case US_BULK_STAT_FAIL:
- ub_state_sense(sc, cmd);
- return;
- case US_BULK_STAT_PHASE:
- goto Bad_End;
- default:
- printk(KERN_INFO "%s: unknown CSW status 0x%x\n",
- sc->name, bcs->Status);
- ub_state_done(sc, cmd, -EINVAL);
- return;
- }
-
- /* Not zeroing error to preserve a babble indicator */
- if (cmd->error != 0) {
- ub_state_sense(sc, cmd);
- return;
- }
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
-
- } else if (cmd->state == UB_CMDST_SENSE) {
- ub_state_done(sc, cmd, -EIO);
-
- } else {
- printk(KERN_WARNING "%s: wrong command state %d\n",
- sc->name, cmd->state);
- ub_state_done(sc, cmd, -EINVAL);
- return;
- }
- return;
-
-Bad_End: /* Little Excel is dead */
- ub_state_done(sc, cmd, -EIO);
-}
-
-/*
- * Factorization helper for the command state machine:
- * Initiate a data segment transfer.
- */
-static void ub_data_start(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct scatterlist *sg = &cmd->sgv[cmd->current_sg];
- int pipe;
- int rc;
-
- UB_INIT_COMPLETION(sc->work_done);
-
- if (cmd->dir == UB_DIR_READ)
- pipe = sc->recv_bulk_pipe;
- else
- pipe = sc->send_bulk_pipe;
- sc->last_pipe = pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, pipe, sg_virt(sg),
- sg->length, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- ub_state_done(sc, cmd, rc);
- return;
- }
-
- if (cmd->timeo)
- sc->work_timer.expires = jiffies + cmd->timeo;
- else
- sc->work_timer.expires = jiffies + UB_DATA_TIMEOUT;
- add_timer(&sc->work_timer);
-
- cmd->state = UB_CMDST_DATA;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Finish the command.
- */
-static void ub_state_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd, int rc)
-{
-
- cmd->error = rc;
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read.
- */
-static int __ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- int rc;
-
- UB_INIT_COMPLETION(sc->work_done);
-
- sc->last_pipe = sc->recv_bulk_pipe;
- usb_fill_bulk_urb(&sc->work_urb, sc->dev, sc->recv_bulk_pipe,
- &sc->work_bcs, US_BULK_CS_WRAP_LEN, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- /* XXX Clear stalls */
- ub_complete(&sc->work_done);
- ub_state_done(sc, cmd, rc);
- return -1;
- }
-
- if (cmd->timeo)
- sc->work_timer.expires = jiffies + cmd->timeo;
- else
- sc->work_timer.expires = jiffies + UB_STAT_TIMEOUT;
- add_timer(&sc->work_timer);
- return 0;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read and go to STAT state.
- */
-static void ub_state_stat(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (__ub_state_stat(sc, cmd) != 0)
- return;
-
- cmd->stat_count = 0;
- cmd->state = UB_CMDST_STAT;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a CSW read and go to STAT state with counter (along [C] path).
- */
-static void ub_state_stat_counted(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
-
- if (++cmd->stat_count >= 4) {
- ub_state_sense(sc, cmd);
- return;
- }
-
- if (__ub_state_stat(sc, cmd) != 0)
- return;
-
- cmd->state = UB_CMDST_STAT;
-}
-
-/*
- * Factorization helper for the command state machine:
- * Submit a REQUEST SENSE and go to SENSE state.
- */
-static void ub_state_sense(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct ub_scsi_cmd *scmd;
- struct scatterlist *sg;
- int rc;
-
- if (cmd->cdb[0] == REQUEST_SENSE) {
- rc = -EPIPE;
- goto error;
- }
-
- scmd = &sc->top_rqs_cmd;
- memset(scmd, 0, sizeof(struct ub_scsi_cmd));
- scmd->cdb[0] = REQUEST_SENSE;
- scmd->cdb[4] = UB_SENSE_SIZE;
- scmd->cdb_len = 6;
- scmd->dir = UB_DIR_READ;
- scmd->state = UB_CMDST_INIT;
- scmd->nsg = 1;
- sg = &scmd->sgv[0];
- sg_init_table(sg, UB_MAX_REQ_SG);
- sg_set_page(sg, virt_to_page(sc->top_sense), UB_SENSE_SIZE,
- (unsigned long)sc->top_sense & (PAGE_SIZE-1));
- scmd->len = UB_SENSE_SIZE;
- scmd->lun = cmd->lun;
- scmd->done = ub_top_sense_done;
- scmd->back = cmd;
-
- scmd->tag = sc->tagcnt++;
-
- cmd->state = UB_CMDST_SENSE;
-
- ub_cmdq_insert(sc, scmd);
- return;
-
-error:
- ub_state_done(sc, cmd, rc);
-}
-
-/*
- * A helper for the command's state machine:
- * Submit a stall clear.
- */
-static int ub_submit_clear_stall(struct ub_dev *sc, struct ub_scsi_cmd *cmd,
- int stalled_pipe)
-{
- int endp;
- struct usb_ctrlrequest *cr;
- int rc;
-
- endp = usb_pipeendpoint(stalled_pipe);
- if (usb_pipein (stalled_pipe))
- endp |= USB_DIR_IN;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_RECIP_ENDPOINT;
- cr->bRequest = USB_REQ_CLEAR_FEATURE;
- cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
- cr->wIndex = cpu_to_le16(endp);
- cr->wLength = cpu_to_le16(0);
-
- UB_INIT_COMPLETION(sc->work_done);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_urb_complete, sc);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_ATOMIC)) != 0) {
- ub_complete(&sc->work_done);
- return rc;
- }
-
- sc->work_timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&sc->work_timer);
- return 0;
-}
-
-/*
- */
-static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd)
-{
- unsigned char *sense = sc->top_sense;
- struct ub_scsi_cmd *cmd;
-
- /*
- * Find the command which triggered the unit attention or a check,
- * save the sense into it, and advance its state machine.
- */
- if ((cmd = ub_cmdq_peek(sc)) == NULL) {
- printk(KERN_WARNING "%s: sense done while idle\n", sc->name);
- return;
- }
- if (cmd != scmd->back) {
- printk(KERN_WARNING "%s: "
- "sense done for wrong command 0x%x\n",
- sc->name, cmd->tag);
- return;
- }
- if (cmd->state != UB_CMDST_SENSE) {
- printk(KERN_WARNING "%s: sense done with bad cmd state %d\n",
- sc->name, cmd->state);
- return;
- }
-
- /*
- * Ignoring scmd->act_len, because the buffer was pre-zeroed.
- */
- cmd->key = sense[2] & 0x0F;
- cmd->asc = sense[12];
- cmd->ascq = sense[13];
-
- ub_scsi_urb_compl(sc, cmd);
-}
-
-/*
- * Reset management
- */
-
-static void ub_reset_enter(struct ub_dev *sc, int try)
-{
-
- if (sc->reset) {
- /* This happens often on multi-LUN devices. */
- return;
- }
- sc->reset = try + 1;
-
-#if 0 /* Not needed because the disconnect waits for us. */
- unsigned long flags;
- spin_lock_irqsave(&ub_lock, flags);
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-#endif
-
-#if 0 /* We let them stop themselves. */
- struct ub_lun *lun;
- list_for_each_entry(lun, &sc->luns, link) {
- blk_stop_queue(lun->disk->queue);
- }
-#endif
-
- schedule_work(&sc->reset_work);
-}
-
-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 rc;
-
- if (!sc->reset) {
- printk(KERN_WARNING "%s: Running reset unrequested\n",
- sc->name);
- return;
- }
-
- if (atomic_read(&sc->poison)) {
- ;
- } else if ((sc->reset & 1) == 0) {
- ub_sync_reset(sc);
- msleep(700); /* usb-storage sleeps 6s (!) */
- ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
- ub_probe_clear_stall(sc, sc->send_bulk_pipe);
- } else if (sc->dev->actconfig->desc.bNumInterfaces != 1) {
- ;
- } else {
- 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, rc);
- } else {
- rc = usb_reset_device(sc->dev);
- if (rc < 0) {
- printk(KERN_NOTICE "%s: "
- "usb_lock_device_for_reset failed (%d)\n",
- sc->name, rc);
- }
- usb_unlock_device(sc->dev);
- }
- }
-
- /*
- * In theory, no commands can be running while reset is active,
- * so nobody can ask for another reset, and so we do not need any
- * queues of resets or anything. We do need a spinlock though,
- * to interact with block layer.
- */
- spin_lock_irqsave(sc->lock, flags);
- sc->reset = 0;
- tasklet_schedule(&sc->tasklet);
- list_for_each_entry(lun, &sc->luns, link) {
- blk_start_queue(lun->disk->queue);
- }
- wake_up(&sc->reset_wait);
- spin_unlock_irqrestore(sc->lock, flags);
-}
-
-/*
- * XXX Reset brackets are too much hassle to implement, so just stub them
- * in order to prevent forced unbinding (which deadlocks solid when our
- * ->disconnect method waits for the reset to complete and this kills keventd).
- *
- * XXX Tell Alan to move usb_unlock_device inside of usb_reset_device,
- * or else the post_reset is invoked, and restats I/O on a locked device.
- */
-static int ub_pre_reset(struct usb_interface *iface) {
- return 0;
-}
-
-static int ub_post_reset(struct usb_interface *iface) {
- return 0;
-}
-
-/*
- * This is called from a process context.
- */
-static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun)
-{
-
- lun->readonly = 0; /* XXX Query this from the device */
-
- lun->capacity.nsec = 0;
- lun->capacity.bsize = 512;
- lun->capacity.bshift = 0;
-
- if (ub_sync_tur(sc, lun) != 0)
- return; /* Not ready */
- lun->changed = 0;
-
- if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
- /*
- * The retry here means something is wrong, either with the
- * device, with the transport, or with our code.
- * We keep this because sd.c has retries for capacity.
- */
- if (ub_sync_read_cap(sc, lun, &lun->capacity) != 0) {
- lun->capacity.nsec = 0;
- lun->capacity.bsize = 512;
- lun->capacity.bshift = 0;
- }
- }
-}
-
-/*
- * The open funcion.
- * This is mostly needed to keep refcounting, but also to support
- * media checks on removable media drives.
- */
-static int ub_bd_open(struct block_device *bdev, fmode_t mode)
-{
- struct ub_lun *lun = bdev->bd_disk->private_data;
- struct ub_dev *sc = lun->udev;
- unsigned long flags;
- int rc;
-
- spin_lock_irqsave(&ub_lock, flags);
- if (atomic_read(&sc->poison)) {
- spin_unlock_irqrestore(&ub_lock, flags);
- return -ENXIO;
- }
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-
- if (lun->removable || lun->readonly)
- check_disk_change(bdev);
-
- /*
- * The sd.c considers ->media_present and ->changed not equivalent,
- * under some pretty murky conditions (a failure of READ CAPACITY).
- * We may need it one day.
- */
- if (lun->removable && lun->changed && !(mode & FMODE_NDELAY)) {
- rc = -ENOMEDIUM;
- goto err_open;
- }
-
- if (lun->readonly && (mode & FMODE_WRITE)) {
- rc = -EROFS;
- goto err_open;
- }
-
- return 0;
-
-err_open:
- ub_put(sc);
- return rc;
-}
-
-static int ub_bd_unlocked_open(struct block_device *bdev, fmode_t mode)
-{
- int ret;
-
- mutex_lock(&ub_mutex);
- ret = ub_bd_open(bdev, mode);
- mutex_unlock(&ub_mutex);
-
- return ret;
-}
-
-
-/*
- */
-static int ub_bd_release(struct gendisk *disk, fmode_t mode)
-{
- struct ub_lun *lun = disk->private_data;
- struct ub_dev *sc = lun->udev;
-
- mutex_lock(&ub_mutex);
- ub_put(sc);
- mutex_unlock(&ub_mutex);
-
- return 0;
-}
-
-/*
- * The ioctl interface.
- */
-static int ub_bd_ioctl(struct block_device *bdev, fmode_t mode,
- unsigned int cmd, unsigned long arg)
-{
- void __user *usermem = (void __user *) arg;
- int ret;
-
- mutex_lock(&ub_mutex);
- ret = scsi_cmd_blk_ioctl(bdev, mode, cmd, usermem);
- mutex_unlock(&ub_mutex);
-
- return ret;
-}
-
-/*
- * This is called by check_disk_change if we reported a media change.
- * The main onjective here is to discover the features of the media such as
- * the capacity, read-only status, etc. USB storage generally does not
- * need to be spun up, but if we needed it, this would be the place.
- *
- * This call can sleep.
- *
- * The return code is not used.
- */
-static int ub_bd_revalidate(struct gendisk *disk)
-{
- struct ub_lun *lun = disk->private_data;
-
- ub_revalidate(lun->udev, lun);
-
- /* XXX Support sector size switching like in sr.c */
- blk_queue_logical_block_size(disk->queue, lun->capacity.bsize);
- set_capacity(disk, lun->capacity.nsec);
- // set_disk_ro(sdkp->disk, lun->readonly);
-
- return 0;
-}
-
-/*
- * The check is called by the block layer to verify if the media
- * is still available. It is supposed to be harmless, lightweight and
- * non-intrusive in case the media was not changed.
- *
- * This call can sleep.
- *
- * The return code is bool!
- */
-static unsigned int ub_bd_check_events(struct gendisk *disk,
- unsigned int clearing)
-{
- struct ub_lun *lun = disk->private_data;
-
- if (!lun->removable)
- return 0;
-
- /*
- * We clean checks always after every command, so this is not
- * as dangerous as it looks. If the TEST_UNIT_READY fails here,
- * the device is actually not ready with operator or software
- * intervention required. One dangerous item might be a drive which
- * spins itself down, and come the time to write dirty pages, this
- * will fail, then block layer discards the data. Since we never
- * spin drives up, such devices simply cannot be used with ub anyway.
- */
- if (ub_sync_tur(lun->udev, lun) != 0) {
- lun->changed = 1;
- return DISK_EVENT_MEDIA_CHANGE;
- }
-
- return lun->changed ? DISK_EVENT_MEDIA_CHANGE : 0;
-}
-
-static const struct block_device_operations ub_bd_fops = {
- .owner = THIS_MODULE,
- .open = ub_bd_unlocked_open,
- .release = ub_bd_release,
- .ioctl = ub_bd_ioctl,
- .check_events = ub_bd_check_events,
- .revalidate_disk = ub_bd_revalidate,
-};
-
-/*
- * Common ->done routine for commands executed synchronously.
- */
-static void ub_probe_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
-{
- struct completion *cop = cmd->back;
- complete(cop);
-}
-
-/*
- * Test if the device has a check condition on it, synchronously.
- */
-static int ub_sync_tur(struct ub_dev *sc, struct ub_lun *lun)
-{
- struct ub_scsi_cmd *cmd;
- enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) };
- unsigned long flags;
- struct completion compl;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
-
- cmd->cdb[0] = TEST_UNIT_READY;
- cmd->cdb_len = 6;
- cmd->dir = UB_DIR_NONE;
- cmd->state = UB_CMDST_INIT;
- cmd->lun = lun; /* This may be NULL, but that's ok */
- cmd->done = ub_probe_done;
- cmd->back = &compl;
-
- spin_lock_irqsave(sc->lock, flags);
- cmd->tag = sc->tagcnt++;
-
- rc = ub_submit_scsi(sc, cmd);
- spin_unlock_irqrestore(sc->lock, flags);
-
- if (rc != 0)
- goto err_submit;
-
- wait_for_completion(&compl);
-
- rc = cmd->error;
-
- if (rc == -EIO && cmd->key != 0) /* Retries for benh's key */
- rc = cmd->key;
-
-err_submit:
- kfree(cmd);
-err_alloc:
- return rc;
-}
-
-/*
- * Read the SCSI capacity synchronously (for probing).
- */
-static int ub_sync_read_cap(struct ub_dev *sc, struct ub_lun *lun,
- struct ub_capacity *ret)
-{
- struct ub_scsi_cmd *cmd;
- struct scatterlist *sg;
- char *p;
- enum { ALLOC_SIZE = sizeof(struct ub_scsi_cmd) + 8 };
- unsigned long flags;
- unsigned int bsize, shift;
- unsigned long nsec;
- struct completion compl;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((cmd = kzalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
- p = (char *)cmd + sizeof(struct ub_scsi_cmd);
-
- cmd->cdb[0] = 0x25;
- cmd->cdb_len = 10;
- cmd->dir = UB_DIR_READ;
- cmd->state = UB_CMDST_INIT;
- cmd->nsg = 1;
- sg = &cmd->sgv[0];
- sg_init_table(sg, UB_MAX_REQ_SG);
- sg_set_page(sg, virt_to_page(p), 8, (unsigned long)p & (PAGE_SIZE-1));
- cmd->len = 8;
- cmd->lun = lun;
- cmd->done = ub_probe_done;
- cmd->back = &compl;
-
- spin_lock_irqsave(sc->lock, flags);
- cmd->tag = sc->tagcnt++;
-
- rc = ub_submit_scsi(sc, cmd);
- spin_unlock_irqrestore(sc->lock, flags);
-
- if (rc != 0)
- goto err_submit;
-
- wait_for_completion(&compl);
-
- if (cmd->error != 0) {
- rc = -EIO;
- goto err_read;
- }
- if (cmd->act_len != 8) {
- rc = -EIO;
- goto err_read;
- }
-
- /* sd.c special-cases sector size of 0 to mean 512. Needed? Safe? */
- nsec = be32_to_cpu(*(__be32 *)p) + 1;
- bsize = be32_to_cpu(*(__be32 *)(p + 4));
- switch (bsize) {
- case 512: shift = 0; break;
- case 1024: shift = 1; break;
- case 2048: shift = 2; break;
- case 4096: shift = 3; break;
- default:
- rc = -EDOM;
- goto err_inv_bsize;
- }
-
- ret->bsize = bsize;
- ret->bshift = shift;
- ret->nsec = nsec << shift;
- rc = 0;
-
-err_inv_bsize:
-err_read:
-err_submit:
- kfree(cmd);
-err_alloc:
- return rc;
-}
-
-/*
- */
-static void ub_probe_urb_complete(struct urb *urb)
-{
- struct completion *cop = urb->context;
- complete(cop);
-}
-
-static void ub_probe_timeout(unsigned long arg)
-{
- struct completion *cop = (struct completion *) arg;
- complete(cop);
-}
-
-/*
- * Reset with a Bulk reset.
- */
-static int ub_sync_reset(struct ub_dev *sc)
-{
- int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int rc;
-
- init_completion(&compl);
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- cr->bRequest = US_BULK_RESET_REQUEST;
- cr->wValue = cpu_to_le16(0);
- cr->wIndex = cpu_to_le16(ifnum);
- cr->wLength = cpu_to_le16(0);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
- printk(KERN_WARNING
- "%s: Unable to submit a bulk reset (%d)\n", sc->name, rc);
- return rc;
- }
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- return sc->work_urb.status;
-}
-
-/*
- * Get number of LUNs by the way of Bulk GetMaxLUN command.
- */
-static int ub_sync_getmaxlun(struct ub_dev *sc)
-{
- int ifnum = sc->intf->cur_altsetting->desc.bInterfaceNumber;
- unsigned char *p;
- enum { ALLOC_SIZE = 1 };
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int nluns;
- int rc;
-
- init_completion(&compl);
-
- rc = -ENOMEM;
- if ((p = kmalloc(ALLOC_SIZE, GFP_KERNEL)) == NULL)
- goto err_alloc;
- *p = 55;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
- cr->bRequest = US_BULK_GET_MAX_LUN;
- cr->wValue = cpu_to_le16(0);
- cr->wIndex = cpu_to_le16(ifnum);
- cr->wLength = cpu_to_le16(1);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->recv_ctrl_pipe,
- (unsigned char*) cr, p, 1, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0)
- goto err_submit;
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- if ((rc = sc->work_urb.status) < 0)
- goto err_io;
-
- if (sc->work_urb.actual_length != 1) {
- nluns = 0;
- } else {
- if ((nluns = *p) == 55) {
- nluns = 0;
- } else {
- /* GetMaxLUN returns the maximum LUN number */
- nluns += 1;
- if (nluns > UB_MAX_LUNS)
- nluns = UB_MAX_LUNS;
- }
- }
-
- kfree(p);
- return nluns;
-
-err_io:
-err_submit:
- kfree(p);
-err_alloc:
- return rc;
-}
-
-/*
- * Clear initial stalls.
- */
-static int ub_probe_clear_stall(struct ub_dev *sc, int stalled_pipe)
-{
- int endp;
- struct usb_ctrlrequest *cr;
- struct completion compl;
- struct timer_list timer;
- int rc;
-
- init_completion(&compl);
-
- endp = usb_pipeendpoint(stalled_pipe);
- if (usb_pipein (stalled_pipe))
- endp |= USB_DIR_IN;
-
- cr = &sc->work_cr;
- cr->bRequestType = USB_RECIP_ENDPOINT;
- cr->bRequest = USB_REQ_CLEAR_FEATURE;
- cr->wValue = cpu_to_le16(USB_ENDPOINT_HALT);
- cr->wIndex = cpu_to_le16(endp);
- cr->wLength = cpu_to_le16(0);
-
- usb_fill_control_urb(&sc->work_urb, sc->dev, sc->send_ctrl_pipe,
- (unsigned char*) cr, NULL, 0, ub_probe_urb_complete, &compl);
-
- if ((rc = usb_submit_urb(&sc->work_urb, GFP_KERNEL)) != 0) {
- printk(KERN_WARNING
- "%s: Unable to submit a probe clear (%d)\n", sc->name, rc);
- return rc;
- }
-
- init_timer(&timer);
- timer.function = ub_probe_timeout;
- timer.data = (unsigned long) &compl;
- timer.expires = jiffies + UB_CTRL_TIMEOUT;
- add_timer(&timer);
-
- wait_for_completion(&compl);
-
- del_timer_sync(&timer);
- usb_kill_urb(&sc->work_urb);
-
- usb_reset_endpoint(sc->dev, endp);
-
- return 0;
-}
-
-/*
- * Get the pipe settings.
- */
-static int ub_get_pipes(struct ub_dev *sc, struct usb_device *dev,
- struct usb_interface *intf)
-{
- struct usb_host_interface *altsetting = intf->cur_altsetting;
- struct usb_endpoint_descriptor *ep_in = NULL;
- struct usb_endpoint_descriptor *ep_out = NULL;
- struct usb_endpoint_descriptor *ep;
- int i;
-
- /*
- * Find the endpoints we need.
- * We are expecting a minimum of 2 endpoints - in and out (bulk).
- * We will ignore any others.
- */
- for (i = 0; i < altsetting->desc.bNumEndpoints; i++) {
- ep = &altsetting->endpoint[i].desc;
-
- /* Is it a BULK endpoint? */
- if (usb_endpoint_xfer_bulk(ep)) {
- /* BULK in or out? */
- if (usb_endpoint_dir_in(ep)) {
- if (ep_in == NULL)
- ep_in = ep;
- } else {
- if (ep_out == NULL)
- ep_out = ep;
- }
- }
- }
-
- if (ep_in == NULL || ep_out == NULL) {
- printk(KERN_NOTICE "%s: failed endpoint check\n", sc->name);
- return -ENODEV;
- }
-
- /* Calculate and store the pipe values */
- sc->send_ctrl_pipe = usb_sndctrlpipe(dev, 0);
- sc->recv_ctrl_pipe = usb_rcvctrlpipe(dev, 0);
- sc->send_bulk_pipe = usb_sndbulkpipe(dev,
- usb_endpoint_num(ep_out));
- sc->recv_bulk_pipe = usb_rcvbulkpipe(dev,
- usb_endpoint_num(ep_in));
-
- return 0;
-}
-
-/*
- * Probing is done in the process context, which allows us to cheat
- * and not to build a state machine for the discovery.
- */
-static int ub_probe(struct usb_interface *intf,
- const struct usb_device_id *dev_id)
-{
- struct ub_dev *sc;
- int nluns;
- int rc;
- int i;
-
- if (usb_usual_check_type(dev_id, USB_US_TYPE_UB))
- return -ENXIO;
-
- rc = -ENOMEM;
- if ((sc = kzalloc(sizeof(struct ub_dev), GFP_KERNEL)) == NULL)
- goto err_core;
- sc->lock = ub_next_lock();
- INIT_LIST_HEAD(&sc->luns);
- usb_init_urb(&sc->work_urb);
- tasklet_init(&sc->tasklet, ub_scsi_action, (unsigned long)sc);
- atomic_set(&sc->poison, 0);
- INIT_WORK(&sc->reset_work, ub_reset_task);
- init_waitqueue_head(&sc->reset_wait);
-
- init_timer(&sc->work_timer);
- sc->work_timer.data = (unsigned long) sc;
- sc->work_timer.function = ub_urb_timeout;
-
- ub_init_completion(&sc->work_done);
- sc->work_done.done = 1; /* A little yuk, but oh well... */
-
- sc->dev = interface_to_usbdev(intf);
- sc->intf = intf;
- // sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
- usb_set_intfdata(intf, sc);
- usb_get_dev(sc->dev);
- /*
- * Since we give the interface struct to the block level through
- * disk->driverfs_dev, we have to pin it. Otherwise, block_uevent
- * oopses on close after a disconnect (kernels 2.6.16 and up).
- */
- usb_get_intf(sc->intf);
-
- snprintf(sc->name, 12, DRV_NAME "(%d.%d)",
- sc->dev->bus->busnum, sc->dev->devnum);
-
- /* XXX Verify that we can handle the device (from descriptors) */
-
- if (ub_get_pipes(sc, sc->dev, intf) != 0)
- goto err_dev_desc;
-
- /*
- * At this point, all USB initialization is done, do upper layer.
- * We really hate halfway initialized structures, so from the
- * invariants perspective, this ub_dev is fully constructed at
- * this point.
- */
-
- /*
- * This is needed to clear toggles. It is a problem only if we do
- * `rmmod ub && modprobe ub` without disconnects, but we like that.
- */
-#if 0 /* iPod Mini fails if we do this (big white iPod works) */
- ub_probe_clear_stall(sc, sc->recv_bulk_pipe);
- ub_probe_clear_stall(sc, sc->send_bulk_pipe);
-#endif
-
- /*
- * The way this is used by the startup code is a little specific.
- * A SCSI check causes a USB stall. Our common case code sees it
- * and clears the check, after which the device is ready for use.
- * But if a check was not present, any command other than
- * TEST_UNIT_READY ends with a lockup (including REQUEST_SENSE).
- *
- * If we neglect to clear the SCSI check, the first real command fails
- * (which is the capacity readout). We clear that and retry, but why
- * causing spurious retries for no reason.
- *
- * Revalidation may start with its own TEST_UNIT_READY, but that one
- * has to succeed, so we clear checks with an additional one here.
- * In any case it's not our business how revaliadation is implemented.
- */
- for (i = 0; i < 3; i++) { /* Retries for the schwag key from KS'04 */
- if ((rc = ub_sync_tur(sc, NULL)) <= 0) break;
- if (rc != 0x6) break;
- msleep(10);
- }
-
- nluns = 1;
- for (i = 0; i < 3; i++) {
- if ((rc = ub_sync_getmaxlun(sc)) < 0)
- break;
- if (rc != 0) {
- nluns = rc;
- break;
- }
- msleep(100);
- }
-
- for (i = 0; i < nluns; i++) {
- ub_probe_lun(sc, i);
- }
- return 0;
-
-err_dev_desc:
- usb_set_intfdata(intf, NULL);
- usb_put_intf(sc->intf);
- usb_put_dev(sc->dev);
- kfree(sc);
-err_core:
- return rc;
-}
-
-static int ub_probe_lun(struct ub_dev *sc, int lnum)
-{
- struct ub_lun *lun;
- struct request_queue *q;
- struct gendisk *disk;
- int rc;
-
- rc = -ENOMEM;
- if ((lun = kzalloc(sizeof(struct ub_lun), GFP_KERNEL)) == NULL)
- goto err_alloc;
- lun->num = lnum;
-
- rc = -ENOSR;
- if ((lun->id = ub_id_get()) == -1)
- goto err_id;
-
- lun->udev = sc;
-
- snprintf(lun->name, 16, DRV_NAME "%c(%d.%d.%d)",
- lun->id + 'a', sc->dev->bus->busnum, sc->dev->devnum, lun->num);
-
- lun->removable = 1; /* XXX Query this from the device */
- lun->changed = 1; /* ub_revalidate clears only */
- ub_revalidate(sc, lun);
-
- rc = -ENOMEM;
- if ((disk = alloc_disk(UB_PARTS_PER_LUN)) == NULL)
- goto err_diskalloc;
-
- sprintf(disk->disk_name, DRV_NAME "%c", lun->id + 'a');
- disk->major = UB_MAJOR;
- disk->first_minor = lun->id * UB_PARTS_PER_LUN;
- disk->fops = &ub_bd_fops;
- disk->private_data = lun;
- disk->driverfs_dev = &sc->intf->dev;
-
- rc = -ENOMEM;
- if ((q = blk_init_queue(ub_request_fn, sc->lock)) == NULL)
- goto err_blkqinit;
-
- disk->queue = q;
-
- blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);
- blk_queue_max_segments(q, UB_MAX_REQ_SG);
- blk_queue_segment_boundary(q, 0xffffffff); /* Dubious. */
- blk_queue_max_hw_sectors(q, UB_MAX_SECTORS);
- blk_queue_logical_block_size(q, lun->capacity.bsize);
-
- lun->disk = disk;
- q->queuedata = lun;
- list_add(&lun->link, &sc->luns);
-
- set_capacity(disk, lun->capacity.nsec);
- if (lun->removable)
- disk->flags |= GENHD_FL_REMOVABLE;
-
- add_disk(disk);
-
- return 0;
-
-err_blkqinit:
- put_disk(disk);
-err_diskalloc:
- ub_id_put(lun->id);
-err_id:
- kfree(lun);
-err_alloc:
- return rc;
-}
-
-static void ub_disconnect(struct usb_interface *intf)
-{
- struct ub_dev *sc = usb_get_intfdata(intf);
- struct ub_lun *lun;
- unsigned long flags;
-
- /*
- * Prevent ub_bd_release from pulling the rug from under us.
- * XXX This is starting to look like a kref.
- * XXX Why not to take this ref at probe time?
- */
- spin_lock_irqsave(&ub_lock, flags);
- sc->openc++;
- spin_unlock_irqrestore(&ub_lock, flags);
-
- /*
- * Fence stall clearings, operations triggered by unlinkings and so on.
- * We do not attempt to unlink any URBs, because we do not trust the
- * unlink paths in HC drivers. Also, we get -84 upon disconnect anyway.
- */
- atomic_set(&sc->poison, 1);
-
- /*
- * Wait for reset to end, if any.
- */
- wait_event(sc->reset_wait, !sc->reset);
-
- /*
- * Blow away queued commands.
- *
- * Actually, this never works, because before we get here
- * the HCD terminates outstanding URB(s). It causes our
- * SCSI command queue to advance, commands fail to submit,
- * and the whole queue drains. So, we just use this code to
- * print warnings.
- */
- spin_lock_irqsave(sc->lock, flags);
- {
- struct ub_scsi_cmd *cmd;
- int cnt = 0;
- while ((cmd = ub_cmdq_peek(sc)) != NULL) {
- cmd->error = -ENOTCONN;
- cmd->state = UB_CMDST_DONE;
- ub_cmdq_pop(sc);
- (*cmd->done)(sc, cmd);
- cnt++;
- }
- if (cnt != 0) {
- printk(KERN_WARNING "%s: "
- "%d was queued after shutdown\n", sc->name, cnt);
- }
- }
- spin_unlock_irqrestore(sc->lock, flags);
-
- /*
- * Unregister the upper layer.
- */
- list_for_each_entry(lun, &sc->luns, link) {
- del_gendisk(lun->disk);
- /*
- * I wish I could do:
- * queue_flag_set(QUEUE_FLAG_DEAD, q);
- * As it is, we rely on our internal poisoning and let
- * the upper levels to spin furiously failing all the I/O.
- */
- }
-
- /*
- * Testing for -EINPROGRESS is always a bug, so we are bending
- * the rules a little.
- */
- spin_lock_irqsave(sc->lock, flags);
- if (sc->work_urb.status == -EINPROGRESS) { /* janitors: ignore */
- printk(KERN_WARNING "%s: "
- "URB is active after disconnect\n", sc->name);
- }
- spin_unlock_irqrestore(sc->lock, flags);
-
- /*
- * There is virtually no chance that other CPU runs a timeout so long
- * after ub_urb_complete should have called del_timer, but only if HCD
- * didn't forget to deliver a callback on unlink.
- */
- del_timer_sync(&sc->work_timer);
-
- /*
- * At this point there must be no commands coming from anyone
- * and no URBs left in transit.
- */
-
- ub_put(sc);
-}
-
-static struct usb_driver ub_driver = {
- .name = "ub",
- .probe = ub_probe,
- .disconnect = ub_disconnect,
- .id_table = ub_usb_ids,
- .pre_reset = ub_pre_reset,
- .post_reset = ub_post_reset,
-};
-
-static int __init ub_init(void)
-{
- int rc;
- int i;
-
- pr_info("'Low Performance USB Block' driver is deprecated. "
- "Please switch to usb-storage\n");
- for (i = 0; i < UB_QLOCK_NUM; i++)
- spin_lock_init(&ub_qlockv[i]);
-
- if ((rc = register_blkdev(UB_MAJOR, DRV_NAME)) != 0)
- goto err_regblkdev;
-
- if ((rc = usb_register(&ub_driver)) != 0)
- goto err_register;
-
- usb_usual_set_present(USB_US_TYPE_UB);
- return 0;
-
-err_register:
- unregister_blkdev(UB_MAJOR, DRV_NAME);
-err_regblkdev:
- return rc;
-}
-
-static void __exit ub_exit(void)
-{
- usb_deregister(&ub_driver);
-
- unregister_blkdev(UB_MAJOR, DRV_NAME);
- usb_usual_clear_present(USB_US_TYPE_UB);
-}
-
-module_init(ub_init);
-module_exit(ub_exit);
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c
index 10308cd..11f36e5 100644
--- a/drivers/bluetooth/ath3k.c
+++ b/drivers/bluetooth/ath3k.c
@@ -79,6 +79,7 @@
{ USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x0CF3, 0xE004) },
{ USB_DEVICE(0x0930, 0x0219) },
+ { USB_DEVICE(0x0489, 0xe057) },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE02C) },
@@ -104,6 +105,7 @@
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU22 with sflash firmware */
{ USB_DEVICE(0x0489, 0xE03C), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index e272214..cef3bac 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -98,6 +98,7 @@
{ USB_DEVICE(0x0a5c, 0x21e6) },
{ USB_DEVICE(0x0a5c, 0x21e8) },
{ USB_DEVICE(0x0a5c, 0x21f3) },
+ { USB_DEVICE(0x0a5c, 0x21f4) },
{ USB_DEVICE(0x413c, 0x8197) },
/* Foxconn - Hon Hai */
@@ -133,6 +134,7 @@
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0cf3, 0xe004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
+ { USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
/* Atheros AR5BBU12 with sflash firmware */
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
diff --git a/drivers/char/agp/intel-agp.h b/drivers/char/agp/intel-agp.h
index 5722642..6ec0fff 100644
--- a/drivers/char/agp/intel-agp.h
+++ b/drivers/char/agp/intel-agp.h
@@ -64,6 +64,7 @@
#define I830_PTE_SYSTEM_CACHED 0x00000006
/* GT PTE cache control fields */
#define GEN6_PTE_UNCACHED 0x00000002
+#define HSW_PTE_UNCACHED 0x00000000
#define GEN6_PTE_LLC 0x00000004
#define GEN6_PTE_LLC_MLC 0x00000006
#define GEN6_PTE_GFDT 0x00000008
@@ -239,16 +240,45 @@
#define PCI_DEVICE_ID_INTEL_IVYBRIDGE_S_GT2_IG 0x016A
#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_HB 0x0F00 /* VLV1 */
#define PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG 0x0F30
-#define PCI_DEVICE_ID_INTEL_HASWELL_HB 0x0400 /* Desktop */
+#define PCI_DEVICE_ID_INTEL_HASWELL_HB 0x0400 /* Desktop */
#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG 0x0402
#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG 0x0412
-#define PCI_DEVICE_ID_INTEL_HASWELL_M_HB 0x0404 /* Mobile */
+#define PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG 0x0422
+#define PCI_DEVICE_ID_INTEL_HASWELL_M_HB 0x0404 /* Mobile */
#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG 0x0406
#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG 0x0416
-#define PCI_DEVICE_ID_INTEL_HASWELL_S_HB 0x0408 /* Server */
+#define PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG 0x0426
+#define PCI_DEVICE_ID_INTEL_HASWELL_S_HB 0x0408 /* Server */
#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG 0x040a
#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG 0x041a
-#define PCI_DEVICE_ID_INTEL_HASWELL_SDV 0x0c16 /* SDV */
-#define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04
+#define PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG 0x042a
+#define PCI_DEVICE_ID_INTEL_HASWELL_E_HB 0x0c04
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG 0x0C02
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG 0x0C12
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG 0x0C22
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG 0x0C06
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG 0x0C16
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG 0x0C26
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG 0x0C0A
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG 0x0C1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG 0x0C2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG 0x0A02
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG 0x0A12
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG 0x0A22
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG 0x0A06
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG 0x0A16
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG 0x0A26
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG 0x0A0A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG 0x0A1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG 0x0A2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG 0x0D12
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG 0x0D22
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG 0x0D32
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG 0x0D16
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG 0x0D26
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG 0x0D36
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG 0x0D1A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG 0x0D2A
+#define PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG 0x0D3A
#endif
diff --git a/drivers/char/agp/intel-gtt.c b/drivers/char/agp/intel-gtt.c
index 9ed92ef..58e32f7 100644
--- a/drivers/char/agp/intel-gtt.c
+++ b/drivers/char/agp/intel-gtt.c
@@ -1156,6 +1156,30 @@
return true;
}
+static void haswell_write_entry(dma_addr_t addr, unsigned int entry,
+ unsigned int flags)
+{
+ unsigned int type_mask = flags & ~AGP_USER_CACHED_MEMORY_GFDT;
+ unsigned int gfdt = flags & AGP_USER_CACHED_MEMORY_GFDT;
+ u32 pte_flags;
+
+ if (type_mask == AGP_USER_MEMORY)
+ pte_flags = HSW_PTE_UNCACHED | I810_PTE_VALID;
+ else if (type_mask == AGP_USER_CACHED_MEMORY_LLC_MLC) {
+ pte_flags = GEN6_PTE_LLC_MLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
+ } else { /* set 'normal'/'cached' to LLC by default */
+ pte_flags = GEN6_PTE_LLC | I810_PTE_VALID;
+ if (gfdt)
+ pte_flags |= GEN6_PTE_GFDT;
+ }
+
+ /* gen6 has bit11-4 for physical addr bit39-32 */
+ addr |= (addr >> 28) & 0xff0;
+ writel(addr | pte_flags, intel_private.gtt + entry);
+}
+
static void gen6_write_entry(dma_addr_t addr, unsigned int entry,
unsigned int flags)
{
@@ -1382,6 +1406,15 @@
.check_flags = gen6_check_flags,
.chipset_flush = i9xx_chipset_flush,
};
+static const struct intel_gtt_driver haswell_gtt_driver = {
+ .gen = 6,
+ .setup = i9xx_setup,
+ .cleanup = gen6_cleanup,
+ .write_entry = haswell_write_entry,
+ .dma_mask_size = 40,
+ .check_flags = gen6_check_flags,
+ .chipset_flush = i9xx_chipset_flush,
+};
static const struct intel_gtt_driver valleyview_gtt_driver = {
.gen = 7,
.setup = i9xx_setup,
@@ -1499,19 +1532,77 @@
{ PCI_DEVICE_ID_INTEL_VALLEYVIEW_IG,
"ValleyView", &valleyview_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_D_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_M_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_S_GT1_IG,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
{ PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_IG,
- "Haswell", &sandybridge_gtt_driver },
- { PCI_DEVICE_ID_INTEL_HASWELL_SDV,
- "Haswell", &sandybridge_gtt_driver },
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_SDV_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_ULT_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_D_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_M_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT1_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_IG,
+ "Haswell", &haswell_gtt_driver },
+ { PCI_DEVICE_ID_INTEL_HASWELL_CRW_S_GT2_PLUS_IG,
+ "Haswell", &haswell_gtt_driver },
{ 0, NULL, NULL }
};
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
index d706bd0e..4fbdceb 100644
--- a/drivers/char/hw_random/omap-rng.c
+++ b/drivers/char/hw_random/omap-rng.c
@@ -160,7 +160,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int omap_rng_suspend(struct device *dev)
{
diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c
index 89682fa..c4be351 100644
--- a/drivers/char/tpm/tpm_tis.c
+++ b/drivers/char/tpm/tpm_tis.c
@@ -807,6 +807,7 @@
MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe");
#endif
+#ifdef CONFIG_PM_SLEEP
static int tpm_tis_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
@@ -816,6 +817,7 @@
return tpm_pm_resume(dev);
}
+#endif
static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume);
diff --git a/drivers/clocksource/cs5535-clockevt.c b/drivers/clocksource/cs5535-clockevt.c
index 540795c..d927938 100644
--- a/drivers/clocksource/cs5535-clockevt.c
+++ b/drivers/clocksource/cs5535-clockevt.c
@@ -53,7 +53,7 @@
#define MFGPT_PERIODIC (MFGPT_HZ / HZ)
/*
- * The MFPGT timers on the CS5536 provide us with suitable timers to use
+ * The MFGPT timers on the CS5536 provide us with suitable timers to use
* as clock event sources - not as good as a HPET or APIC, but certainly
* better than the PIT. This isn't a general purpose MFGPT driver, but
* a simplified one designed specifically to act as a clock event source.
@@ -144,7 +144,7 @@
timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
if (!timer) {
- printk(KERN_ERR DRV_NAME ": Could not allocate MFPGT timer\n");
+ printk(KERN_ERR DRV_NAME ": Could not allocate MFGPT timer\n");
return -ENODEV;
}
cs5535_event_clock = timer;
diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c
index cdc02ac..503996a 100644
--- a/drivers/cpufreq/pcc-cpufreq.c
+++ b/drivers/cpufreq/pcc-cpufreq.c
@@ -454,6 +454,7 @@
mem_resource->address_length);
if (pcch_virt_addr == NULL) {
pr_debug("probe: could not map shared mem region\n");
+ ret = -ENOMEM;
goto out_free;
}
pcch_hdr = pcch_virt_addr;
diff --git a/drivers/cpuidle/coupled.c b/drivers/cpuidle/coupled.c
index 2c9bf26..3265844 100644
--- a/drivers/cpuidle/coupled.c
+++ b/drivers/cpuidle/coupled.c
@@ -678,10 +678,22 @@
int cpu = (unsigned long)hcpu;
struct cpuidle_device *dev;
+ switch (action & ~CPU_TASKS_FROZEN) {
+ case CPU_UP_PREPARE:
+ case CPU_DOWN_PREPARE:
+ case CPU_ONLINE:
+ case CPU_DEAD:
+ case CPU_UP_CANCELED:
+ case CPU_DOWN_FAILED:
+ break;
+ default:
+ return NOTIFY_OK;
+ }
+
mutex_lock(&cpuidle_lock);
dev = per_cpu(cpuidle_devices, cpu);
- if (!dev->coupled)
+ if (!dev || !dev->coupled)
goto out;
switch (action & ~CPU_TASKS_FROZEN) {
diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c
index fcfeb3c..5084975 100644
--- a/drivers/dma/imx-dma.c
+++ b/drivers/dma/imx-dma.c
@@ -172,7 +172,8 @@
struct device_dma_parameters dma_parms;
struct dma_device dma_device;
void __iomem *base;
- struct clk *dma_clk;
+ struct clk *dma_ahb;
+ struct clk *dma_ipg;
spinlock_t lock;
struct imx_dma_2d_config slots_2d[IMX_DMA_2D_SLOTS];
struct imxdma_channel channel[IMX_DMA_CHANNELS];
@@ -976,10 +977,20 @@
return 0;
}
- imxdma->dma_clk = clk_get(NULL, "dma");
- if (IS_ERR(imxdma->dma_clk))
- return PTR_ERR(imxdma->dma_clk);
- clk_enable(imxdma->dma_clk);
+ imxdma->dma_ipg = devm_clk_get(&pdev->dev, "ipg");
+ if (IS_ERR(imxdma->dma_ipg)) {
+ ret = PTR_ERR(imxdma->dma_ipg);
+ goto err_clk;
+ }
+
+ imxdma->dma_ahb = devm_clk_get(&pdev->dev, "ahb");
+ if (IS_ERR(imxdma->dma_ahb)) {
+ ret = PTR_ERR(imxdma->dma_ahb);
+ goto err_clk;
+ }
+
+ clk_prepare_enable(imxdma->dma_ipg);
+ clk_prepare_enable(imxdma->dma_ahb);
/* reset DMA module */
imx_dmav1_writel(imxdma, DCR_DRST, DMA_DCR);
@@ -988,16 +999,14 @@
ret = request_irq(MX1_DMA_INT, dma_irq_handler, 0, "DMA", imxdma);
if (ret) {
dev_warn(imxdma->dev, "Can't register IRQ for DMA\n");
- kfree(imxdma);
- return ret;
+ goto err_enable;
}
ret = request_irq(MX1_DMA_ERR, imxdma_err_handler, 0, "DMA", imxdma);
if (ret) {
dev_warn(imxdma->dev, "Can't register ERRIRQ for DMA\n");
free_irq(MX1_DMA_INT, NULL);
- kfree(imxdma);
- return ret;
+ goto err_enable;
}
}
@@ -1094,7 +1103,10 @@
free_irq(MX1_DMA_INT, NULL);
free_irq(MX1_DMA_ERR, NULL);
}
-
+err_enable:
+ clk_disable_unprepare(imxdma->dma_ipg);
+ clk_disable_unprepare(imxdma->dma_ahb);
+err_clk:
kfree(imxdma);
return ret;
}
@@ -1114,7 +1126,9 @@
free_irq(MX1_DMA_ERR, NULL);
}
- kfree(imxdma);
+ clk_disable_unprepare(imxdma->dma_ipg);
+ clk_disable_unprepare(imxdma->dma_ahb);
+ kfree(imxdma);
return 0;
}
diff --git a/drivers/dma/tegra20-apb-dma.c b/drivers/dma/tegra20-apb-dma.c
index d52dbc6..24acd71 100644
--- a/drivers/dma/tegra20-apb-dma.c
+++ b/drivers/dma/tegra20-apb-dma.c
@@ -1119,15 +1119,21 @@
static int tegra_dma_alloc_chan_resources(struct dma_chan *dc)
{
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+ struct tegra_dma *tdma = tdc->tdma;
+ int ret;
dma_cookie_init(&tdc->dma_chan);
tdc->config_init = false;
- return 0;
+ ret = clk_prepare_enable(tdma->dma_clk);
+ if (ret < 0)
+ dev_err(tdc2dev(tdc), "clk_prepare_enable failed: %d\n", ret);
+ return ret;
}
static void tegra_dma_free_chan_resources(struct dma_chan *dc)
{
struct tegra_dma_channel *tdc = to_tegra_dma_chan(dc);
+ struct tegra_dma *tdma = tdc->tdma;
struct tegra_dma_desc *dma_desc;
struct tegra_dma_sg_req *sg_req;
@@ -1163,6 +1169,7 @@
list_del(&sg_req->node);
kfree(sg_req);
}
+ clk_disable_unprepare(tdma->dma_clk);
}
/* Tegra20 specific DMA controller information */
@@ -1255,6 +1262,13 @@
}
}
+ /* Enable clock before accessing registers */
+ ret = clk_prepare_enable(tdma->dma_clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n", ret);
+ goto err_pm_disable;
+ }
+
/* Reset DMA controller */
tegra_periph_reset_assert(tdma->dma_clk);
udelay(2);
@@ -1265,6 +1279,8 @@
tdma_write(tdma, TEGRA_APBDMA_CONTROL, 0);
tdma_write(tdma, TEGRA_APBDMA_IRQ_MASK_SET, 0xFFFFFFFFul);
+ clk_disable_unprepare(tdma->dma_clk);
+
INIT_LIST_HEAD(&tdma->dma_dev.channels);
for (i = 0; i < cdata->nr_channels; i++) {
struct tegra_dma_channel *tdc = &tdma->channels[i];
diff --git a/drivers/extcon/extcon_gpio.c b/drivers/extcon/extcon_gpio.c
index fe3db45..3cc152e 100644
--- a/drivers/extcon/extcon_gpio.c
+++ b/drivers/extcon/extcon_gpio.c
@@ -107,7 +107,8 @@
if (ret < 0)
return ret;
- ret = gpio_request_one(extcon_data->gpio, GPIOF_DIR_IN, pdev->name);
+ ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
+ pdev->name);
if (ret < 0)
goto err;
diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c
index 150d976..ae37181 100644
--- a/drivers/gpio/gpio-em.c
+++ b/drivers/gpio/gpio-em.c
@@ -266,7 +266,7 @@
return 0;
}
-static void __devexit em_gio_irq_domain_cleanup(struct em_gio_priv *p)
+static void em_gio_irq_domain_cleanup(struct em_gio_priv *p)
{
struct gpio_em_config *pdata = p->pdev->dev.platform_data;
diff --git a/drivers/gpio/gpio-langwell.c b/drivers/gpio/gpio-langwell.c
index a1c8754..202a992 100644
--- a/drivers/gpio/gpio-langwell.c
+++ b/drivers/gpio/gpio-langwell.c
@@ -339,7 +339,7 @@
resource_size_t start, len;
struct lnw_gpio *lnw;
u32 gpio_base;
- int retval = 0;
+ int retval;
int ngpio = id->driver_data;
retval = pci_enable_device(pdev);
@@ -357,6 +357,7 @@
base = ioremap_nocache(start, len);
if (!base) {
dev_err(&pdev->dev, "error mapping bar1\n");
+ retval = -EFAULT;
goto err3;
}
gpio_base = *((u32 *)base + 1);
@@ -381,8 +382,10 @@
lnw->domain = irq_domain_add_linear(pdev->dev.of_node, ngpio,
&lnw_gpio_irq_ops, lnw);
- if (!lnw->domain)
+ if (!lnw->domain) {
+ retval = -ENOMEM;
goto err3;
+ }
lnw->reg_base = base;
lnw->chip.label = dev_name(&pdev->dev);
diff --git a/drivers/gpio/gpio-msic.c b/drivers/gpio/gpio-msic.c
index 71a838f..b389862 100644
--- a/drivers/gpio/gpio-msic.c
+++ b/drivers/gpio/gpio-msic.c
@@ -99,7 +99,7 @@
if (offset < 20)
return INTEL_MSIC_GPIO0HV0CTLO - offset + 16;
- return INTEL_MSIC_GPIO1HV0CTLO + offset + 20;
+ return INTEL_MSIC_GPIO1HV0CTLO - offset + 20;
}
static int msic_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index 4db460b..80f44bb 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -465,9 +465,8 @@
goto out_iounmap;
port->bgc.gc.to_irq = mxc_gpio_to_irq;
- port->bgc.gc.base = pdev->id * 32;
- port->bgc.dir = port->bgc.read_reg(port->bgc.reg_dir);
- port->bgc.data = port->bgc.read_reg(port->bgc.reg_set);
+ port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
+ pdev->id * 32;
err = gpiochip_add(&port->bgc.gc);
if (err)
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 58a6a63..9cac88a 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -62,6 +62,7 @@
#ifdef CONFIG_OF
static struct irq_domain *domain;
+static struct device_node *pxa_gpio_of_node;
#endif
struct pxa_gpio_chip {
@@ -277,6 +278,24 @@
(value ? GPSR_OFFSET : GPCR_OFFSET));
}
+#ifdef CONFIG_OF_GPIO
+static int pxa_gpio_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec,
+ u32 *flags)
+{
+ if (gpiospec->args[0] > pxa_last_gpio)
+ return -EINVAL;
+
+ if (gc != &pxa_gpio_chips[gpiospec->args[0] / 32].chip)
+ return -EINVAL;
+
+ if (flags)
+ *flags = gpiospec->args[1];
+
+ return gpiospec->args[0] % 32;
+}
+#endif
+
static int __devinit pxa_init_gpio_chip(int gpio_end,
int (*set_wake)(unsigned int, unsigned int))
{
@@ -304,6 +323,11 @@
c->get = pxa_gpio_get;
c->set = pxa_gpio_set;
c->to_irq = pxa_gpio_to_irq;
+#ifdef CONFIG_OF_GPIO
+ c->of_node = pxa_gpio_of_node;
+ c->of_xlate = pxa_gpio_of_xlate;
+ c->of_gpio_n_cells = 2;
+#endif
/* number of GPIOs on last bank may be less than 32 */
c->ngpio = (gpio + 31 > gpio_end) ? (gpio_end - gpio + 1) : 32;
@@ -488,6 +512,7 @@
return count;
}
+#ifdef CONFIG_OF
static struct of_device_id pxa_gpio_dt_ids[] = {
{ .compatible = "mrvl,pxa-gpio" },
{ .compatible = "mrvl,mmp-gpio", .data = (void *)MMP_GPIO },
@@ -505,9 +530,9 @@
const struct irq_domain_ops pxa_irq_domain_ops = {
.map = pxa_irq_domain_map,
+ .xlate = irq_domain_xlate_twocell,
};
-#ifdef CONFIG_OF
static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev)
{
int ret, nr_banks, nr_gpios, irq_base;
@@ -545,6 +570,7 @@
}
domain = irq_domain_add_legacy(np, nr_gpios, irq_base, 0,
&pxa_irq_domain_ops, NULL);
+ pxa_gpio_of_node = np;
return 0;
err:
iounmap(gpio_reg_base);
@@ -653,7 +679,7 @@
.probe = pxa_gpio_probe,
.driver = {
.name = "pxa-gpio",
- .of_match_table = pxa_gpio_dt_ids,
+ .of_match_table = of_match_ptr(pxa_gpio_dt_ids),
},
};
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 92f7b2b..ba126cc 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -2454,12 +2454,6 @@
},
}, {
.chip = {
- .base = EXYNOS5_GPC4(0),
- .ngpio = EXYNOS5_GPIO_C4_NR,
- .label = "GPC4",
- },
- }, {
- .chip = {
.base = EXYNOS5_GPD0(0),
.ngpio = EXYNOS5_GPIO_D0_NR,
.label = "GPD0",
@@ -2513,6 +2507,12 @@
.label = "GPY6",
},
}, {
+ .chip = {
+ .base = EXYNOS5_GPC4(0),
+ .ngpio = EXYNOS5_GPIO_C4_NR,
+ .label = "GPC4",
+ },
+ }, {
.config = &samsung_gpio_cfgs[9],
.irq_base = IRQ_EINT(0),
.chip = {
@@ -2836,7 +2836,7 @@
}
/* need to set base address for gpc4 */
- exynos5_gpios_1[11].base = gpio_base1 + 0x2E0;
+ exynos5_gpios_1[20].base = gpio_base1 + 0x2E0;
/* need to set base address for gpx */
chip = &exynos5_gpios_1[21];
diff --git a/drivers/gpio/gpio-sch.c b/drivers/gpio/gpio-sch.c
index 424dce8..8707d45 100644
--- a/drivers/gpio/gpio-sch.c
+++ b/drivers/gpio/gpio-sch.c
@@ -241,7 +241,8 @@
break;
default:
- return -ENODEV;
+ err = -ENODEV;
+ goto err_sch_gpio_core;
}
sch_gpio_core.dev = &pdev->dev;
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 23120c0..90e2808 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -22,6 +22,7 @@
config DRM_USB
tristate
depends on DRM
+ depends on USB_ARCH_HAS_HCD
select USB
config DRM_KMS_HELPER
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
index 66d4a28..0303935 100644
--- a/drivers/gpu/drm/drm_edid_load.c
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -119,7 +119,7 @@
{
const struct firmware *fw;
struct platform_device *pdev;
- u8 *fwdata = NULL, *edid;
+ u8 *fwdata = NULL, *edid, *new_edid;
int fwsize, expected;
int builtin = 0, err = 0;
int i, valid_extensions = 0;
@@ -195,12 +195,14 @@
"\"%s\" for connector \"%s\"\n", valid_extensions,
edid[0x7e], name, connector_name);
edid[0x7e] = valid_extensions;
- edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
+ new_edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
GFP_KERNEL);
- if (edid == NULL) {
+ if (new_edid == NULL) {
err = -ENOMEM;
+ kfree(edid);
goto relfw_out;
}
+ edid = new_edid;
}
connector->display_info.raw_edid = edid;
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index b7adb4a..28637c1 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -706,9 +706,6 @@
p->crtc_vblank_end = max(p->crtc_vsync_end, p->crtc_vtotal);
p->crtc_hblank_start = min(p->crtc_hsync_start, p->crtc_hdisplay);
p->crtc_hblank_end = max(p->crtc_hsync_end, p->crtc_htotal);
-
- p->crtc_hadjusted = false;
- p->crtc_vadjusted = false;
}
EXPORT_SYMBOL(drm_mode_set_crtcinfo);
diff --git a/drivers/gpu/drm/drm_proc.c b/drivers/gpu/drm/drm_proc.c
index 371c695..da457b1 100644
--- a/drivers/gpu/drm/drm_proc.c
+++ b/drivers/gpu/drm/drm_proc.c
@@ -89,7 +89,7 @@
* Create a given set of proc files represented by an array of
* gdm_proc_lists in the given root directory.
*/
-int drm_proc_create_files(struct drm_info_list *files, int count,
+static int drm_proc_create_files(struct drm_info_list *files, int count,
struct proc_dir_entry *root, struct drm_minor *minor)
{
struct drm_device *dev = minor->dev;
@@ -172,7 +172,7 @@
return 0;
}
-int drm_proc_remove_files(struct drm_info_list *files, int count,
+static int drm_proc_remove_files(struct drm_info_list *files, int count,
struct drm_minor *minor)
{
struct list_head *pos, *q;
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
index ed22612..a24ffbe 100644
--- a/drivers/gpu/drm/i915/i915_drv.c
+++ b/drivers/gpu/drm/i915/i915_drv.c
@@ -346,11 +346,40 @@
INTEL_VGA_DEVICE(0x016a, &intel_ivybridge_d_info), /* GT2 server */
INTEL_VGA_DEVICE(0x0402, &intel_haswell_d_info), /* GT1 desktop */
INTEL_VGA_DEVICE(0x0412, &intel_haswell_d_info), /* GT2 desktop */
+ INTEL_VGA_DEVICE(0x0422, &intel_haswell_d_info), /* GT2 desktop */
INTEL_VGA_DEVICE(0x040a, &intel_haswell_d_info), /* GT1 server */
INTEL_VGA_DEVICE(0x041a, &intel_haswell_d_info), /* GT2 server */
+ INTEL_VGA_DEVICE(0x042a, &intel_haswell_d_info), /* GT2 server */
INTEL_VGA_DEVICE(0x0406, &intel_haswell_m_info), /* GT1 mobile */
INTEL_VGA_DEVICE(0x0416, &intel_haswell_m_info), /* GT2 mobile */
- INTEL_VGA_DEVICE(0x0c16, &intel_haswell_d_info), /* SDV */
+ INTEL_VGA_DEVICE(0x0426, &intel_haswell_m_info), /* GT2 mobile */
+ INTEL_VGA_DEVICE(0x0C02, &intel_haswell_d_info), /* SDV GT1 desktop */
+ INTEL_VGA_DEVICE(0x0C12, &intel_haswell_d_info), /* SDV GT2 desktop */
+ INTEL_VGA_DEVICE(0x0C22, &intel_haswell_d_info), /* SDV GT2 desktop */
+ INTEL_VGA_DEVICE(0x0C0A, &intel_haswell_d_info), /* SDV GT1 server */
+ INTEL_VGA_DEVICE(0x0C1A, &intel_haswell_d_info), /* SDV GT2 server */
+ INTEL_VGA_DEVICE(0x0C2A, &intel_haswell_d_info), /* SDV GT2 server */
+ INTEL_VGA_DEVICE(0x0C06, &intel_haswell_m_info), /* SDV GT1 mobile */
+ INTEL_VGA_DEVICE(0x0C16, &intel_haswell_m_info), /* SDV GT2 mobile */
+ INTEL_VGA_DEVICE(0x0C26, &intel_haswell_m_info), /* SDV GT2 mobile */
+ INTEL_VGA_DEVICE(0x0A02, &intel_haswell_d_info), /* ULT GT1 desktop */
+ INTEL_VGA_DEVICE(0x0A12, &intel_haswell_d_info), /* ULT GT2 desktop */
+ INTEL_VGA_DEVICE(0x0A22, &intel_haswell_d_info), /* ULT GT2 desktop */
+ INTEL_VGA_DEVICE(0x0A0A, &intel_haswell_d_info), /* ULT GT1 server */
+ INTEL_VGA_DEVICE(0x0A1A, &intel_haswell_d_info), /* ULT GT2 server */
+ INTEL_VGA_DEVICE(0x0A2A, &intel_haswell_d_info), /* ULT GT2 server */
+ INTEL_VGA_DEVICE(0x0A06, &intel_haswell_m_info), /* ULT GT1 mobile */
+ INTEL_VGA_DEVICE(0x0A16, &intel_haswell_m_info), /* ULT GT2 mobile */
+ INTEL_VGA_DEVICE(0x0A26, &intel_haswell_m_info), /* ULT GT2 mobile */
+ INTEL_VGA_DEVICE(0x0D12, &intel_haswell_d_info), /* CRW GT1 desktop */
+ INTEL_VGA_DEVICE(0x0D22, &intel_haswell_d_info), /* CRW GT2 desktop */
+ INTEL_VGA_DEVICE(0x0D32, &intel_haswell_d_info), /* CRW GT2 desktop */
+ INTEL_VGA_DEVICE(0x0D1A, &intel_haswell_d_info), /* CRW GT1 server */
+ INTEL_VGA_DEVICE(0x0D2A, &intel_haswell_d_info), /* CRW GT2 server */
+ INTEL_VGA_DEVICE(0x0D3A, &intel_haswell_d_info), /* CRW GT2 server */
+ INTEL_VGA_DEVICE(0x0D16, &intel_haswell_m_info), /* CRW GT1 mobile */
+ INTEL_VGA_DEVICE(0x0D26, &intel_haswell_m_info), /* CRW GT2 mobile */
+ INTEL_VGA_DEVICE(0x0D36, &intel_haswell_m_info), /* CRW GT2 mobile */
INTEL_VGA_DEVICE(0x0f30, &intel_valleyview_m_info),
INTEL_VGA_DEVICE(0x0157, &intel_valleyview_m_info),
INTEL_VGA_DEVICE(0x0155, &intel_valleyview_d_info),
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 5c4657a..489e2b1 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -2365,6 +2365,10 @@
/* Flush everything onto the inactive list. */
for_each_ring(ring, dev_priv, i) {
+ ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
+ if (ret)
+ return ret;
+
ret = i915_ring_idle(ring);
if (ret)
return ret;
@@ -2372,10 +2376,6 @@
/* Is the device fubar? */
if (WARN_ON(!list_empty(&ring->gpu_write_list)))
return -EBUSY;
-
- ret = i915_switch_context(ring, NULL, DEFAULT_CONTEXT_ID);
- if (ret)
- return ret;
}
return 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_context.c b/drivers/gpu/drm/i915/i915_gem_context.c
index da8b01f..a9d58d7 100644
--- a/drivers/gpu/drm/i915/i915_gem_context.c
+++ b/drivers/gpu/drm/i915/i915_gem_context.c
@@ -451,7 +451,6 @@
struct drm_i915_file_private *file_priv = NULL;
struct i915_hw_context *to;
struct drm_i915_gem_object *from_obj = ring->last_context_obj;
- int ret;
if (dev_priv->hw_contexts_disabled)
return 0;
diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
index 5af631e..ff2819e 100644
--- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c
+++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c
@@ -291,6 +291,16 @@
target_i915_obj = to_intel_bo(target_obj);
target_offset = target_i915_obj->gtt_offset;
+ /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and
+ * pipe_control writes because the gpu doesn't properly redirect them
+ * through the ppgtt for non_secure batchbuffers. */
+ if (unlikely(IS_GEN6(dev) &&
+ reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
+ !target_i915_obj->has_global_gtt_mapping)) {
+ i915_gem_gtt_bind_object(target_i915_obj,
+ target_i915_obj->cache_level);
+ }
+
/* The target buffer should have appeared before us in the
* exec_object list, so it should have a GTT space bound by now.
*/
@@ -399,16 +409,6 @@
io_mapping_unmap_atomic(reloc_page);
}
- /* Sandybridge PPGTT errata: We need a global gtt mapping for MI and
- * pipe_control writes because the gpu doesn't properly redirect them
- * through the ppgtt for non_secure batchbuffers. */
- if (unlikely(IS_GEN6(dev) &&
- reloc->write_domain == I915_GEM_DOMAIN_INSTRUCTION &&
- !target_i915_obj->has_global_gtt_mapping)) {
- i915_gem_gtt_bind_object(target_i915_obj,
- target_i915_obj->cache_level);
- }
-
/* and update the user's relocation entry */
reloc->presumed_offset = target_offset;
diff --git a/drivers/gpu/drm/i915/i915_gem_gtt.c b/drivers/gpu/drm/i915/i915_gem_gtt.c
index 9fd25a4..d9a5372 100644
--- a/drivers/gpu/drm/i915/i915_gem_gtt.c
+++ b/drivers/gpu/drm/i915/i915_gem_gtt.c
@@ -261,7 +261,10 @@
pte_flags |= GEN6_PTE_CACHE_LLC;
break;
case I915_CACHE_NONE:
- pte_flags |= GEN6_PTE_UNCACHED;
+ if (IS_HASWELL(dev))
+ pte_flags |= HSW_PTE_UNCACHED;
+ else
+ pte_flags |= GEN6_PTE_UNCACHED;
break;
default:
BUG();
@@ -361,7 +364,8 @@
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
- if (dev_priv->mm.gtt->needs_dmar)
+ /* don't map imported dma buf objects */
+ if (dev_priv->mm.gtt->needs_dmar && !obj->sg_table)
return intel_gtt_map_memory(obj->pages,
obj->base.size >> PAGE_SHIFT,
&obj->sg_list,
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index acc99b2..28725ce 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -115,6 +115,7 @@
#define GEN6_PTE_VALID (1 << 0)
#define GEN6_PTE_UNCACHED (1 << 1)
+#define HSW_PTE_UNCACHED (0)
#define GEN6_PTE_CACHE_LLC (2 << 1)
#define GEN6_PTE_CACHE_LLC_MLC (3 << 1)
#define GEN6_PTE_CACHE_BITS (3 << 1)
diff --git a/drivers/gpu/drm/i915/i915_sysfs.c b/drivers/gpu/drm/i915/i915_sysfs.c
index 2f5388a..7631807 100644
--- a/drivers/gpu/drm/i915/i915_sysfs.c
+++ b/drivers/gpu/drm/i915/i915_sysfs.c
@@ -32,6 +32,7 @@
#include "intel_drv.h"
#include "i915_drv.h"
+#ifdef CONFIG_PM
static u32 calc_residency(struct drm_device *dev, const u32 reg)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -224,3 +225,14 @@
device_remove_bin_file(&dev->primary->kdev, &dpf_attrs);
sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group);
}
+#else
+void i915_setup_sysfs(struct drm_device *dev)
+{
+ return;
+}
+
+void i915_teardown_sysfs(struct drm_device *dev)
+{
+ return;
+}
+#endif /* CONFIG_PM */
diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
index 7ed4a41..23bdc8c 100644
--- a/drivers/gpu/drm/i915/intel_crt.c
+++ b/drivers/gpu/drm/i915/intel_crt.c
@@ -326,6 +326,36 @@
return ret;
}
+static struct edid *intel_crt_get_edid(struct drm_connector *connector,
+ struct i2c_adapter *i2c)
+{
+ struct edid *edid;
+
+ edid = drm_get_edid(connector, i2c);
+
+ if (!edid && !intel_gmbus_is_forced_bit(i2c)) {
+ DRM_DEBUG_KMS("CRT GMBUS EDID read failed, retry using GPIO bit-banging\n");
+ intel_gmbus_force_bit(i2c, true);
+ edid = drm_get_edid(connector, i2c);
+ intel_gmbus_force_bit(i2c, false);
+ }
+
+ return edid;
+}
+
+/* local version of intel_ddc_get_modes() to use intel_crt_get_edid() */
+static int intel_crt_ddc_get_modes(struct drm_connector *connector,
+ struct i2c_adapter *adapter)
+{
+ struct edid *edid;
+
+ edid = intel_crt_get_edid(connector, adapter);
+ if (!edid)
+ return 0;
+
+ return intel_connector_update_modes(connector, edid);
+}
+
static bool intel_crt_detect_ddc(struct drm_connector *connector)
{
struct intel_crt *crt = intel_attached_crt(connector);
@@ -336,7 +366,7 @@
BUG_ON(crt->base.type != INTEL_OUTPUT_ANALOG);
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
- edid = drm_get_edid(connector, i2c);
+ edid = intel_crt_get_edid(connector, i2c);
if (edid) {
bool is_digital = edid->input & DRM_EDID_INPUT_DIGITAL;
@@ -544,13 +574,13 @@
struct i2c_adapter *i2c;
i2c = intel_gmbus_get_adapter(dev_priv, dev_priv->crt_ddc_pin);
- ret = intel_ddc_get_modes(connector, i2c);
+ ret = intel_crt_ddc_get_modes(connector, i2c);
if (ret || !IS_G4X(dev))
return ret;
/* Try to probe digital port for output in DVI-I -> VGA mode. */
i2c = intel_gmbus_get_adapter(dev_priv, GMBUS_PORT_DPB);
- return intel_ddc_get_modes(connector, i2c);
+ return intel_crt_ddc_get_modes(connector, i2c);
}
static int intel_crt_set_property(struct drm_connector *connector,
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index f615976..a69a3d0 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -869,6 +869,7 @@
unsigned long bestppm, ppm, absppm;
int dotclk, flag;
+ flag = 0;
dotclk = target * 1000;
bestppm = 1000000;
ppm = absppm = 0;
@@ -3753,17 +3754,6 @@
continue;
}
- if (intel_encoder->type == INTEL_OUTPUT_EDP) {
- /* Use VBT settings if we have an eDP panel */
- unsigned int edp_bpc = dev_priv->edp.bpp / 3;
-
- if (edp_bpc < display_bpc) {
- DRM_DEBUG_KMS("clamping display bpc (was %d) to eDP (%d)\n", display_bpc, edp_bpc);
- display_bpc = edp_bpc;
- }
- continue;
- }
-
/* Not one of the known troublemakers, check the EDID */
list_for_each_entry(connector, &dev->mode_config.connector_list,
head) {
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 0a56b9a..a6c426a 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -1174,10 +1174,14 @@
WARN(!intel_dp->want_panel_vdd, "Need VDD to turn off panel\n");
pp = ironlake_get_pp_control(dev_priv);
- pp &= ~(POWER_TARGET_ON | PANEL_POWER_RESET | EDP_BLC_ENABLE);
+ /* We need to switch off panel power _and_ force vdd, for otherwise some
+ * panels get very unhappy and cease to work. */
+ pp &= ~(POWER_TARGET_ON | EDP_FORCE_VDD | PANEL_POWER_RESET | EDP_BLC_ENABLE);
I915_WRITE(PCH_PP_CONTROL, pp);
POSTING_READ(PCH_PP_CONTROL);
+ intel_dp->want_panel_vdd = false;
+
ironlake_wait_panel_off(intel_dp);
}
@@ -1287,11 +1291,9 @@
* ensure that we have vdd while we switch off the panel. */
ironlake_edp_panel_vdd_on(intel_dp);
ironlake_edp_backlight_off(intel_dp);
- ironlake_edp_panel_off(intel_dp);
-
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
+ ironlake_edp_panel_off(intel_dp);
intel_dp_link_down(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, false);
}
static void intel_dp_commit(struct drm_encoder *encoder)
@@ -1326,11 +1328,9 @@
/* Switching the panel off requires vdd. */
ironlake_edp_panel_vdd_on(intel_dp);
ironlake_edp_backlight_off(intel_dp);
- ironlake_edp_panel_off(intel_dp);
-
intel_dp_sink_dpms(intel_dp, mode);
+ ironlake_edp_panel_off(intel_dp);
intel_dp_link_down(intel_dp);
- ironlake_edp_panel_vdd_off(intel_dp, false);
if (is_cpu_edp(intel_dp))
ironlake_edp_pll_off(encoder);
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 8435355..cd54cf8 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -46,15 +46,16 @@
})
#define wait_for_atomic_us(COND, US) ({ \
- int i, ret__ = -ETIMEDOUT; \
- for (i = 0; i < (US); i++) { \
- if ((COND)) { \
- ret__ = 0; \
- break; \
- } \
- udelay(1); \
- } \
- ret__; \
+ unsigned long timeout__ = jiffies + usecs_to_jiffies(US); \
+ int ret__ = 0; \
+ while (!(COND)) { \
+ if (time_after(jiffies, timeout__)) { \
+ ret__ = -ETIMEDOUT; \
+ break; \
+ } \
+ cpu_relax(); \
+ } \
+ ret__; \
})
#define wait_for(COND, MS) _wait_for(COND, MS, 1)
@@ -341,6 +342,8 @@
int interval;
};
+int intel_connector_update_modes(struct drm_connector *connector,
+ struct edid *edid);
int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
extern void intel_attach_force_audio_property(struct drm_connector *connector);
@@ -380,7 +383,6 @@
const struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode);
extern u32 intel_panel_get_max_backlight(struct drm_device *dev);
-extern u32 intel_panel_get_backlight(struct drm_device *dev);
extern void intel_panel_set_backlight(struct drm_device *dev, u32 level);
extern int intel_panel_setup_backlight(struct drm_device *dev);
extern void intel_panel_enable_backlight(struct drm_device *dev,
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 1991a44..b9755f6 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -486,9 +486,6 @@
bus->dev_priv = dev_priv;
bus->adapter.algo = &gmbus_algorithm;
- ret = i2c_add_adapter(&bus->adapter);
- if (ret)
- goto err;
/* By default use a conservative clock rate */
bus->reg0 = port | GMBUS_RATE_100KHZ;
@@ -498,6 +495,10 @@
bus->force_bit = true;
intel_gpio_setup(bus, port);
+
+ ret = i2c_add_adapter(&bus->adapter);
+ if (ret)
+ goto err;
}
intel_i2c_reset(dev_priv->dev);
@@ -540,9 +541,6 @@
struct drm_i915_private *dev_priv = dev->dev_private;
int i;
- if (dev_priv->gmbus == NULL)
- return;
-
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
i2c_del_adapter(&bus->adapter);
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 45848b9..29b7259 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -33,6 +33,25 @@
#include "i915_drv.h"
/**
+ * intel_connector_update_modes - update connector from edid
+ * @connector: DRM connector device to use
+ * @edid: previously read EDID information
+ */
+int intel_connector_update_modes(struct drm_connector *connector,
+ struct edid *edid)
+{
+ int ret;
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ drm_edid_to_eld(connector, edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+
+ return ret;
+}
+
+/**
* intel_ddc_get_modes - get modelist from monitor
* @connector: DRM connector device to use
* @adapter: i2c adapter
@@ -43,18 +62,12 @@
struct i2c_adapter *adapter)
{
struct edid *edid;
- int ret = 0;
edid = drm_get_edid(connector, adapter);
- if (edid) {
- drm_mode_connector_update_edid_property(connector, edid);
- ret = drm_add_edid_modes(connector, edid);
- drm_edid_to_eld(connector, edid);
- connector->display_info.raw_edid = NULL;
- kfree(edid);
- }
+ if (!edid)
+ return 0;
- return ret;
+ return intel_connector_update_modes(connector, edid);
}
static const struct drm_prop_enum_list force_audio_names[] = {
diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c
index 10c7d39..3df4f5f 100644
--- a/drivers/gpu/drm/i915/intel_panel.c
+++ b/drivers/gpu/drm/i915/intel_panel.c
@@ -213,7 +213,7 @@
return val;
}
-u32 intel_panel_get_backlight(struct drm_device *dev)
+static u32 intel_panel_get_backlight(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
u32 val;
@@ -311,9 +311,6 @@
if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
- dev_priv->backlight_enabled = true;
- intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
-
if (INTEL_INFO(dev)->gen >= 4) {
uint32_t reg, tmp;
@@ -326,7 +323,7 @@
* we don't track the backlight dpms state, hence check whether
* we have to do anything first. */
if (tmp & BLM_PWM_ENABLE)
- return;
+ goto set_level;
if (dev_priv->num_pipe == 3)
tmp &= ~BLM_PIPE_SELECT_IVB;
@@ -347,6 +344,14 @@
I915_WRITE(BLC_PWM_PCH_CTL1, tmp);
}
}
+
+set_level:
+ /* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1.
+ * BLC_PWM_CPU_CTL may be cleared to zero automatically when these
+ * registers are set.
+ */
+ dev_priv->backlight_enabled = true;
+ intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
}
static void intel_panel_init_backlight(struct drm_device *dev)
diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c
index 94aabca..1881c8c 100644
--- a/drivers/gpu/drm/i915/intel_pm.c
+++ b/drivers/gpu/drm/i915/intel_pm.c
@@ -2441,17 +2441,10 @@
dev_priv->max_delay << 24 |
dev_priv->min_delay << 16);
- if (IS_HASWELL(dev)) {
- I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
- I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
- I915_WRITE(GEN6_RP_UP_EI, 66000);
- I915_WRITE(GEN6_RP_DOWN_EI, 350000);
- } else {
- I915_WRITE(GEN6_RP_UP_THRESHOLD, 10000);
- I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 1000000);
- I915_WRITE(GEN6_RP_UP_EI, 100000);
- I915_WRITE(GEN6_RP_DOWN_EI, 5000000);
- }
+ I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
+ I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
+ I915_WRITE(GEN6_RP_UP_EI, 66000);
+ I915_WRITE(GEN6_RP_DOWN_EI, 350000);
I915_WRITE(GEN6_RP_IDLE_HYSTERSIS, 10);
I915_WRITE(GEN6_RP_CONTROL,
@@ -3963,6 +3956,7 @@
DRM_ERROR("Force wake wait timed out\n");
I915_WRITE_NOTRACE(FORCEWAKE, 1);
+ POSTING_READ(FORCEWAKE);
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
DRM_ERROR("Force wake wait timed out\n");
@@ -3983,6 +3977,7 @@
DRM_ERROR("Force wake wait timed out\n");
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1));
+ POSTING_READ(FORCEWAKE_MT);
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
DRM_ERROR("Force wake wait timed out\n");
@@ -4018,14 +4013,14 @@
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE, 0);
- /* The below doubles as a POSTING_READ */
+ POSTING_READ(FORCEWAKE);
gen6_gt_check_fifodbg(dev_priv);
}
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
{
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1));
- /* The below doubles as a POSTING_READ */
+ POSTING_READ(FORCEWAKE_MT);
gen6_gt_check_fifodbg(dev_priv);
}
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index bf0195a..e2a73b3 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -227,31 +227,36 @@
* number of bits based on the write domains has little performance
* impact.
*/
- flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
- flags |= PIPE_CONTROL_TLB_INVALIDATE;
- flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
- flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
- flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
- /*
- * Ensure that any following seqno writes only happen when the render
- * cache is indeed flushed (but only if the caller actually wants that).
- */
- if (flush_domains)
+ if (flush_domains) {
+ flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
+ flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
+ /*
+ * Ensure that any following seqno writes only happen
+ * when the render cache is indeed flushed.
+ */
flags |= PIPE_CONTROL_CS_STALL;
+ }
+ if (invalidate_domains) {
+ flags |= PIPE_CONTROL_TLB_INVALIDATE;
+ flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
+ flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
+ /*
+ * TLB invalidate requires a post-sync write.
+ */
+ flags |= PIPE_CONTROL_QW_WRITE;
+ }
- ret = intel_ring_begin(ring, 6);
+ ret = intel_ring_begin(ring, 4);
if (ret)
return ret;
- intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
+ intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
intel_ring_emit(ring, flags);
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
- intel_ring_emit(ring, 0); /* lower dword */
- intel_ring_emit(ring, 0); /* uppwer dword */
- intel_ring_emit(ring, MI_NOOP);
+ intel_ring_emit(ring, 0);
intel_ring_advance(ring);
return 0;
@@ -289,8 +294,6 @@
I915_WRITE_HEAD(ring, 0);
ring->write_tail(ring, 0);
- /* Initialize the ring. */
- I915_WRITE_START(ring, obj->gtt_offset);
head = I915_READ_HEAD(ring) & HEAD_ADDR;
/* G45 ring initialization fails to reset head to zero */
@@ -316,6 +319,11 @@
}
}
+ /* Initialize the ring. This must happen _after_ we've cleared the ring
+ * registers with the above sequence (the readback of the HEAD registers
+ * also enforces ordering), otherwise the hw might lose the new ring
+ * register values. */
+ I915_WRITE_START(ring, obj->gtt_offset);
I915_WRITE_CTL(ring,
((ring->size - PAGE_SIZE) & RING_NR_PAGES)
| RING_VALID);
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 26a6a4d..d81bb0b 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -444,13 +444,16 @@
struct i2c_msg *msgs;
int i, ret = true;
+ /* Would be simpler to allocate both in one go ? */
buf = (u8 *)kzalloc(args_len * 2 + 2, GFP_KERNEL);
if (!buf)
return false;
msgs = kcalloc(args_len + 3, sizeof(*msgs), GFP_KERNEL);
- if (!msgs)
+ if (!msgs) {
+ kfree(buf);
return false;
+ }
intel_sdvo_debug_write(intel_sdvo, cmd, args, args_len);
@@ -1689,6 +1692,7 @@
edid = intel_sdvo_get_edid(connector);
if (edid != NULL && edid->input & DRM_EDID_INPUT_DIGITAL)
has_audio = drm_detect_monitor_audio(edid);
+ kfree(edid);
return has_audio;
}
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index a4d7c50..b69642d 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -468,10 +468,11 @@
{
unsigned int vcomax, vcomin, pllreffreq;
unsigned int delta, tmpdelta;
- unsigned int testr, testn, testm, testo;
+ int testr, testn, testm, testo;
unsigned int p, m, n;
- unsigned int computed;
+ unsigned int computed, vco;
int tmp;
+ const unsigned int m_div_val[] = { 1, 2, 4, 8 };
m = n = p = 0;
vcomax = 1488000;
@@ -490,12 +491,13 @@
if (delta == 0)
break;
for (testo = 5; testo < 33; testo++) {
- computed = pllreffreq * (testn + 1) /
+ vco = pllreffreq * (testn + 1) /
(testr + 1);
- if (computed < vcomin)
+ if (vco < vcomin)
continue;
- if (computed > vcomax)
+ if (vco > vcomax)
continue;
+ computed = vco / (m_div_val[testm] * (testo + 1));
if (computed > clock)
tmpdelta = computed - clock;
else
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index fc841e8..26ebffe 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -211,11 +211,6 @@
return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
}
-static int nouveau_dsm_init(void)
-{
- return 0;
-}
-
static int nouveau_dsm_get_client_id(struct pci_dev *pdev)
{
/* easy option one - intel vendor ID means Integrated */
@@ -232,7 +227,6 @@
static struct vga_switcheroo_handler nouveau_dsm_handler = {
.switchto = nouveau_dsm_switchto,
.power_state = nouveau_dsm_power_state,
- .init = nouveau_dsm_init,
.get_client_id = nouveau_dsm_get_client_id,
};
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 77e5646..240cf96 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -229,7 +229,7 @@
}
break;
case 6: /* NV50- DP AUX */
- port->drive = entry[0];
+ port->drive = entry[0] & 0x0f;
port->sense = port->drive;
port->adapter.algo = &nouveau_dp_i2c_algo;
break;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 1cdfd6e..1866dbb 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -731,7 +731,6 @@
case 0xa3:
case 0xa5:
case 0xa8:
- case 0xaf:
nva3_copy_create(dev);
break;
}
diff --git a/drivers/gpu/drm/nouveau/nv84_fifo.c b/drivers/gpu/drm/nouveau/nv84_fifo.c
index cc82d79..c564c5e 100644
--- a/drivers/gpu/drm/nouveau/nv84_fifo.c
+++ b/drivers/gpu/drm/nouveau/nv84_fifo.c
@@ -117,17 +117,22 @@
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
unsigned long flags;
+ u32 save;
/* remove channel from playlist, will context switch if active */
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
nv_mask(dev, 0x002600 + (chan->id * 4), 0x80000000, 0x00000000);
nv50_fifo_playlist_update(dev);
+ save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
+
/* tell any engines on this channel to unload their contexts */
nv_wr32(dev, 0x0032fc, chan->ramin->vinst >> 12);
if (!nv_wait_ne(dev, 0x0032fc, 0xffffffff, 0xffffffff))
NV_INFO(dev, "PFIFO: channel %d unload timeout\n", chan->id);
+ nv_wr32(dev, 0x002520, save);
+
nv_wr32(dev, 0x002600 + (chan->id * 4), 0x00000000);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
@@ -184,10 +189,13 @@
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv84_fifo_priv *priv = nv_engine(dev, engine);
int i;
+ u32 save;
/* set playlist length to zero, fifo will unload context */
nv_wr32(dev, 0x0032ec, 0);
+ save = nv_mask(dev, 0x002520, 0x0000003f, 0x15);
+
/* tell all connected engines to unload their contexts */
for (i = 0; i < priv->base.channels; i++) {
struct nouveau_channel *chan = dev_priv->channels.ptr[i];
@@ -199,6 +207,7 @@
}
}
+ nv_wr32(dev, 0x002520, save);
nv_wr32(dev, 0x002140, 0);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nvc0_pm.c b/drivers/gpu/drm/nouveau/nvc0_pm.c
index 7c95c44..4e712b1 100644
--- a/drivers/gpu/drm/nouveau/nvc0_pm.c
+++ b/drivers/gpu/drm/nouveau/nvc0_pm.c
@@ -557,7 +557,7 @@
nouveau_mem_exec(&exec, info->perflvl);
if (dev_priv->chipset < 0xd0)
- nv_wr32(dev, 0x611200, 0x00003300);
+ nv_wr32(dev, 0x611200, 0x00003330);
else
nv_wr32(dev, 0x62c000, 0x03030300);
}
diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c
index d0d60e1..dac525b 100644
--- a/drivers/gpu/drm/nouveau/nvd0_display.c
+++ b/drivers/gpu/drm/nouveau/nvd0_display.c
@@ -790,7 +790,7 @@
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ch = EVO_CURS(nv_crtc->index);
- evo_piow(crtc->dev, ch, 0x0084, (y << 16) | x);
+ evo_piow(crtc->dev, ch, 0x0084, (y << 16) | (x & 0xffff));
evo_piow(crtc->dev, ch, 0x0080, 0x00000000);
return 0;
}
diff --git a/drivers/gpu/drm/nouveau/nve0_fifo.c b/drivers/gpu/drm/nouveau/nve0_fifo.c
index 1855ecbd..e98d144 100644
--- a/drivers/gpu/drm/nouveau/nve0_fifo.c
+++ b/drivers/gpu/drm/nouveau/nve0_fifo.c
@@ -294,6 +294,25 @@
printk(" on channel 0x%010llx\n", (u64)inst << 12);
}
+static int
+nve0_fifo_page_flip(struct drm_device *dev, u32 chid)
+{
+ struct nve0_fifo_priv *priv = nv_engine(dev, NVOBJ_ENGINE_FIFO);
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nouveau_channel *chan = NULL;
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&dev_priv->channels.lock, flags);
+ if (likely(chid >= 0 && chid < priv->base.channels)) {
+ chan = dev_priv->channels.ptr[chid];
+ if (likely(chan))
+ ret = nouveau_finish_page_flip(chan, NULL);
+ }
+ spin_unlock_irqrestore(&dev_priv->channels.lock, flags);
+ return ret;
+}
+
static void
nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
{
@@ -303,11 +322,21 @@
u32 chid = nv_rd32(dev, 0x040120 + (unit * 0x2000)) & 0x7f;
u32 subc = (addr & 0x00070000);
u32 mthd = (addr & 0x00003ffc);
+ u32 show = stat;
- NV_INFO(dev, "PSUBFIFO %d:", unit);
- nouveau_bitfield_print(nve0_fifo_subfifo_intr, stat);
- NV_INFO(dev, "PSUBFIFO %d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
- unit, chid, subc, mthd, data);
+ if (stat & 0x00200000) {
+ if (mthd == 0x0054) {
+ if (!nve0_fifo_page_flip(dev, chid))
+ show &= ~0x00200000;
+ }
+ }
+
+ if (show) {
+ NV_INFO(dev, "PFIFO%d:", unit);
+ nouveau_bitfield_print(nve0_fifo_subfifo_intr, show);
+ NV_INFO(dev, "PFIFO%d: ch %d subc %d mthd 0x%04x data 0x%08x\n",
+ unit, chid, subc, mthd, data);
+ }
nv_wr32(dev, 0x0400c0 + (unit * 0x2000), 0x80600008);
nv_wr32(dev, 0x040108 + (unit * 0x2000), stat);
diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
index 9e6f76f..f4d4505 100644
--- a/drivers/gpu/drm/radeon/atombios_crtc.c
+++ b/drivers/gpu/drm/radeon/atombios_crtc.c
@@ -259,7 +259,7 @@
/* adjust pm to dpms changes BEFORE enabling crtcs */
radeon_pm_compute_clocks(rdev);
/* disable crtc pair power gating before programming */
- if (ASIC_IS_DCE6(rdev))
+ if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set)
atombios_powergate_crtc(crtc, ATOM_DISABLE);
atombios_enable_crtc(crtc, ATOM_ENABLE);
if (ASIC_IS_DCE3(rdev) && !ASIC_IS_DCE6(rdev))
@@ -279,7 +279,7 @@
atombios_enable_crtc(crtc, ATOM_DISABLE);
radeon_crtc->enabled = false;
/* power gating is per-pair */
- if (ASIC_IS_DCE6(rdev)) {
+ if (ASIC_IS_DCE6(rdev) && !radeon_crtc->in_mode_set) {
struct drm_crtc *other_crtc;
struct radeon_crtc *other_radeon_crtc;
list_for_each_entry(other_crtc, &rdev->ddev->mode_config.crtc_list, head) {
@@ -444,11 +444,28 @@
static void atombios_crtc_program_ss(struct radeon_device *rdev,
int enable,
int pll_id,
+ int crtc_id,
struct radeon_atom_ss *ss)
{
+ unsigned i;
int index = GetIndexIntoMasterTable(COMMAND, EnableSpreadSpectrumOnPPLL);
union atom_enable_ss args;
+ if (!enable) {
+ for (i = 0; i < rdev->num_crtc; i++) {
+ if (rdev->mode_info.crtcs[i] &&
+ rdev->mode_info.crtcs[i]->enabled &&
+ i != crtc_id &&
+ pll_id == rdev->mode_info.crtcs[i]->pll_id) {
+ /* one other crtc is using this pll don't turn
+ * off spread spectrum as it might turn off
+ * display on active crtc
+ */
+ return;
+ }
+ }
+ }
+
memset(&args, 0, sizeof(args));
if (ASIC_IS_DCE5(rdev)) {
@@ -1028,7 +1045,7 @@
radeon_compute_pll_legacy(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
&ref_div, &post_div);
- atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_DISABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
atombios_crtc_program_pll(crtc, radeon_crtc->crtc_id, radeon_crtc->pll_id,
encoder_mode, radeon_encoder->encoder_id, mode->clock,
@@ -1051,7 +1068,7 @@
ss.step = step_size;
}
- atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_ENABLE, radeon_crtc->pll_id, radeon_crtc->crtc_id, &ss);
}
}
@@ -1531,12 +1548,12 @@
* crtc virtual pixel clock.
*/
if (ENCODER_MODE_IS_DP(atombios_get_encoder_mode(test_encoder))) {
- if (ASIC_IS_DCE5(rdev))
- return ATOM_DCPLL;
+ if (rdev->clock.dp_extclk)
+ return ATOM_PPLL_INVALID;
else if (ASIC_IS_DCE6(rdev))
return ATOM_PPLL0;
- else if (rdev->clock.dp_extclk)
- return ATOM_PPLL_INVALID;
+ else if (ASIC_IS_DCE5(rdev))
+ return ATOM_DCPLL;
}
}
}
@@ -1572,11 +1589,11 @@
ASIC_INTERNAL_SS_ON_DCPLL,
rdev->clock.default_dispclk);
if (ss_enabled)
- atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_DISABLE, ATOM_DCPLL, -1, &ss);
/* XXX: DCE5, make sure voltage, dispclk is high enough */
atombios_crtc_set_disp_eng_pll(rdev, rdev->clock.default_dispclk);
if (ss_enabled)
- atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, &ss);
+ atombios_crtc_program_ss(rdev, ATOM_ENABLE, ATOM_DCPLL, -1, &ss);
}
}
@@ -1635,18 +1652,28 @@
static void atombios_crtc_prepare(struct drm_crtc *crtc)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ radeon_crtc->in_mode_set = true;
/* pick pll */
radeon_crtc->pll_id = radeon_atom_pick_pll(crtc);
+ /* disable crtc pair power gating before programming */
+ if (ASIC_IS_DCE6(rdev))
+ atombios_powergate_crtc(crtc, ATOM_DISABLE);
+
atombios_lock_crtc(crtc, ATOM_ENABLE);
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void atombios_crtc_commit(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+
atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
atombios_lock_crtc(crtc, ATOM_DISABLE);
+ radeon_crtc->in_mode_set = false;
}
static void atombios_crtc_disable(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index e585a3b..e93b80a 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -1229,24 +1229,8 @@
void evergreen_mc_stop(struct radeon_device *rdev, struct evergreen_mc_save *save)
{
- save->vga_control[0] = RREG32(D1VGA_CONTROL);
- save->vga_control[1] = RREG32(D2VGA_CONTROL);
save->vga_render_control = RREG32(VGA_RENDER_CONTROL);
save->vga_hdp_control = RREG32(VGA_HDP_CONTROL);
- save->crtc_control[0] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET);
- save->crtc_control[1] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET);
- if (rdev->num_crtc >= 4) {
- save->vga_control[2] = RREG32(EVERGREEN_D3VGA_CONTROL);
- save->vga_control[3] = RREG32(EVERGREEN_D4VGA_CONTROL);
- save->crtc_control[2] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET);
- save->crtc_control[3] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET);
- }
- if (rdev->num_crtc >= 6) {
- save->vga_control[4] = RREG32(EVERGREEN_D5VGA_CONTROL);
- save->vga_control[5] = RREG32(EVERGREEN_D6VGA_CONTROL);
- save->crtc_control[4] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET);
- save->crtc_control[5] = RREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET);
- }
/* Stop all video */
WREG32(VGA_RENDER_CONTROL, 0);
@@ -1357,47 +1341,6 @@
/* Unlock host access */
WREG32(VGA_HDP_CONTROL, save->vga_hdp_control);
mdelay(1);
- /* Restore video state */
- WREG32(D1VGA_CONTROL, save->vga_control[0]);
- WREG32(D2VGA_CONTROL, save->vga_control[1]);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_D3VGA_CONTROL, save->vga_control[2]);
- WREG32(EVERGREEN_D4VGA_CONTROL, save->vga_control[3]);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_D5VGA_CONTROL, save->vga_control[4]);
- WREG32(EVERGREEN_D6VGA_CONTROL, save->vga_control[5]);
- }
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 1);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 1);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 1);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 1);
- }
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC0_REGISTER_OFFSET, save->crtc_control[0]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC1_REGISTER_OFFSET, save->crtc_control[1]);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC2_REGISTER_OFFSET, save->crtc_control[2]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC3_REGISTER_OFFSET, save->crtc_control[3]);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC4_REGISTER_OFFSET, save->crtc_control[4]);
- WREG32(EVERGREEN_CRTC_CONTROL + EVERGREEN_CRTC5_REGISTER_OFFSET, save->crtc_control[5]);
- }
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC0_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC1_REGISTER_OFFSET, 0);
- if (rdev->num_crtc >= 4) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC2_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC3_REGISTER_OFFSET, 0);
- }
- if (rdev->num_crtc >= 6) {
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC4_REGISTER_OFFSET, 0);
- WREG32(EVERGREEN_CRTC_UPDATE_LOCK + EVERGREEN_CRTC5_REGISTER_OFFSET, 0);
- }
WREG32(VGA_RENDER_CONTROL, save->vga_render_control);
}
@@ -1986,10 +1929,18 @@
if (rdev->flags & RADEON_IS_IGP)
rdev->config.evergreen.tile_config |= 1 << 4;
else {
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.evergreen.tile_config |= 1 << 4;
- else
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.evergreen.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.evergreen.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.evergreen.tile_config |= 2 << 4;
+ break;
+ }
}
rdev->config.evergreen.tile_config |= 0 << 8;
rdev->config.evergreen.tile_config |=
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index c1655412..e44a62a 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -788,6 +788,13 @@
case V_030000_SQ_TEX_DIM_1D_ARRAY:
case V_030000_SQ_TEX_DIM_2D_ARRAY:
depth = 1;
+ break;
+ case V_030000_SQ_TEX_DIM_2D_MSAA:
+ case V_030000_SQ_TEX_DIM_2D_ARRAY_MSAA:
+ surf.nsamples = 1 << llevel;
+ llevel = 0;
+ depth = 1;
+ break;
case V_030000_SQ_TEX_DIM_3D:
break;
default:
@@ -961,13 +968,15 @@
if (track->db_dirty) {
/* Check stencil buffer */
- if (G_028800_STENCIL_ENABLE(track->db_depth_control)) {
+ if (G_028044_FORMAT(track->db_s_info) != V_028044_STENCIL_INVALID &&
+ G_028800_STENCIL_ENABLE(track->db_depth_control)) {
r = evergreen_cs_track_validate_stencil(p);
if (r)
return r;
}
/* Check depth buffer */
- if (G_028800_Z_ENABLE(track->db_depth_control)) {
+ if (G_028040_FORMAT(track->db_z_info) != V_028040_Z_INVALID &&
+ G_028800_Z_ENABLE(track->db_depth_control)) {
r = evergreen_cs_track_validate_depth(p);
if (r)
return r;
diff --git a/drivers/gpu/drm/radeon/evergreend.h b/drivers/gpu/drm/radeon/evergreend.h
index d3bd098..7934785 100644
--- a/drivers/gpu/drm/radeon/evergreend.h
+++ b/drivers/gpu/drm/radeon/evergreend.h
@@ -1277,6 +1277,8 @@
#define S_028044_FORMAT(x) (((x) & 0x1) << 0)
#define G_028044_FORMAT(x) (((x) >> 0) & 0x1)
#define C_028044_FORMAT 0xFFFFFFFE
+#define V_028044_STENCIL_INVALID 0
+#define V_028044_STENCIL_8 1
#define G_028044_TILE_SPLIT(x) (((x) >> 8) & 0x7)
#define DB_Z_READ_BASE 0x28048
#define DB_STENCIL_READ_BASE 0x2804c
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 9945d86..853800e 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -574,10 +574,18 @@
if (rdev->flags & RADEON_IS_IGP)
rdev->config.cayman.tile_config |= 1 << 4;
else {
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.cayman.tile_config |= 1 << 4;
- else
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.cayman.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.cayman.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.cayman.tile_config |= 2 << 4;
+ break;
+ }
}
rdev->config.cayman.tile_config |=
((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 637280f..d79c639 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -3789,3 +3789,23 @@
WREG32_PCIE_P(PCIE_LC_LINK_WIDTH_CNTL, link_width_cntl);
}
}
+
+/**
+ * r600_get_gpu_clock - return GPU clock counter snapshot
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Fetches a GPU clock counter snapshot (R6xx-cayman).
+ * Returns the 64 bit clock counter snapshot.
+ */
+uint64_t r600_get_gpu_clock(struct radeon_device *rdev)
+{
+ uint64_t clock;
+
+ mutex_lock(&rdev->gpu_clock_mutex);
+ WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1);
+ clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) |
+ ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL);
+ mutex_unlock(&rdev->gpu_clock_mutex);
+ return clock;
+}
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index ca87f7a..ab74e6b 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -47,13 +47,17 @@
u32 npipes;
/* value we track */
u32 sq_config;
+ u32 log_nsamples;
u32 nsamples;
u32 cb_color_base_last[8];
struct radeon_bo *cb_color_bo[8];
u64 cb_color_bo_mc[8];
- u32 cb_color_bo_offset[8];
- struct radeon_bo *cb_color_frag_bo[8]; /* unused */
- struct radeon_bo *cb_color_tile_bo[8]; /* unused */
+ u64 cb_color_bo_offset[8];
+ struct radeon_bo *cb_color_frag_bo[8];
+ u64 cb_color_frag_offset[8];
+ struct radeon_bo *cb_color_tile_bo[8];
+ u64 cb_color_tile_offset[8];
+ u32 cb_color_mask[8];
u32 cb_color_info[8];
u32 cb_color_view[8];
u32 cb_color_size_idx[8]; /* unused */
@@ -349,10 +353,6 @@
unsigned array_mode;
u32 format;
- if (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
- dev_warn(p->dev, "FMASK or CMASK buffer are not supported by this kernel\n");
- return -EINVAL;
- }
size = radeon_bo_size(track->cb_color_bo[i]) - track->cb_color_bo_offset[i];
format = G_0280A0_FORMAT(track->cb_color_info[i]);
if (!r600_fmt_is_valid_color(format)) {
@@ -420,7 +420,8 @@
}
/* check offset */
- tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) * r600_fmt_get_blocksize(format);
+ tmp = r600_fmt_get_nblocksy(format, height) * r600_fmt_get_nblocksx(format, pitch) *
+ r600_fmt_get_blocksize(format) * track->nsamples;
switch (array_mode) {
default:
case V_0280A0_ARRAY_LINEAR_GENERAL:
@@ -441,7 +442,7 @@
* broken userspace.
*/
} else {
- dev_warn(p->dev, "%s offset[%d] %d %d %d %lu too big (%d %d) (%d %d %d)\n",
+ dev_warn(p->dev, "%s offset[%d] %d %llu %d %lu too big (%d %d) (%d %d %d)\n",
__func__, i, array_mode,
track->cb_color_bo_offset[i], tmp,
radeon_bo_size(track->cb_color_bo[i]),
@@ -458,6 +459,51 @@
tmp = S_028060_PITCH_TILE_MAX((pitch / 8) - 1) |
S_028060_SLICE_TILE_MAX(slice_tile_max - 1);
ib[track->cb_color_size_idx[i]] = tmp;
+
+ /* FMASK/CMASK */
+ switch (G_0280A0_TILE_MODE(track->cb_color_info[i])) {
+ case V_0280A0_TILE_DISABLE:
+ break;
+ case V_0280A0_FRAG_ENABLE:
+ if (track->nsamples > 1) {
+ uint32_t tile_max = G_028100_FMASK_TILE_MAX(track->cb_color_mask[i]);
+ /* the tile size is 8x8, but the size is in units of bits.
+ * for bytes, do just * 8. */
+ uint32_t bytes = track->nsamples * track->log_nsamples * 8 * (tile_max + 1);
+
+ if (bytes + track->cb_color_frag_offset[i] >
+ radeon_bo_size(track->cb_color_frag_bo[i])) {
+ dev_warn(p->dev, "%s FMASK_TILE_MAX too large "
+ "(tile_max=%u, bytes=%u, offset=%llu, bo_size=%lu)\n",
+ __func__, tile_max, bytes,
+ track->cb_color_frag_offset[i],
+ radeon_bo_size(track->cb_color_frag_bo[i]));
+ return -EINVAL;
+ }
+ }
+ /* fall through */
+ case V_0280A0_CLEAR_ENABLE:
+ {
+ uint32_t block_max = G_028100_CMASK_BLOCK_MAX(track->cb_color_mask[i]);
+ /* One block = 128x128 pixels, one 8x8 tile has 4 bits..
+ * (128*128) / (8*8) / 2 = 128 bytes per block. */
+ uint32_t bytes = (block_max + 1) * 128;
+
+ if (bytes + track->cb_color_tile_offset[i] >
+ radeon_bo_size(track->cb_color_tile_bo[i])) {
+ dev_warn(p->dev, "%s CMASK_BLOCK_MAX too large "
+ "(block_max=%u, bytes=%u, offset=%llu, bo_size=%lu)\n",
+ __func__, block_max, bytes,
+ track->cb_color_tile_offset[i],
+ radeon_bo_size(track->cb_color_tile_bo[i]));
+ return -EINVAL;
+ }
+ break;
+ }
+ default:
+ dev_warn(p->dev, "%s invalid tile mode\n", __func__);
+ return -EINVAL;
+ }
return 0;
}
@@ -566,7 +612,7 @@
ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
- tmp = ntiles * bpe * 64 * nviews;
+ tmp = ntiles * bpe * 64 * nviews * track->nsamples;
if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
array_mode,
@@ -764,8 +810,10 @@
}
/* Check depth buffer */
- if (track->db_dirty && (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
- G_028800_Z_ENABLE(track->db_depth_control))) {
+ if (track->db_dirty &&
+ G_028010_FORMAT(track->db_depth_info) != V_028010_DEPTH_INVALID &&
+ (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
+ G_028800_Z_ENABLE(track->db_depth_control))) {
r = r600_cs_track_validate_db(p);
if (r)
return r;
@@ -1229,6 +1277,7 @@
break;
case R_028C04_PA_SC_AA_CONFIG:
tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx));
+ track->log_nsamples = tmp;
track->nsamples = 1 << tmp;
track->cb_dirty = true;
break;
@@ -1310,16 +1359,21 @@
dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
return -EINVAL;
}
- ib[idx] = track->cb_color_base_last[tmp];
track->cb_color_frag_bo[tmp] = track->cb_color_bo[tmp];
+ track->cb_color_frag_offset[tmp] = track->cb_color_bo_offset[tmp];
+ ib[idx] = track->cb_color_base_last[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_frag_bo[tmp] = reloc->robj;
+ track->cb_color_frag_offset[tmp] = (u64)ib[idx] << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ }
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
}
break;
case R_0280C0_CB_COLOR0_TILE:
@@ -1336,16 +1390,35 @@
dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
return -EINVAL;
}
- ib[idx] = track->cb_color_base_last[tmp];
track->cb_color_tile_bo[tmp] = track->cb_color_bo[tmp];
+ track->cb_color_tile_offset[tmp] = track->cb_color_bo_offset[tmp];
+ ib[idx] = track->cb_color_base_last[tmp];
} else {
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
return -EINVAL;
}
- ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->cb_color_tile_bo[tmp] = reloc->robj;
+ track->cb_color_tile_offset[tmp] = (u64)ib[idx] << 8;
+ ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
+ }
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
+ }
+ break;
+ case R_028100_CB_COLOR0_MASK:
+ case R_028104_CB_COLOR1_MASK:
+ case R_028108_CB_COLOR2_MASK:
+ case R_02810C_CB_COLOR3_MASK:
+ case R_028110_CB_COLOR4_MASK:
+ case R_028114_CB_COLOR5_MASK:
+ case R_028118_CB_COLOR6_MASK:
+ case R_02811C_CB_COLOR7_MASK:
+ tmp = (reg - R_028100_CB_COLOR0_MASK) / 4;
+ track->cb_color_mask[tmp] = ib[idx];
+ if (G_0280A0_TILE_MODE(track->cb_color_info[tmp])) {
+ track->cb_dirty = true;
}
break;
case CB_COLOR0_BASE:
@@ -1490,7 +1563,7 @@
}
static void r600_texture_size(unsigned nfaces, unsigned blevel, unsigned llevel,
- unsigned w0, unsigned h0, unsigned d0, unsigned format,
+ unsigned w0, unsigned h0, unsigned d0, unsigned nsamples, unsigned format,
unsigned block_align, unsigned height_align, unsigned base_align,
unsigned *l0_size, unsigned *mipmap_size)
{
@@ -1518,7 +1591,7 @@
depth = r600_mip_minify(d0, i);
- size = nbx * nby * blocksize;
+ size = nbx * nby * blocksize * nsamples;
if (nfaces)
size *= nfaces;
else
@@ -1557,13 +1630,14 @@
u32 tiling_flags)
{
struct r600_cs_track *track = p->track;
- u32 nfaces, llevel, blevel, w0, h0, d0;
- u32 word0, word1, l0_size, mipmap_size, word2, word3;
+ u32 dim, nfaces, llevel, blevel, w0, h0, d0;
+ u32 word0, word1, l0_size, mipmap_size, word2, word3, word4, word5;
u32 height_align, pitch, pitch_align, depth_align;
- u32 array, barray, larray;
+ u32 barray, larray;
u64 base_align;
struct array_mode_checker array_check;
u32 format;
+ bool is_array;
/* on legacy kernel we don't perform advanced check */
if (p->rdev == NULL)
@@ -1581,12 +1655,28 @@
word0 |= S_038000_TILE_MODE(V_038000_ARRAY_1D_TILED_THIN1);
}
word1 = radeon_get_ib_value(p, idx + 1);
+ word2 = radeon_get_ib_value(p, idx + 2) << 8;
+ word3 = radeon_get_ib_value(p, idx + 3) << 8;
+ word4 = radeon_get_ib_value(p, idx + 4);
+ word5 = radeon_get_ib_value(p, idx + 5);
+ dim = G_038000_DIM(word0);
w0 = G_038000_TEX_WIDTH(word0) + 1;
+ pitch = (G_038000_PITCH(word0) + 1) * 8;
h0 = G_038004_TEX_HEIGHT(word1) + 1;
d0 = G_038004_TEX_DEPTH(word1);
+ format = G_038004_DATA_FORMAT(word1);
+ blevel = G_038010_BASE_LEVEL(word4);
+ llevel = G_038014_LAST_LEVEL(word5);
+ /* pitch in texels */
+ array_check.array_mode = G_038000_TILE_MODE(word0);
+ array_check.group_size = track->group_size;
+ array_check.nbanks = track->nbanks;
+ array_check.npipes = track->npipes;
+ array_check.nsamples = 1;
+ array_check.blocksize = r600_fmt_get_blocksize(format);
nfaces = 1;
- array = 0;
- switch (G_038000_DIM(word0)) {
+ is_array = false;
+ switch (dim) {
case V_038000_SQ_TEX_DIM_1D:
case V_038000_SQ_TEX_DIM_2D:
case V_038000_SQ_TEX_DIM_3D:
@@ -1599,29 +1689,25 @@
break;
case V_038000_SQ_TEX_DIM_1D_ARRAY:
case V_038000_SQ_TEX_DIM_2D_ARRAY:
- array = 1;
+ is_array = true;
break;
- case V_038000_SQ_TEX_DIM_2D_MSAA:
case V_038000_SQ_TEX_DIM_2D_ARRAY_MSAA:
+ is_array = true;
+ /* fall through */
+ case V_038000_SQ_TEX_DIM_2D_MSAA:
+ array_check.nsamples = 1 << llevel;
+ llevel = 0;
+ break;
default:
dev_warn(p->dev, "this kernel doesn't support %d texture dim\n", G_038000_DIM(word0));
return -EINVAL;
}
- format = G_038004_DATA_FORMAT(word1);
if (!r600_fmt_is_valid_texture(format, p->family)) {
dev_warn(p->dev, "%s:%d texture invalid format %d\n",
__func__, __LINE__, format);
return -EINVAL;
}
- /* pitch in texels */
- pitch = (G_038000_PITCH(word0) + 1) * 8;
- array_check.array_mode = G_038000_TILE_MODE(word0);
- array_check.group_size = track->group_size;
- array_check.nbanks = track->nbanks;
- array_check.npipes = track->npipes;
- array_check.nsamples = 1;
- array_check.blocksize = r600_fmt_get_blocksize(format);
if (r600_get_array_mode_alignment(&array_check,
&pitch_align, &height_align, &depth_align, &base_align)) {
dev_warn(p->dev, "%s:%d tex array mode (%d) invalid\n",
@@ -1647,24 +1733,17 @@
return -EINVAL;
}
- word2 = radeon_get_ib_value(p, idx + 2) << 8;
- word3 = radeon_get_ib_value(p, idx + 3) << 8;
-
- word0 = radeon_get_ib_value(p, idx + 4);
- word1 = radeon_get_ib_value(p, idx + 5);
- blevel = G_038010_BASE_LEVEL(word0);
- llevel = G_038014_LAST_LEVEL(word1);
if (blevel > llevel) {
dev_warn(p->dev, "texture blevel %d > llevel %d\n",
blevel, llevel);
}
- if (array == 1) {
- barray = G_038014_BASE_ARRAY(word1);
- larray = G_038014_LAST_ARRAY(word1);
+ if (is_array) {
+ barray = G_038014_BASE_ARRAY(word5);
+ larray = G_038014_LAST_ARRAY(word5);
nfaces = larray - barray + 1;
}
- r600_texture_size(nfaces, blevel, llevel, w0, h0, d0, format,
+ r600_texture_size(nfaces, blevel, llevel, w0, h0, d0, array_check.nsamples, format,
pitch_align, height_align, base_align,
&l0_size, &mipmap_size);
/* using get ib will give us the offset into the texture bo */
@@ -1677,7 +1756,6 @@
return -EINVAL;
}
/* using get ib will give us the offset into the mipmap bo */
- word3 = radeon_get_ib_value(p, idx + 3) << 8;
if ((mipmap_size + word3) > radeon_bo_size(mipmap)) {
/*dev_warn(p->dev, "mipmap bo too small (%d %d %d %d %d %d -> %d have %ld)\n",
w0, h0, format, blevel, nlevels, word3, mipmap_size, radeon_bo_size(texture));*/
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 4b116ae..bdb69a6 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -92,6 +92,20 @@
#define R_028094_CB_COLOR5_VIEW 0x028094
#define R_028098_CB_COLOR6_VIEW 0x028098
#define R_02809C_CB_COLOR7_VIEW 0x02809C
+#define R_028100_CB_COLOR0_MASK 0x028100
+#define S_028100_CMASK_BLOCK_MAX(x) (((x) & 0xFFF) << 0)
+#define G_028100_CMASK_BLOCK_MAX(x) (((x) >> 0) & 0xFFF)
+#define C_028100_CMASK_BLOCK_MAX 0xFFFFF000
+#define S_028100_FMASK_TILE_MAX(x) (((x) & 0xFFFFF) << 12)
+#define G_028100_FMASK_TILE_MAX(x) (((x) >> 12) & 0xFFFFF)
+#define C_028100_FMASK_TILE_MAX 0x00000FFF
+#define R_028104_CB_COLOR1_MASK 0x028104
+#define R_028108_CB_COLOR2_MASK 0x028108
+#define R_02810C_CB_COLOR3_MASK 0x02810C
+#define R_028110_CB_COLOR4_MASK 0x028110
+#define R_028114_CB_COLOR5_MASK 0x028114
+#define R_028118_CB_COLOR6_MASK 0x028118
+#define R_02811C_CB_COLOR7_MASK 0x02811C
#define CB_COLOR0_INFO 0x280a0
# define CB_FORMAT(x) ((x) << 2)
# define CB_ARRAY_MODE(x) ((x) << 8)
@@ -602,6 +616,9 @@
#define RLC_HB_WPTR 0x3f1c
#define RLC_HB_WPTR_LSB_ADDR 0x3f14
#define RLC_HB_WPTR_MSB_ADDR 0x3f18
+#define RLC_GPU_CLOCK_COUNT_LSB 0x3f38
+#define RLC_GPU_CLOCK_COUNT_MSB 0x3f3c
+#define RLC_CAPTURE_GPU_CLOCK_COUNT 0x3f40
#define RLC_MC_CNTL 0x3f44
#define RLC_UCODE_CNTL 0x3f48
#define RLC_UCODE_ADDR 0x3f2c
@@ -1397,6 +1414,9 @@
#define S_0280A0_TILE_MODE(x) (((x) & 0x3) << 18)
#define G_0280A0_TILE_MODE(x) (((x) >> 18) & 0x3)
#define C_0280A0_TILE_MODE 0xFFF3FFFF
+#define V_0280A0_TILE_DISABLE 0
+#define V_0280A0_CLEAR_ENABLE 1
+#define V_0280A0_FRAG_ENABLE 2
#define S_0280A0_BLEND_CLAMP(x) (((x) & 0x1) << 20)
#define G_0280A0_BLEND_CLAMP(x) (((x) >> 20) & 0x1)
#define C_0280A0_BLEND_CLAMP 0xFFEFFFFF
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 5431af2..59a1531 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -142,21 +142,6 @@
/*
* BIOS.
*/
-#define ATRM_BIOS_PAGE 4096
-
-#if defined(CONFIG_VGA_SWITCHEROO)
-bool radeon_atrm_supported(struct pci_dev *pdev);
-int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len);
-#else
-static inline bool radeon_atrm_supported(struct pci_dev *pdev)
-{
- return false;
-}
-
-static inline int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len){
- return -EINVAL;
-}
-#endif
bool radeon_get_bios(struct radeon_device *rdev);
/*
@@ -300,6 +285,7 @@
uint64_t soffset;
uint64_t eoffset;
uint32_t flags;
+ struct radeon_fence *fence;
bool valid;
};
@@ -1533,6 +1519,7 @@
unsigned debugfs_count;
/* virtual memory */
struct radeon_vm_manager vm_manager;
+ struct mutex gpu_clock_mutex;
};
int radeon_device_init(struct radeon_device *rdev,
@@ -1733,11 +1720,11 @@
#define radeon_pm_finish(rdev) (rdev)->asic->pm.finish((rdev))
#define radeon_pm_init_profile(rdev) (rdev)->asic->pm.init_profile((rdev))
#define radeon_pm_get_dynpm_state(rdev) (rdev)->asic->pm.get_dynpm_state((rdev))
-#define radeon_pre_page_flip(rdev, crtc) rdev->asic->pflip.pre_page_flip((rdev), (crtc))
-#define radeon_page_flip(rdev, crtc, base) rdev->asic->pflip.page_flip((rdev), (crtc), (base))
-#define radeon_post_page_flip(rdev, crtc) rdev->asic->pflip.post_page_flip((rdev), (crtc))
-#define radeon_wait_for_vblank(rdev, crtc) rdev->asic->display.wait_for_vblank((rdev), (crtc))
-#define radeon_mc_wait_for_idle(rdev) rdev->asic->mc_wait_for_idle((rdev))
+#define radeon_pre_page_flip(rdev, crtc) (rdev)->asic->pflip.pre_page_flip((rdev), (crtc))
+#define radeon_page_flip(rdev, crtc, base) (rdev)->asic->pflip.page_flip((rdev), (crtc), (base))
+#define radeon_post_page_flip(rdev, crtc) (rdev)->asic->pflip.post_page_flip((rdev), (crtc))
+#define radeon_wait_for_vblank(rdev, crtc) (rdev)->asic->display.wait_for_vblank((rdev), (crtc))
+#define radeon_mc_wait_for_idle(rdev) (rdev)->asic->mc_wait_for_idle((rdev))
/* Common functions */
/* AGP */
diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
index f4af243..18c38d1 100644
--- a/drivers/gpu/drm/radeon/radeon_asic.h
+++ b/drivers/gpu/drm/radeon/radeon_asic.h
@@ -255,13 +255,10 @@
* rv515
*/
struct rv515_mc_save {
- u32 d1vga_control;
- u32 d2vga_control;
u32 vga_render_control;
u32 vga_hdp_control;
- u32 d1crtc_control;
- u32 d2crtc_control;
};
+
int rv515_init(struct radeon_device *rdev);
void rv515_fini(struct radeon_device *rdev);
uint32_t rv515_mc_rreg(struct radeon_device *rdev, uint32_t reg);
@@ -371,6 +368,7 @@
unsigned num_gpu_pages,
struct radeon_sa_bo *vb);
int r600_mc_wait_for_idle(struct radeon_device *rdev);
+uint64_t r600_get_gpu_clock(struct radeon_device *rdev);
/*
* rv770,rv730,rv710,rv740
@@ -389,11 +387,10 @@
* evergreen
*/
struct evergreen_mc_save {
- u32 vga_control[6];
u32 vga_render_control;
u32 vga_hdp_control;
- u32 crtc_control[6];
};
+
void evergreen_pcie_gart_tlb_flush(struct radeon_device *rdev);
int evergreen_init(struct radeon_device *rdev);
void evergreen_fini(struct radeon_device *rdev);
@@ -472,5 +469,6 @@
void si_vm_unbind(struct radeon_device *rdev, struct radeon_vm *vm);
void si_vm_tlb_flush(struct radeon_device *rdev, struct radeon_vm *vm);
int si_ib_parse(struct radeon_device *rdev, struct radeon_ib *ib);
+uint64_t si_get_gpu_clock(struct radeon_device *rdev);
#endif
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index b1e3820..d67d4f3 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -452,7 +452,7 @@
}
/* Fujitsu D3003-S2 board lists DVI-I as DVI-D and VGA */
- if ((dev->pdev->device == 0x9802) &&
+ if (((dev->pdev->device == 0x9802) || (dev->pdev->device == 0x9806)) &&
(dev->pdev->subsystem_vendor == 0x1734) &&
(dev->pdev->subsystem_device == 0x11bd)) {
if (*connector_type == DRM_MODE_CONNECTOR_VGA) {
@@ -1263,6 +1263,8 @@
union igp_info {
struct _ATOM_INTEGRATED_SYSTEM_INFO info;
struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 info_6;
+ struct _ATOM_INTEGRATED_SYSTEM_INFO_V1_7 info_7;
};
bool radeon_atombios_sideport_present(struct radeon_device *rdev)
@@ -1390,27 +1392,50 @@
struct radeon_mode_info *mode_info = &rdev->mode_info;
int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
u16 data_offset, size;
- struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 *igp_info;
+ union igp_info *igp_info;
u8 frev, crev;
u16 percentage = 0, rate = 0;
/* get any igp specific overrides */
if (atom_parse_data_header(mode_info->atom_context, index, &size,
&frev, &crev, &data_offset)) {
- igp_info = (struct _ATOM_INTEGRATED_SYSTEM_INFO_V6 *)
+ igp_info = (union igp_info *)
(mode_info->atom_context->bios + data_offset);
- switch (id) {
- case ASIC_INTERNAL_SS_ON_TMDS:
- percentage = le16_to_cpu(igp_info->usDVISSPercentage);
- rate = le16_to_cpu(igp_info->usDVISSpreadRateIn10Hz);
+ switch (crev) {
+ case 6:
+ switch (id) {
+ case ASIC_INTERNAL_SS_ON_TMDS:
+ percentage = le16_to_cpu(igp_info->info_6.usDVISSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usDVISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_HDMI:
+ percentage = le16_to_cpu(igp_info->info_6.usHDMISSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usHDMISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_LVDS:
+ percentage = le16_to_cpu(igp_info->info_6.usLvdsSSPercentage);
+ rate = le16_to_cpu(igp_info->info_6.usLvdsSSpreadRateIn10Hz);
+ break;
+ }
break;
- case ASIC_INTERNAL_SS_ON_HDMI:
- percentage = le16_to_cpu(igp_info->usHDMISSPercentage);
- rate = le16_to_cpu(igp_info->usHDMISSpreadRateIn10Hz);
+ case 7:
+ switch (id) {
+ case ASIC_INTERNAL_SS_ON_TMDS:
+ percentage = le16_to_cpu(igp_info->info_7.usDVISSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usDVISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_HDMI:
+ percentage = le16_to_cpu(igp_info->info_7.usHDMISSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usHDMISSpreadRateIn10Hz);
+ break;
+ case ASIC_INTERNAL_SS_ON_LVDS:
+ percentage = le16_to_cpu(igp_info->info_7.usLvdsSSPercentage);
+ rate = le16_to_cpu(igp_info->info_7.usLvdsSSpreadRateIn10Hz);
+ break;
+ }
break;
- case ASIC_INTERNAL_SS_ON_LVDS:
- percentage = le16_to_cpu(igp_info->usLvdsSSPercentage);
- rate = le16_to_cpu(igp_info->usLvdsSSpreadRateIn10Hz);
+ default:
+ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
break;
}
if (percentage)
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index 98724fc..2a2cf0b 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -30,57 +30,8 @@
/* handle for device - and atpx */
acpi_handle dhandle;
acpi_handle atpx_handle;
- acpi_handle atrm_handle;
} radeon_atpx_priv;
-/* retrieve the ROM in 4k blocks */
-static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
- int offset, int len)
-{
- acpi_status status;
- union acpi_object atrm_arg_elements[2], *obj;
- struct acpi_object_list atrm_arg;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
-
- atrm_arg.count = 2;
- atrm_arg.pointer = &atrm_arg_elements[0];
-
- atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
- atrm_arg_elements[0].integer.value = offset;
-
- atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
- atrm_arg_elements[1].integer.value = len;
-
- status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
- if (ACPI_FAILURE(status)) {
- printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
- return -ENODEV;
- }
-
- obj = (union acpi_object *)buffer.pointer;
- memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
- len = obj->buffer.length;
- kfree(buffer.pointer);
- return len;
-}
-
-bool radeon_atrm_supported(struct pci_dev *pdev)
-{
- /* get the discrete ROM only via ATRM */
- if (!radeon_atpx_priv.atpx_detected)
- return false;
-
- if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev))
- return false;
- return true;
-}
-
-
-int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len)
-{
- return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len);
-}
-
static int radeon_atpx_get_version(acpi_handle handle)
{
acpi_status status;
@@ -198,7 +149,7 @@
static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev)
{
- acpi_handle dhandle, atpx_handle, atrm_handle;
+ acpi_handle dhandle, atpx_handle;
acpi_status status;
dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
@@ -209,13 +160,8 @@
if (ACPI_FAILURE(status))
return false;
- status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
- if (ACPI_FAILURE(status))
- return false;
-
radeon_atpx_priv.dhandle = dhandle;
radeon_atpx_priv.atpx_handle = atpx_handle;
- radeon_atpx_priv.atrm_handle = atrm_handle;
return true;
}
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
index 501f488..d306cc8 100644
--- a/drivers/gpu/drm/radeon/radeon_bios.c
+++ b/drivers/gpu/drm/radeon/radeon_bios.c
@@ -32,6 +32,7 @@
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
/*
* BIOS.
*/
@@ -98,16 +99,81 @@
return true;
}
+#ifdef CONFIG_ACPI
/* ATRM is used to get the BIOS on the discrete cards in
* dual-gpu systems.
*/
+/* retrieve the ROM in 4k blocks */
+#define ATRM_BIOS_PAGE 4096
+/**
+ * radeon_atrm_call - fetch a chunk of the vbios
+ *
+ * @atrm_handle: acpi ATRM handle
+ * @bios: vbios image pointer
+ * @offset: offset of vbios image data to fetch
+ * @len: length of vbios image data to fetch
+ *
+ * Executes ATRM to fetch a chunk of the discrete
+ * vbios image on PX systems (all asics).
+ * Returns the length of the buffer fetched.
+ */
+static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios,
+ int offset, int len)
+{
+ acpi_status status;
+ union acpi_object atrm_arg_elements[2], *obj;
+ struct acpi_object_list atrm_arg;
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL};
+
+ atrm_arg.count = 2;
+ atrm_arg.pointer = &atrm_arg_elements[0];
+
+ atrm_arg_elements[0].type = ACPI_TYPE_INTEGER;
+ atrm_arg_elements[0].integer.value = offset;
+
+ atrm_arg_elements[1].type = ACPI_TYPE_INTEGER;
+ atrm_arg_elements[1].integer.value = len;
+
+ status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer);
+ if (ACPI_FAILURE(status)) {
+ printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status));
+ return -ENODEV;
+ }
+
+ obj = (union acpi_object *)buffer.pointer;
+ memcpy(bios+offset, obj->buffer.pointer, obj->buffer.length);
+ len = obj->buffer.length;
+ kfree(buffer.pointer);
+ return len;
+}
+
static bool radeon_atrm_get_bios(struct radeon_device *rdev)
{
int ret;
int size = 256 * 1024;
int i;
+ struct pci_dev *pdev = NULL;
+ acpi_handle dhandle, atrm_handle;
+ acpi_status status;
+ bool found = false;
- if (!radeon_atrm_supported(rdev->pdev))
+ /* ATRM is for the discrete card only */
+ if (rdev->flags & RADEON_IS_IGP)
+ return false;
+
+ while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
+ dhandle = DEVICE_ACPI_HANDLE(&pdev->dev);
+ if (!dhandle)
+ continue;
+
+ status = acpi_get_handle(dhandle, "ATRM", &atrm_handle);
+ if (!ACPI_FAILURE(status)) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found)
return false;
rdev->bios = kmalloc(size, GFP_KERNEL);
@@ -117,9 +183,10 @@
}
for (i = 0; i < size / ATRM_BIOS_PAGE; i++) {
- ret = radeon_atrm_get_bios_chunk(rdev->bios,
- (i * ATRM_BIOS_PAGE),
- ATRM_BIOS_PAGE);
+ ret = radeon_atrm_call(atrm_handle,
+ rdev->bios,
+ (i * ATRM_BIOS_PAGE),
+ ATRM_BIOS_PAGE);
if (ret < ATRM_BIOS_PAGE)
break;
}
@@ -130,6 +197,12 @@
}
return true;
}
+#else
+static inline bool radeon_atrm_get_bios(struct radeon_device *rdev)
+{
+ return false;
+}
+#endif
static bool ni_read_disabled_bios(struct radeon_device *rdev)
{
@@ -476,6 +549,61 @@
return legacy_read_disabled_bios(rdev);
}
+#ifdef CONFIG_ACPI
+static bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
+{
+ bool ret = false;
+ struct acpi_table_header *hdr;
+ acpi_size tbl_size;
+ UEFI_ACPI_VFCT *vfct;
+ GOP_VBIOS_CONTENT *vbios;
+ VFCT_IMAGE_HEADER *vhdr;
+
+ if (!ACPI_SUCCESS(acpi_get_table_with_size("VFCT", 1, &hdr, &tbl_size)))
+ return false;
+ if (tbl_size < sizeof(UEFI_ACPI_VFCT)) {
+ DRM_ERROR("ACPI VFCT table present but broken (too short #1)\n");
+ goto out_unmap;
+ }
+
+ vfct = (UEFI_ACPI_VFCT *)hdr;
+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) > tbl_size) {
+ DRM_ERROR("ACPI VFCT table present but broken (too short #2)\n");
+ goto out_unmap;
+ }
+
+ vbios = (GOP_VBIOS_CONTENT *)((char *)hdr + vfct->VBIOSImageOffset);
+ vhdr = &vbios->VbiosHeader;
+ DRM_INFO("ACPI VFCT contains a BIOS for %02x:%02x.%d %04x:%04x, size %d\n",
+ vhdr->PCIBus, vhdr->PCIDevice, vhdr->PCIFunction,
+ vhdr->VendorID, vhdr->DeviceID, vhdr->ImageLength);
+
+ if (vhdr->PCIBus != rdev->pdev->bus->number ||
+ vhdr->PCIDevice != PCI_SLOT(rdev->pdev->devfn) ||
+ vhdr->PCIFunction != PCI_FUNC(rdev->pdev->devfn) ||
+ vhdr->VendorID != rdev->pdev->vendor ||
+ vhdr->DeviceID != rdev->pdev->device) {
+ DRM_INFO("ACPI VFCT table is not for this card\n");
+ goto out_unmap;
+ };
+
+ if (vfct->VBIOSImageOffset + sizeof(VFCT_IMAGE_HEADER) + vhdr->ImageLength > tbl_size) {
+ DRM_ERROR("ACPI VFCT image truncated\n");
+ goto out_unmap;
+ }
+
+ rdev->bios = kmemdup(&vbios->VbiosContent, vhdr->ImageLength, GFP_KERNEL);
+ ret = !!rdev->bios;
+
+out_unmap:
+ return ret;
+}
+#else
+static inline bool radeon_acpi_vfct_bios(struct radeon_device *rdev)
+{
+ return false;
+}
+#endif
bool radeon_get_bios(struct radeon_device *rdev)
{
@@ -484,6 +612,8 @@
r = radeon_atrm_get_bios(rdev);
if (r == false)
+ r = radeon_acpi_vfct_bios(rdev);
+ if (r == false)
r = igp_read_bios_from_vram(rdev);
if (r == false)
r = radeon_read_bios(rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
index 576f4f6..f75247d 100644
--- a/drivers/gpu/drm/radeon/radeon_combios.c
+++ b/drivers/gpu/drm/radeon/radeon_combios.c
@@ -719,6 +719,34 @@
return i2c;
}
+static struct radeon_i2c_bus_rec radeon_combios_get_i2c_info_from_table(struct radeon_device *rdev)
+{
+ struct drm_device *dev = rdev->ddev;
+ struct radeon_i2c_bus_rec i2c;
+ u16 offset;
+ u8 id, blocks, clk, data;
+ int i;
+
+ i2c.valid = false;
+
+ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
+ if (offset) {
+ blocks = RBIOS8(offset + 2);
+ for (i = 0; i < blocks; i++) {
+ id = RBIOS8(offset + 3 + (i * 5) + 0);
+ if (id == 136) {
+ clk = RBIOS8(offset + 3 + (i * 5) + 3);
+ data = RBIOS8(offset + 3 + (i * 5) + 4);
+ /* gpiopad */
+ i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
+ (1 << clk), (1 << data));
+ break;
+ }
+ }
+ }
+ return i2c;
+}
+
void radeon_combios_i2c_init(struct radeon_device *rdev)
{
struct drm_device *dev = rdev->ddev;
@@ -755,30 +783,14 @@
} else if (rdev->family == CHIP_RS300 ||
rdev->family == CHIP_RS400 ||
rdev->family == CHIP_RS480) {
- u16 offset;
- u8 id, blocks, clk, data;
- int i;
-
/* 0x68 */
i2c = combios_setup_i2c_bus(rdev, DDC_CRT2, 0, 0);
rdev->i2c_bus[3] = radeon_i2c_create(dev, &i2c, "MONID");
- offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
- if (offset) {
- blocks = RBIOS8(offset + 2);
- for (i = 0; i < blocks; i++) {
- id = RBIOS8(offset + 3 + (i * 5) + 0);
- if (id == 136) {
- clk = RBIOS8(offset + 3 + (i * 5) + 3);
- data = RBIOS8(offset + 3 + (i * 5) + 4);
- /* gpiopad */
- i2c = combios_setup_i2c_bus(rdev, DDC_MONID,
- (1 << clk), (1 << data));
- rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
- break;
- }
- }
- }
+ /* gpiopad */
+ i2c = radeon_combios_get_i2c_info_from_table(rdev);
+ if (i2c.valid)
+ rdev->i2c_bus[4] = radeon_i2c_create(dev, &i2c, "GPIOPAD_MASK");
} else if ((rdev->family == CHIP_R200) ||
(rdev->family >= CHIP_R300)) {
/* 0x68 */
@@ -2321,7 +2333,10 @@
connector = (tmp >> 12) & 0xf;
ddc_type = (tmp >> 8) & 0xf;
- ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
+ if (ddc_type == 5)
+ ddc_i2c = radeon_combios_get_i2c_info_from_table(rdev);
+ else
+ ddc_i2c = combios_setup_i2c_bus(rdev, ddc_type, 0, 0);
switch (connector) {
case CONNECTOR_PROPRIETARY_LEGACY:
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index 8a4c49e..b4a0db24 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -278,6 +278,30 @@
return 0;
}
+static void radeon_bo_vm_fence_va(struct radeon_cs_parser *parser,
+ struct radeon_fence *fence)
+{
+ struct radeon_fpriv *fpriv = parser->filp->driver_priv;
+ struct radeon_vm *vm = &fpriv->vm;
+ struct radeon_bo_list *lobj;
+
+ if (parser->chunk_ib_idx == -1) {
+ return;
+ }
+ if ((parser->cs_flags & RADEON_CS_USE_VM) == 0) {
+ return;
+ }
+
+ list_for_each_entry(lobj, &parser->validated, tv.head) {
+ struct radeon_bo_va *bo_va;
+ struct radeon_bo *rbo = lobj->bo;
+
+ bo_va = radeon_bo_va(rbo, vm);
+ radeon_fence_unref(&bo_va->fence);
+ bo_va->fence = radeon_fence_ref(fence);
+ }
+}
+
/**
* cs_parser_fini() - clean parser states
* @parser: parser structure holding parsing context.
@@ -290,11 +314,14 @@
{
unsigned i;
- if (!error)
+ if (!error) {
+ /* fence all bo va before ttm_eu_fence_buffer_objects so bo are still reserved */
+ radeon_bo_vm_fence_va(parser, parser->ib.fence);
ttm_eu_fence_buffer_objects(&parser->validated,
parser->ib.fence);
- else
+ } else {
ttm_eu_backoff_reservation(&parser->validated);
+ }
if (parser->relocs != NULL) {
for (i = 0; i < parser->nrelocs; i++) {
@@ -388,7 +415,6 @@
if (parser->chunk_ib_idx == -1)
return 0;
-
if ((parser->cs_flags & RADEON_CS_USE_VM) == 0)
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index 711e95a..8794744 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -67,7 +67,8 @@
if (ASIC_IS_DCE4(rdev)) {
WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
- WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+ WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+ EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
} else if (ASIC_IS_AVIVO(rdev)) {
WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, (AVIVO_D1CURSOR_MODE_24BPP << AVIVO_D1CURSOR_MODE_SHIFT));
@@ -94,7 +95,8 @@
if (ASIC_IS_DCE4(rdev)) {
WREG32(RADEON_MM_INDEX, EVERGREEN_CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, EVERGREEN_CURSOR_EN |
- EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT));
+ EVERGREEN_CURSOR_MODE(EVERGREEN_CURSOR_24_8_PRE_MULT) |
+ EVERGREEN_CURSOR_URGENT_CONTROL(EVERGREEN_CURSOR_URGENT_1_2));
} else if (ASIC_IS_AVIVO(rdev)) {
WREG32(RADEON_MM_INDEX, AVIVO_D1CUR_CONTROL + radeon_crtc->crtc_offset);
WREG32(RADEON_MM_DATA, AVIVO_D1CURSOR_EN |
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 742af82..d2e2438 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1009,6 +1009,7 @@
atomic_set(&rdev->ih.lock, 0);
mutex_init(&rdev->gem.mutex);
mutex_init(&rdev->pm.mutex);
+ mutex_init(&rdev->gpu_clock_mutex);
init_rwsem(&rdev->pm.mclk_lock);
init_rwsem(&rdev->exclusive_lock);
init_waitqueue_head(&rdev->irq.vblank_queue);
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
index dcea6f0..27d22d7 100644
--- a/drivers/gpu/drm/radeon/radeon_drv.c
+++ b/drivers/gpu/drm/radeon/radeon_drv.c
@@ -59,9 +59,13 @@
* 2.15.0 - add max_pipes query
* 2.16.0 - fix evergreen 2D tiled surface calculation
* 2.17.0 - add STRMOUT_BASE_UPDATE for r7xx
+ * 2.18.0 - r600-eg: allow "invalid" DB formats
+ * 2.19.0 - r600-eg: MSAA textures
+ * 2.20.0 - r600-si: RADEON_INFO_TIMESTAMP query
+ * 2.21.0 - r600-r700: FMASK and CMASK
*/
#define KMS_DRIVER_MAJOR 2
-#define KMS_DRIVER_MINOR 17
+#define KMS_DRIVER_MINOR 21
#define KMS_DRIVER_PATCHLEVEL 0
int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags);
int radeon_driver_unload_kms(struct drm_device *dev);
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index b372005..bb3b7fe 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -814,7 +814,7 @@
return -EINVAL;
}
- if (bo_va->valid)
+ if (bo_va->valid && mem)
return 0;
ngpu_pages = radeon_bo_ngpu_pages(bo);
@@ -859,11 +859,27 @@
struct radeon_bo *bo)
{
struct radeon_bo_va *bo_va;
+ int r;
bo_va = radeon_bo_va(bo, vm);
if (bo_va == NULL)
return 0;
+ /* wait for va use to end */
+ while (bo_va->fence) {
+ r = radeon_fence_wait(bo_va->fence, false);
+ if (r) {
+ DRM_ERROR("error while waiting for fence: %d\n", r);
+ }
+ if (r == -EDEADLK) {
+ r = radeon_gpu_reset(rdev);
+ if (!r)
+ continue;
+ }
+ break;
+ }
+ radeon_fence_unref(&bo_va->fence);
+
mutex_lock(&rdev->vm_manager.lock);
mutex_lock(&vm->mutex);
radeon_vm_bo_update_pte(rdev, vm, bo, NULL);
@@ -934,7 +950,7 @@
}
/**
- * radeon_vm_init - tear down a vm instance
+ * radeon_vm_fini - tear down a vm instance
*
* @rdev: radeon_device pointer
* @vm: requested vm
@@ -952,12 +968,15 @@
radeon_vm_unbind_locked(rdev, vm);
mutex_unlock(&rdev->vm_manager.lock);
- /* remove all bo */
+ /* remove all bo at this point non are busy any more because unbind
+ * waited for the last vm fence to signal
+ */
r = radeon_bo_reserve(rdev->ring_tmp_bo.bo, false);
if (!r) {
bo_va = radeon_bo_va(rdev->ring_tmp_bo.bo, vm);
list_del_init(&bo_va->bo_list);
list_del_init(&bo_va->vm_list);
+ radeon_fence_unref(&bo_va->fence);
radeon_bo_unreserve(rdev->ring_tmp_bo.bo);
kfree(bo_va);
}
@@ -969,6 +988,7 @@
r = radeon_bo_reserve(bo_va->bo, false);
if (!r) {
list_del_init(&bo_va->bo_list);
+ radeon_fence_unref(&bo_va->fence);
radeon_bo_unreserve(bo_va->bo);
kfree(bo_va);
}
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 84d0452..1b57b00 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -134,25 +134,16 @@
struct radeon_device *rdev = rbo->rdev;
struct radeon_fpriv *fpriv = file_priv->driver_priv;
struct radeon_vm *vm = &fpriv->vm;
- struct radeon_bo_va *bo_va, *tmp;
if (rdev->family < CHIP_CAYMAN) {
return;
}
if (radeon_bo_reserve(rbo, false)) {
+ dev_err(rdev->dev, "leaking bo va because we fail to reserve bo\n");
return;
}
- list_for_each_entry_safe(bo_va, tmp, &rbo->va, bo_list) {
- if (bo_va->vm == vm) {
- /* remove from this vm address space */
- mutex_lock(&vm->mutex);
- list_del(&bo_va->vm_list);
- mutex_unlock(&vm->mutex);
- list_del(&bo_va->bo_list);
- kfree(bo_va);
- }
- }
+ radeon_vm_bo_rmv(rdev, vm, rbo);
radeon_bo_unreserve(rbo);
}
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
index 1d73f16..414b4ac 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -29,6 +29,7 @@
#include "drm_sarea.h"
#include "radeon.h"
#include "radeon_drm.h"
+#include "radeon_asic.h"
#include <linux/vga_switcheroo.h>
#include <linux/slab.h>
@@ -167,17 +168,39 @@
int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
{
struct radeon_device *rdev = dev->dev_private;
- struct drm_radeon_info *info;
+ struct drm_radeon_info *info = data;
struct radeon_mode_info *minfo = &rdev->mode_info;
- uint32_t *value_ptr;
- uint32_t value;
+ uint32_t value, *value_ptr;
+ uint64_t value64, *value_ptr64;
struct drm_crtc *crtc;
int i, found;
- info = data;
+ /* TIMESTAMP is a 64-bit value, needs special handling. */
+ if (info->request == RADEON_INFO_TIMESTAMP) {
+ if (rdev->family >= CHIP_R600) {
+ value_ptr64 = (uint64_t*)((unsigned long)info->value);
+ if (rdev->family >= CHIP_TAHITI) {
+ value64 = si_get_gpu_clock(rdev);
+ } else {
+ value64 = r600_get_gpu_clock(rdev);
+ }
+
+ if (DRM_COPY_TO_USER(value_ptr64, &value64, sizeof(value64))) {
+ DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ return 0;
+ } else {
+ DRM_DEBUG_KMS("timestamp is r6xx+ only!\n");
+ return -EINVAL;
+ }
+ }
+
value_ptr = (uint32_t *)((unsigned long)info->value);
- if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value)))
+ if (DRM_COPY_FROM_USER(&value, value_ptr, sizeof(value))) {
+ DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__);
return -EFAULT;
+ }
switch (info->request) {
case RADEON_INFO_DEVICE_ID:
@@ -337,7 +360,7 @@
return -EINVAL;
}
if (DRM_COPY_TO_USER(value_ptr, &value, sizeof(uint32_t))) {
- DRM_ERROR("copy_to_user\n");
+ DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__);
return -EFAULT;
}
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index d5fd615..94b4a1c 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -1025,9 +1025,11 @@
static void radeon_crtc_prepare(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct drm_crtc *crtci;
+ radeon_crtc->in_mode_set = true;
/*
* The hardware wedges sometimes if you reconfigure one CRTC
* whilst another is running (see fdo bug #24611).
@@ -1038,6 +1040,7 @@
static void radeon_crtc_commit(struct drm_crtc *crtc)
{
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
struct drm_device *dev = crtc->dev;
struct drm_crtc *crtci;
@@ -1048,6 +1051,7 @@
if (crtci->enabled)
radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
}
+ radeon_crtc->in_mode_set = false;
}
static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index f380d59..d569789 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -275,6 +275,7 @@
u16 lut_r[256], lut_g[256], lut_b[256];
bool enabled;
bool can_tile;
+ bool in_mode_set;
uint32_t crtc_offset;
struct drm_gem_object *cursor_bo;
uint64_t cursor_addr;
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 1f1a4c8..9024e72 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -52,11 +52,7 @@
list_for_each_entry_safe(bo_va, tmp, &bo->va, bo_list) {
/* remove from all vm address space */
- mutex_lock(&bo_va->vm->mutex);
- list_del(&bo_va->vm_list);
- mutex_unlock(&bo_va->vm->mutex);
- list_del(&bo_va->bo_list);
- kfree(bo_va);
+ radeon_vm_bo_rmv(bo->rdev, bo_va->vm, bo);
}
}
@@ -136,6 +132,7 @@
acc_size = ttm_bo_dma_acc_size(&rdev->mman.bdev, size,
sizeof(struct radeon_bo));
+retry:
bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
if (bo == NULL)
return -ENOMEM;
@@ -149,8 +146,6 @@
bo->surface_reg = -1;
INIT_LIST_HEAD(&bo->list);
INIT_LIST_HEAD(&bo->va);
-
-retry:
radeon_ttm_placement_from_domain(bo, domain);
/* Kernel allocation are uninterruptible */
down_read(&rdev->pm.mclk_lock);
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index ec79b37..43c431a 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -706,6 +706,7 @@
if (radeon_debugfs_ring_init(rdev, ring)) {
DRM_ERROR("Failed to register debugfs file for rings !\n");
}
+ radeon_ring_lockup_update(ring);
return 0;
}
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index 5e659b0..f93e45d 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -744,14 +744,6 @@
0x00028C38 CB_CLRCMP_DST
0x00028C3C CB_CLRCMP_MSK
0x00028C34 CB_CLRCMP_SRC
-0x00028100 CB_COLOR0_MASK
-0x00028104 CB_COLOR1_MASK
-0x00028108 CB_COLOR2_MASK
-0x0002810C CB_COLOR3_MASK
-0x00028110 CB_COLOR4_MASK
-0x00028114 CB_COLOR5_MASK
-0x00028118 CB_COLOR6_MASK
-0x0002811C CB_COLOR7_MASK
0x00028808 CB_COLOR_CONTROL
0x0002842C CB_FOG_BLUE
0x00028428 CB_FOG_GREEN
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index a12fbcc..aa8ef49 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -281,12 +281,8 @@
void rv515_mc_stop(struct radeon_device *rdev, struct rv515_mc_save *save)
{
- save->d1vga_control = RREG32(R_000330_D1VGA_CONTROL);
- save->d2vga_control = RREG32(R_000338_D2VGA_CONTROL);
save->vga_render_control = RREG32(R_000300_VGA_RENDER_CONTROL);
save->vga_hdp_control = RREG32(R_000328_VGA_HDP_CONTROL);
- save->d1crtc_control = RREG32(R_006080_D1CRTC_CONTROL);
- save->d2crtc_control = RREG32(R_006880_D2CRTC_CONTROL);
/* Stop all video */
WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0);
@@ -311,15 +307,6 @@
/* Unlock host access */
WREG32(R_000328_VGA_HDP_CONTROL, save->vga_hdp_control);
mdelay(1);
- /* Restore video state */
- WREG32(R_000330_D1VGA_CONTROL, save->d1vga_control);
- WREG32(R_000338_D2VGA_CONTROL, save->d2vga_control);
- WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 1);
- WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 1);
- WREG32(R_006080_D1CRTC_CONTROL, save->d1crtc_control);
- WREG32(R_006880_D2CRTC_CONTROL, save->d2crtc_control);
- WREG32(R_0060E8_D1CRTC_UPDATE_LOCK, 0);
- WREG32(R_0068E8_D2CRTC_UPDATE_LOCK, 0);
WREG32(R_000300_VGA_RENDER_CONTROL, save->vga_render_control);
}
diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c
index c053f81..0139e22 100644
--- a/drivers/gpu/drm/radeon/si.c
+++ b/drivers/gpu/drm/radeon/si.c
@@ -1639,11 +1639,19 @@
/* XXX what about 12? */
rdev->config.si.tile_config |= (3 << 0);
break;
- }
- if ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT)
- rdev->config.si.tile_config |= 1 << 4;
- else
+ }
+ switch ((mc_arb_ramcfg & NOOFBANK_MASK) >> NOOFBANK_SHIFT) {
+ case 0: /* four banks */
rdev->config.si.tile_config |= 0 << 4;
+ break;
+ case 1: /* eight banks */
+ rdev->config.si.tile_config |= 1 << 4;
+ break;
+ case 2: /* sixteen banks */
+ default:
+ rdev->config.si.tile_config |= 2 << 4;
+ break;
+ }
rdev->config.si.tile_config |=
((gb_addr_config & PIPE_INTERLEAVE_SIZE_MASK) >> PIPE_INTERLEAVE_SIZE_SHIFT) << 8;
rdev->config.si.tile_config |=
@@ -3960,3 +3968,22 @@
rdev->bios = NULL;
}
+/**
+ * si_get_gpu_clock - return GPU clock counter snapshot
+ *
+ * @rdev: radeon_device pointer
+ *
+ * Fetches a GPU clock counter snapshot (SI).
+ * Returns the 64 bit clock counter snapshot.
+ */
+uint64_t si_get_gpu_clock(struct radeon_device *rdev)
+{
+ uint64_t clock;
+
+ mutex_lock(&rdev->gpu_clock_mutex);
+ WREG32(RLC_CAPTURE_GPU_CLOCK_COUNT, 1);
+ clock = (uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_LSB) |
+ ((uint64_t)RREG32(RLC_GPU_CLOCK_COUNT_MSB) << 32ULL);
+ mutex_unlock(&rdev->gpu_clock_mutex);
+ return clock;
+}
diff --git a/drivers/gpu/drm/radeon/sid.h b/drivers/gpu/drm/radeon/sid.h
index 7869089..ef4815c 100644
--- a/drivers/gpu/drm/radeon/sid.h
+++ b/drivers/gpu/drm/radeon/sid.h
@@ -698,6 +698,9 @@
#define RLC_UCODE_ADDR 0xC32C
#define RLC_UCODE_DATA 0xC330
+#define RLC_GPU_CLOCK_COUNT_LSB 0xC338
+#define RLC_GPU_CLOCK_COUNT_MSB 0xC33C
+#define RLC_CAPTURE_GPU_CLOCK_COUNT 0xC340
#define RLC_MC_CNTL 0xC344
#define RLC_UCODE_CNTL 0xC348
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig
index 0b5e096..56e0bf3 100644
--- a/drivers/gpu/drm/udl/Kconfig
+++ b/drivers/gpu/drm/udl/Kconfig
@@ -1,6 +1,7 @@
config DRM_UDL
tristate "DisplayLink"
depends on DRM && EXPERIMENTAL
+ depends on USB_ARCH_HAS_HCD
select DRM_USB
select FB_SYS_FILLRECT
select FB_SYS_COPYAREA
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
index 7bd65bd..291ecc1 100644
--- a/drivers/gpu/drm/udl/udl_gem.c
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -308,7 +308,7 @@
/* need to attach */
attach = dma_buf_attach(dma_buf, dev->dev);
if (IS_ERR(attach))
- return ERR_PTR(PTR_ERR(attach));
+ return ERR_CAST(attach);
sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
if (IS_ERR(sg)) {
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index f5dd89e..9159d48 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -354,8 +354,7 @@
static void udl_crtc_disable(struct drm_crtc *crtc)
{
-
-
+ udl_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
}
static void udl_crtc_destroy(struct drm_crtc *crtc)
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 6b0078f..c50724b 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -1688,15 +1688,19 @@
struct vmw_private *dev_priv = vmw_priv(crtc->dev);
struct drm_framebuffer *old_fb = crtc->fb;
struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb);
- struct drm_file *file_priv = event->base.file_priv;
+ struct drm_file *file_priv ;
struct vmw_fence_obj *fence = NULL;
struct drm_clip_rect clips;
int ret;
+ if (event == NULL)
+ return -EINVAL;
+
/* require ScreenObject support for page flipping */
if (!dev_priv->sou_priv)
return -ENOSYS;
+ file_priv = event->base.file_priv;
if (!vmw_kms_screen_object_flippable(dev_priv, crtc))
return -EINVAL;
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 5b3c7d1..e25cf31 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -70,27 +70,12 @@
.clients = LIST_HEAD_INIT(vgasr_priv.clients),
};
-int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
+static bool vga_switcheroo_ready(void)
{
- mutex_lock(&vgasr_mutex);
- if (vgasr_priv.handler) {
- mutex_unlock(&vgasr_mutex);
- return -EINVAL;
- }
-
- vgasr_priv.handler = handler;
- mutex_unlock(&vgasr_mutex);
- return 0;
+ /* we're ready if we get two clients + handler */
+ return !vgasr_priv.active &&
+ vgasr_priv.registered_clients == 2 && vgasr_priv.handler;
}
-EXPORT_SYMBOL(vga_switcheroo_register_handler);
-
-void vga_switcheroo_unregister_handler(void)
-{
- mutex_lock(&vgasr_mutex);
- vgasr_priv.handler = NULL;
- mutex_unlock(&vgasr_mutex);
-}
-EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
static void vga_switcheroo_enable(void)
{
@@ -98,7 +83,8 @@
struct vga_switcheroo_client *client;
/* call the handler to init */
- vgasr_priv.handler->init();
+ if (vgasr_priv.handler->init)
+ vgasr_priv.handler->init();
list_for_each_entry(client, &vgasr_priv.clients, list) {
if (client->id != -1)
@@ -113,6 +99,37 @@
vgasr_priv.active = true;
}
+int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
+{
+ mutex_lock(&vgasr_mutex);
+ if (vgasr_priv.handler) {
+ mutex_unlock(&vgasr_mutex);
+ return -EINVAL;
+ }
+
+ vgasr_priv.handler = handler;
+ if (vga_switcheroo_ready()) {
+ printk(KERN_INFO "vga_switcheroo: enabled\n");
+ vga_switcheroo_enable();
+ }
+ mutex_unlock(&vgasr_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(vga_switcheroo_register_handler);
+
+void vga_switcheroo_unregister_handler(void)
+{
+ mutex_lock(&vgasr_mutex);
+ vgasr_priv.handler = NULL;
+ if (vgasr_priv.active) {
+ pr_info("vga_switcheroo: disabled\n");
+ vga_switcheroo_debugfs_fini(&vgasr_priv);
+ vgasr_priv.active = false;
+ }
+ mutex_unlock(&vgasr_mutex);
+}
+EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
+
static int register_client(struct pci_dev *pdev,
const struct vga_switcheroo_client_ops *ops,
int id, bool active)
@@ -134,9 +151,7 @@
if (client_is_vga(client))
vgasr_priv.registered_clients++;
- /* if we get two clients + handler */
- if (!vgasr_priv.active &&
- vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
+ if (vga_switcheroo_ready()) {
printk(KERN_INFO "vga_switcheroo: enabled\n");
vga_switcheroo_enable();
}
diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c
index faa16f8..0fa356f 100644
--- a/drivers/hwmon/coretemp.c
+++ b/drivers/hwmon/coretemp.c
@@ -196,7 +196,7 @@
int tjmax;
};
-static struct tjmax __cpuinitconst tjmax_table[] = {
+static const struct tjmax __cpuinitconst tjmax_table[] = {
{ "CPU D410", 100000 },
{ "CPU D425", 100000 },
{ "CPU D510", 100000 },
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index ab48252..5b1a6a6 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -1206,7 +1206,7 @@
int err = -ENODEV;
u16 val;
- static const __initdata char *names[] = {
+ static __initconst char *const names[] = {
"W83627HF",
"W83627THF",
"W83697HF",
diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c
index aedb94f..dae3ddfe 100644
--- a/drivers/i2c/busses/i2c-diolan-u2c.c
+++ b/drivers/i2c/busses/i2c-diolan-u2c.c
@@ -405,6 +405,7 @@
}
}
}
+ ret = num;
abort:
sret = diolan_i2c_stop(dev);
if (sret < 0 && ret >= 0)
diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c
index 5e6f1ee..61b00ed 100644
--- a/drivers/i2c/busses/i2c-nomadik.c
+++ b/drivers/i2c/busses/i2c-nomadik.c
@@ -350,10 +350,6 @@
i2c_clk = clk_get_rate(dev->clk);
- /* fallback to std. mode if machine has not provided it */
- if (dev->cfg.clk_freq == 0)
- dev->cfg.clk_freq = 100000;
-
/*
* The spec says, in case of std. mode the divider is
* 2 whereas it is 3 for fast and fastplus mode of
@@ -911,20 +907,32 @@
.functionality = nmk_i2c_functionality
};
+static struct nmk_i2c_controller u8500_i2c = {
+ /*
+ * Slave data setup time; 250ns, 100ns, and 10ns, which
+ * is 14, 6 and 2 respectively for a 48Mhz i2c clock.
+ */
+ .slsu = 0xe,
+ .tft = 1, /* Tx FIFO threshold */
+ .rft = 8, /* Rx FIFO threshold */
+ .clk_freq = 400000, /* fast mode operation */
+ .timeout = 200, /* Slave response timeout(ms) */
+ .sm = I2C_FREQ_MODE_FAST,
+};
+
static atomic_t adapter_id = ATOMIC_INIT(0);
static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
- struct nmk_i2c_controller *pdata =
- adev->dev.platform_data;
+ struct nmk_i2c_controller *pdata = adev->dev.platform_data;
struct nmk_i2c_dev *dev;
struct i2c_adapter *adap;
- if (!pdata) {
- dev_warn(&adev->dev, "no platform data\n");
- return -ENODEV;
- }
+ if (!pdata)
+ /* No i2c configuration found, using the default. */
+ pdata = &u8500_i2c;
+
dev = kzalloc(sizeof(struct nmk_i2c_dev), GFP_KERNEL);
if (!dev) {
dev_err(&adev->dev, "cannot allocate memory\n");
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index 6849635..5d19a49 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -584,7 +584,7 @@
r = pm_runtime_get_sync(dev->dev);
if (IS_ERR_VALUE(r))
- return r;
+ goto out;
r = omap_i2c_wait_for_bb(dev);
if (r < 0)
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index 66eb53f..9a08c57 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -712,7 +712,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int tegra_i2c_suspend(struct device *dev)
{
struct tegra_i2c_dev *i2c_dev = dev_get_drvdata(dev);
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index f559088..e872617 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -606,8 +606,9 @@
intel_idle_cpuidle_driver_init();
retval = cpuidle_register_driver(&intel_idle_driver);
if (retval) {
+ struct cpuidle_driver *drv = cpuidle_get_driver();
printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
- cpuidle_get_driver()->name);
+ drv ? drv->name : "none");
return retval;
}
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 59fbb3ae..e35bb8f 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -129,7 +129,7 @@
{
struct adf4350_platform_data *pdata = st->pdata;
u64 tmp;
- u32 div_gcd, prescaler;
+ u32 div_gcd, prescaler, chspc;
u16 mdiv, r_cnt = 0;
u8 band_sel_div;
@@ -158,14 +158,20 @@
if (pdata->ref_div_factor)
r_cnt = pdata->ref_div_factor - 1;
- do {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ chspc = st->chspc;
- st->r1_mod = st->fpfd / st->chspc;
- while (st->r1_mod > ADF4350_MAX_MODULUS) {
- r_cnt = adf4350_tune_r_cnt(st, r_cnt);
- st->r1_mod = st->fpfd / st->chspc;
- }
+ do {
+ do {
+ do {
+ r_cnt = adf4350_tune_r_cnt(st, r_cnt);
+ st->r1_mod = st->fpfd / chspc;
+ if (r_cnt > ADF4350_MAX_R_CNT) {
+ /* try higher spacing values */
+ chspc++;
+ r_cnt = 0;
+ }
+ } while ((st->r1_mod > ADF4350_MAX_MODULUS) && r_cnt);
+ } while (r_cnt == 0);
tmp = freq * (u64)st->r1_mod + (st->fpfd > 1);
do_div(tmp, st->fpfd); /* Div round closest (n + d/2)/d */
@@ -194,7 +200,7 @@
st->regs[ADF4350_REG0] = ADF4350_REG0_INT(st->r0_int) |
ADF4350_REG0_FRACT(st->r0_fract);
- st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(0) |
+ st->regs[ADF4350_REG1] = ADF4350_REG1_PHASE(1) |
ADF4350_REG1_MOD(st->r1_mod) |
prescaler;
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index 1cbb449..9a99f43 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -271,9 +271,10 @@
const unsigned long *scan_mask)
{
struct adjd_s311_data *data = iio_priv(indio_dev);
- data->buffer = krealloc(data->buffer, indio_dev->scan_bytes,
- GFP_KERNEL);
- if (!data->buffer)
+
+ kfree(data->buffer);
+ data->buffer = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
+ if (data->buffer == NULL)
return -ENOMEM;
return 0;
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index c3e7bac..e45712a9 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -404,7 +404,7 @@
return ret;
}
-static int show_thresh_either_en(struct device *dev,
+static ssize_t show_thresh_either_en(struct device *dev,
struct device_attribute *attr,
char *buf)
{
@@ -424,7 +424,7 @@
return scnprintf(buf, PAGE_SIZE, "%u\n", enable);
}
-static int store_thresh_either_en(struct device *dev,
+static ssize_t store_thresh_either_en(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t len)
{
diff --git a/drivers/infiniband/core/ucma.c b/drivers/infiniband/core/ucma.c
index 6bf8504..055ed59 100644
--- a/drivers/infiniband/core/ucma.c
+++ b/drivers/infiniband/core/ucma.c
@@ -267,6 +267,7 @@
if (!uevent)
return event->event == RDMA_CM_EVENT_CONNECT_REQUEST;
+ mutex_lock(&ctx->file->mut);
uevent->cm_id = cm_id;
ucma_set_event_context(ctx, event, uevent);
uevent->resp.event = event->event;
@@ -277,7 +278,6 @@
ucma_copy_conn_event(&uevent->resp.param.conn,
&event->param.conn);
- mutex_lock(&ctx->file->mut);
if (event->event == RDMA_CM_EVENT_CONNECT_REQUEST) {
if (!ctx->backlog) {
ret = -ENOMEM;
diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c
index 8c81992..e4a7315 100644
--- a/drivers/infiniband/hw/amso1100/c2_rnic.c
+++ b/drivers/infiniband/hw/amso1100/c2_rnic.c
@@ -439,7 +439,7 @@
/*
* Called by c2_probe to initialize the RNIC. This principally
- * involves initalizing the various limits and resouce pools that
+ * involves initializing the various limits and resource pools that
* comprise the RNIC instance.
*/
int __devinit c2_rnic_init(struct c2_dev *c2dev)
diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c
index 77b6b18..aaf88ef9 100644
--- a/drivers/infiniband/hw/cxgb3/iwch_cm.c
+++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c
@@ -1680,7 +1680,7 @@
* T3A does 3 things when a TERM is received:
* 1) send up a CPL_RDMA_TERMINATE message with the TERM packet
* 2) generate an async event on the QP with the TERMINATE opcode
- * 3) post a TERMINATE opcde cqe into the associated CQ.
+ * 3) post a TERMINATE opcode cqe into the associated CQ.
*
* For (1), we save the message in the qp for later consumer consumption.
* For (2), we move the QP into TERMINATE, post a QP event and disconnect.
diff --git a/drivers/infiniband/hw/mlx4/mad.c b/drivers/infiniband/hw/mlx4/mad.c
index c27141f..9c2ae7e 100644
--- a/drivers/infiniband/hw/mlx4/mad.c
+++ b/drivers/infiniband/hw/mlx4/mad.c
@@ -125,6 +125,7 @@
{
struct ib_ah *new_ah;
struct ib_ah_attr ah_attr;
+ unsigned long flags;
if (!dev->send_agent[port_num - 1][0])
return;
@@ -139,11 +140,11 @@
if (IS_ERR(new_ah))
return;
- spin_lock(&dev->sm_lock);
+ spin_lock_irqsave(&dev->sm_lock, flags);
if (dev->sm_ah[port_num - 1])
ib_destroy_ah(dev->sm_ah[port_num - 1]);
dev->sm_ah[port_num - 1] = new_ah;
- spin_unlock(&dev->sm_lock);
+ spin_unlock_irqrestore(&dev->sm_lock, flags);
}
/*
@@ -197,13 +198,15 @@
static void node_desc_override(struct ib_device *dev,
struct ib_mad *mad)
{
+ unsigned long flags;
+
if ((mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_LID_ROUTED ||
mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_DIRECTED_ROUTE) &&
mad->mad_hdr.method == IB_MGMT_METHOD_GET_RESP &&
mad->mad_hdr.attr_id == IB_SMP_ATTR_NODE_DESC) {
- spin_lock(&to_mdev(dev)->sm_lock);
+ spin_lock_irqsave(&to_mdev(dev)->sm_lock, flags);
memcpy(((struct ib_smp *) mad)->data, dev->node_desc, 64);
- spin_unlock(&to_mdev(dev)->sm_lock);
+ spin_unlock_irqrestore(&to_mdev(dev)->sm_lock, flags);
}
}
@@ -213,6 +216,7 @@
struct ib_mad_send_buf *send_buf;
struct ib_mad_agent *agent = dev->send_agent[port_num - 1][qpn];
int ret;
+ unsigned long flags;
if (agent) {
send_buf = ib_create_send_mad(agent, qpn, 0, 0, IB_MGMT_MAD_HDR,
@@ -225,13 +229,13 @@
* wrong following the IB spec strictly, but we know
* it's OK for our devices).
*/
- spin_lock(&dev->sm_lock);
+ spin_lock_irqsave(&dev->sm_lock, flags);
memcpy(send_buf->mad, mad, sizeof *mad);
if ((send_buf->ah = dev->sm_ah[port_num - 1]))
ret = ib_post_send_mad(send_buf, NULL);
else
ret = -EINVAL;
- spin_unlock(&dev->sm_lock);
+ spin_unlock_irqrestore(&dev->sm_lock, flags);
if (ret)
ib_free_send_mad(send_buf);
diff --git a/drivers/infiniband/hw/mlx4/main.c b/drivers/infiniband/hw/mlx4/main.c
index fe2088c..cc05579 100644
--- a/drivers/infiniband/hw/mlx4/main.c
+++ b/drivers/infiniband/hw/mlx4/main.c
@@ -423,6 +423,7 @@
struct ib_device_modify *props)
{
struct mlx4_cmd_mailbox *mailbox;
+ unsigned long flags;
if (mask & ~IB_DEVICE_MODIFY_NODE_DESC)
return -EOPNOTSUPP;
@@ -430,9 +431,9 @@
if (!(mask & IB_DEVICE_MODIFY_NODE_DESC))
return 0;
- spin_lock(&to_mdev(ibdev)->sm_lock);
+ spin_lock_irqsave(&to_mdev(ibdev)->sm_lock, flags);
memcpy(ibdev->node_desc, props->node_desc, 64);
- spin_unlock(&to_mdev(ibdev)->sm_lock);
+ spin_unlock_irqrestore(&to_mdev(ibdev)->sm_lock, flags);
/*
* If possible, pass node desc to FW, so it can generate
diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c
index a6d8ea0..f585edd 100644
--- a/drivers/infiniband/hw/mlx4/qp.c
+++ b/drivers/infiniband/hw/mlx4/qp.c
@@ -1407,6 +1407,7 @@
struct mlx4_wqe_mlx_seg *mlx = wqe;
struct mlx4_wqe_inline_seg *inl = wqe + sizeof *mlx;
struct mlx4_ib_ah *ah = to_mah(wr->wr.ud.ah);
+ struct net_device *ndev;
union ib_gid sgid;
u16 pkey;
int send_size;
@@ -1483,7 +1484,10 @@
memcpy(sqp->ud_header.eth.dmac_h, ah->av.eth.mac, 6);
/* FIXME: cache smac value? */
- smac = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1]->dev_addr;
+ ndev = to_mdev(sqp->qp.ibqp.device)->iboe.netdevs[sqp->qp.port - 1];
+ if (!ndev)
+ return -ENODEV;
+ smac = ndev->dev_addr;
memcpy(sqp->ud_header.eth.smac_h, smac, 6);
if (!memcmp(sqp->ud_header.eth.smac_h, sqp->ud_header.eth.dmac_h, 6))
mlx->flags |= cpu_to_be32(MLX4_WQE_CTRL_FORCE_LOOPBACK);
diff --git a/drivers/infiniband/hw/ocrdma/ocrdma_main.c b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
index 5a04452..c4e0131 100644
--- a/drivers/infiniband/hw/ocrdma/ocrdma_main.c
+++ b/drivers/infiniband/hw/ocrdma/ocrdma_main.c
@@ -161,7 +161,7 @@
ocrdma_get_guid(dev, &sgid->raw[8]);
}
-#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#if IS_ENABLED(CONFIG_VLAN_8021Q)
static void ocrdma_add_vlan_sgids(struct ocrdma_dev *dev)
{
struct net_device *netdev, *tmp;
@@ -202,14 +202,13 @@
return 0;
}
-#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_VLAN_8021Q)
+#if IS_ENABLED(CONFIG_IPV6)
static int ocrdma_inet6addr_event(struct notifier_block *notifier,
unsigned long event, void *ptr)
{
struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr;
- struct net_device *event_netdev = ifa->idev->dev;
- struct net_device *netdev = NULL;
+ struct net_device *netdev = ifa->idev->dev;
struct ib_event gid_event;
struct ocrdma_dev *dev;
bool found = false;
@@ -217,11 +216,12 @@
bool is_vlan = false;
u16 vid = 0;
- netdev = vlan_dev_real_dev(event_netdev);
- if (netdev != event_netdev) {
- is_vlan = true;
- vid = vlan_dev_vlan_id(event_netdev);
+ is_vlan = netdev->priv_flags & IFF_802_1Q_VLAN;
+ if (is_vlan) {
+ vid = vlan_dev_vlan_id(netdev);
+ netdev = vlan_dev_real_dev(netdev);
}
+
rcu_read_lock();
list_for_each_entry_rcu(dev, &ocrdma_dev_list, entry) {
if (dev->nic_info.netdev == netdev) {
diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c
index 0d7280a..3f6b21e 100644
--- a/drivers/infiniband/hw/qib/qib_iba7322.c
+++ b/drivers/infiniband/hw/qib/qib_iba7322.c
@@ -6346,8 +6346,10 @@
dd->piobcnt4k * dd->align4k;
dd->piovl15base = ioremap_nocache(vl15off,
NUM_VL15_BUFS * dd->align4k);
- if (!dd->piovl15base)
+ if (!dd->piovl15base) {
+ ret = -ENOMEM;
goto bail;
+ }
}
qib_7322_set_baseaddrs(dd); /* set chip access pointers now */
diff --git a/drivers/infiniband/hw/qib/qib_sd7220.c b/drivers/infiniband/hw/qib/qib_sd7220.c
index a322d51..50a8a0d 100644
--- a/drivers/infiniband/hw/qib/qib_sd7220.c
+++ b/drivers/infiniband/hw/qib/qib_sd7220.c
@@ -372,7 +372,7 @@
/* Read CTRL reg for each channel to check TRIMDONE */
if (baduns & (1 << chn)) {
qib_dev_err(dd,
- "Reseting TRIMDONE on chn %d (%s)\n",
+ "Resetting TRIMDONE on chn %d (%s)\n",
chn, where);
ret = qib_sd7220_reg_mod(dd, IB_7220_SERDES,
IB_CTRL2(chn), 0x10, 0x10);
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_cm.c b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
index 95ecf4e..24683fd 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_cm.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_cm.c
@@ -1271,12 +1271,15 @@
void ipoib_cm_destroy_tx(struct ipoib_cm_tx *tx)
{
struct ipoib_dev_priv *priv = netdev_priv(tx->dev);
+ unsigned long flags;
if (test_and_clear_bit(IPOIB_FLAG_INITIALIZED, &tx->flags)) {
+ spin_lock_irqsave(&priv->lock, flags);
list_move(&tx->list, &priv->cm.reap_list);
queue_work(ipoib_workqueue, &priv->cm.reap_task);
ipoib_dbg(priv, "Reap connection for gid %pI6\n",
tx->neigh->daddr + 4);
tx->neigh = NULL;
+ spin_unlock_irqrestore(&priv->lock, flags);
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 97920b7..3e2085a 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -1052,7 +1052,7 @@
for (n = rcu_dereference_protected(*np,
lockdep_is_held(&ntbl->rwlock));
n != NULL;
- n = rcu_dereference_protected(neigh->hnext,
+ n = rcu_dereference_protected(*np,
lockdep_is_held(&ntbl->rwlock))) {
if (n == neigh) {
/* found */
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index bcbf22e..1b5b0c7 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -586,24 +586,62 @@
scmnd->sc_data_direction);
}
-static void srp_remove_req(struct srp_target_port *target,
- struct srp_request *req, s32 req_lim_delta)
+/**
+ * srp_claim_req - Take ownership of the scmnd associated with a request.
+ * @target: SRP target port.
+ * @req: SRP request.
+ * @scmnd: If NULL, take ownership of @req->scmnd. If not NULL, only take
+ * ownership of @req->scmnd if it equals @scmnd.
+ *
+ * Return value:
+ * Either NULL or a pointer to the SCSI command the caller became owner of.
+ */
+static struct scsi_cmnd *srp_claim_req(struct srp_target_port *target,
+ struct srp_request *req,
+ struct scsi_cmnd *scmnd)
{
unsigned long flags;
- srp_unmap_data(req->scmnd, target, req);
+ spin_lock_irqsave(&target->lock, flags);
+ if (!scmnd) {
+ scmnd = req->scmnd;
+ req->scmnd = NULL;
+ } else if (req->scmnd == scmnd) {
+ req->scmnd = NULL;
+ } else {
+ scmnd = NULL;
+ }
+ spin_unlock_irqrestore(&target->lock, flags);
+
+ return scmnd;
+}
+
+/**
+ * srp_free_req() - Unmap data and add request to the free request list.
+ */
+static void srp_free_req(struct srp_target_port *target,
+ struct srp_request *req, struct scsi_cmnd *scmnd,
+ s32 req_lim_delta)
+{
+ unsigned long flags;
+
+ srp_unmap_data(scmnd, target, req);
+
spin_lock_irqsave(&target->lock, flags);
target->req_lim += req_lim_delta;
- req->scmnd = NULL;
list_add_tail(&req->list, &target->free_reqs);
spin_unlock_irqrestore(&target->lock, flags);
}
static void srp_reset_req(struct srp_target_port *target, struct srp_request *req)
{
- req->scmnd->result = DID_RESET << 16;
- req->scmnd->scsi_done(req->scmnd);
- srp_remove_req(target, req, 0);
+ struct scsi_cmnd *scmnd = srp_claim_req(target, req, NULL);
+
+ if (scmnd) {
+ scmnd->result = DID_RESET << 16;
+ scmnd->scsi_done(scmnd);
+ srp_free_req(target, req, scmnd, 0);
+ }
}
static int srp_reconnect_target(struct srp_target_port *target)
@@ -1073,11 +1111,18 @@
complete(&target->tsk_mgmt_done);
} else {
req = &target->req_ring[rsp->tag];
- scmnd = req->scmnd;
- if (!scmnd)
+ scmnd = srp_claim_req(target, req, NULL);
+ if (!scmnd) {
shost_printk(KERN_ERR, target->scsi_host,
"Null scmnd for RSP w/tag %016llx\n",
(unsigned long long) rsp->tag);
+
+ spin_lock_irqsave(&target->lock, flags);
+ target->req_lim += be32_to_cpu(rsp->req_lim_delta);
+ spin_unlock_irqrestore(&target->lock, flags);
+
+ return;
+ }
scmnd->result = rsp->status;
if (rsp->flags & SRP_RSP_FLAG_SNSVALID) {
@@ -1092,7 +1137,9 @@
else if (rsp->flags & (SRP_RSP_FLAG_DIOVER | SRP_RSP_FLAG_DIUNDER))
scsi_set_resid(scmnd, be32_to_cpu(rsp->data_in_res_cnt));
- srp_remove_req(target, req, be32_to_cpu(rsp->req_lim_delta));
+ srp_free_req(target, req, scmnd,
+ be32_to_cpu(rsp->req_lim_delta));
+
scmnd->host_scribble = NULL;
scmnd->scsi_done(scmnd);
}
@@ -1631,25 +1678,17 @@
{
struct srp_target_port *target = host_to_target(scmnd->device->host);
struct srp_request *req = (struct srp_request *) scmnd->host_scribble;
- int ret = SUCCESS;
shost_printk(KERN_ERR, target->scsi_host, "SRP abort called\n");
- if (!req || target->qp_in_error)
+ if (!req || target->qp_in_error || !srp_claim_req(target, req, scmnd))
return FAILED;
- if (srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
- SRP_TSK_ABORT_TASK))
- return FAILED;
+ srp_send_tsk_mgmt(target, req->index, scmnd->device->lun,
+ SRP_TSK_ABORT_TASK);
+ srp_free_req(target, req, scmnd, 0);
+ scmnd->result = DID_ABORT << 16;
- if (req->scmnd) {
- if (!target->tsk_mgmt_status) {
- srp_remove_req(target, req, 0);
- scmnd->result = DID_ABORT << 16;
- } else
- ret = FAILED;
- }
-
- return ret;
+ return SUCCESS;
}
static int srp_reset_device(struct scsi_cmnd *scmnd)
diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c
index 7a0ce8d..9e1449f 100644
--- a/drivers/infiniband/ulp/srpt/ib_srpt.c
+++ b/drivers/infiniband/ulp/srpt/ib_srpt.c
@@ -1469,7 +1469,7 @@
*
* XXX: what is now target_execute_cmd used to be asynchronous, and unmapping
* the data that has been transferred via IB RDMA had to be postponed until the
- * check_stop_free() callback. None of this is nessecary anymore and needs to
+ * check_stop_free() callback. None of this is necessary anymore and needs to
* be cleaned up.
*/
static void srpt_handle_rdma_comp(struct srpt_rdma_ch *ch,
diff --git a/drivers/input/touchscreen/eeti_ts.c b/drivers/input/touchscreen/eeti_ts.c
index 503c709..908407e 100644
--- a/drivers/input/touchscreen/eeti_ts.c
+++ b/drivers/input/touchscreen/eeti_ts.c
@@ -48,7 +48,7 @@
struct input_dev *input;
struct work_struct work;
struct mutex mutex;
- int irq, irq_active_high;
+ int irq_gpio, irq, irq_active_high;
};
#define EETI_TS_BITDEPTH (11)
@@ -62,7 +62,7 @@
static inline int eeti_ts_irq_active(struct eeti_ts_priv *priv)
{
- return gpio_get_value(irq_to_gpio(priv->irq)) == priv->irq_active_high;
+ return gpio_get_value(priv->irq_gpio) == priv->irq_active_high;
}
static void eeti_ts_read(struct work_struct *work)
@@ -157,7 +157,7 @@
static int __devinit eeti_ts_probe(struct i2c_client *client,
const struct i2c_device_id *idp)
{
- struct eeti_ts_platform_data *pdata;
+ struct eeti_ts_platform_data *pdata = client->dev.platform_data;
struct eeti_ts_priv *priv;
struct input_dev *input;
unsigned int irq_flags;
@@ -199,9 +199,12 @@
priv->client = client;
priv->input = input;
- priv->irq = client->irq;
+ priv->irq_gpio = pdata->irq_gpio;
+ priv->irq = gpio_to_irq(pdata->irq_gpio);
- pdata = client->dev.platform_data;
+ err = gpio_request_one(pdata->irq_gpio, GPIOF_IN, client->name);
+ if (err < 0)
+ goto err1;
if (pdata)
priv->irq_active_high = pdata->irq_active_high;
@@ -215,13 +218,13 @@
err = input_register_device(input);
if (err)
- goto err1;
+ goto err2;
err = request_irq(priv->irq, eeti_ts_isr, irq_flags,
client->name, priv);
if (err) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
- goto err2;
+ goto err3;
}
/*
@@ -233,9 +236,11 @@
device_init_wakeup(&client->dev, 0);
return 0;
-err2:
+err3:
input_unregister_device(input);
input = NULL; /* so we dont try to free it below */
+err2:
+ gpio_free(pdata->irq_gpio);
err1:
input_free_device(input);
kfree(priv);
diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index 6d1cbdf..b64502d 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -296,8 +296,13 @@
} else
dma_pdev = pci_dev_get(pdev);
+ /* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
+ /*
+ * If it's a multifunction device that does not support our
+ * required ACS flags, add to the same group as function 0.
+ */
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
swap_pci_ref(&dma_pdev,
@@ -305,14 +310,28 @@
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
0)));
+ /*
+ * Devices on the root bus go through the iommu. If that's not us,
+ * find the next upstream device and test ACS up to the root bus.
+ * Finding the next device may require skipping virtual buses.
+ */
while (!pci_is_root_bus(dma_pdev->bus)) {
- if (pci_acs_path_enabled(dma_pdev->bus->self,
- NULL, REQ_ACS_FLAGS))
+ struct pci_bus *bus = dma_pdev->bus;
+
+ while (!bus->self) {
+ if (!pci_is_root_bus(bus))
+ bus = bus->parent;
+ else
+ goto root_bus;
+ }
+
+ if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
- swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self));
+ swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
+root_bus:
group = iommu_group_get(&dma_pdev->dev);
pci_dev_put(dma_pdev);
if (!group) {
diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 500e7f1..18a89b7 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1111,7 +1111,7 @@
if (iommu->cap & (1 << IOMMU_CAP_EFR)) {
pr_info("AMD-Vi: Extended features: ");
- for (i = 0; ARRAY_SIZE(feat_str); ++i) {
+ for (i = 0; i < ARRAY_SIZE(feat_str); ++i) {
if (iommu_feature(iommu, (1ULL << i)))
pr_cont(" %s", feat_str[i]);
}
@@ -1131,9 +1131,6 @@
break;
}
- /* Make sure ACS will be enabled */
- pci_request_acs();
-
ret = amd_iommu_init_devices();
print_iommu_info();
@@ -1652,6 +1649,9 @@
early_acpi_os_unmap_memory((char __iomem *)ivrs_base, ivrs_size);
+ /* Make sure ACS will be enabled during PCI probe */
+ pci_request_acs();
+
return true;
}
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 45350ff..80bad32 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -732,9 +732,9 @@
spin_lock_init(&priv->pgtablelock);
INIT_LIST_HEAD(&priv->clients);
- dom->geometry.aperture_start = 0;
- dom->geometry.aperture_end = ~0UL;
- dom->geometry.force_aperture = true;
+ domain->geometry.aperture_start = 0;
+ domain->geometry.aperture_end = ~0UL;
+ domain->geometry.force_aperture = true;
domain->priv = priv;
return 0;
diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
index 7469b53..2297ec1 100644
--- a/drivers/iommu/intel-iommu.c
+++ b/drivers/iommu/intel-iommu.c
@@ -2008,6 +2008,7 @@
if (!drhd) {
printk(KERN_ERR "IOMMU: can't find DMAR for device %s\n",
pci_name(pdev));
+ free_domain_mem(domain);
return NULL;
}
iommu = drhd->iommu;
@@ -4124,8 +4125,13 @@
} else
dma_pdev = pci_dev_get(pdev);
+ /* Account for quirked devices */
swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev));
+ /*
+ * If it's a multifunction device that does not support our
+ * required ACS flags, add to the same group as function 0.
+ */
if (dma_pdev->multifunction &&
!pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))
swap_pci_ref(&dma_pdev,
@@ -4133,14 +4139,28 @@
PCI_DEVFN(PCI_SLOT(dma_pdev->devfn),
0)));
+ /*
+ * Devices on the root bus go through the iommu. If that's not us,
+ * find the next upstream device and test ACS up to the root bus.
+ * Finding the next device may require skipping virtual buses.
+ */
while (!pci_is_root_bus(dma_pdev->bus)) {
- if (pci_acs_path_enabled(dma_pdev->bus->self,
- NULL, REQ_ACS_FLAGS))
+ struct pci_bus *bus = dma_pdev->bus;
+
+ while (!bus->self) {
+ if (!pci_is_root_bus(bus))
+ bus = bus->parent;
+ else
+ goto root_bus;
+ }
+
+ if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS))
break;
- swap_pci_ref(&dma_pdev, pci_dev_get(dma_pdev->bus->self));
+ swap_pci_ref(&dma_pdev, pci_dev_get(bus->self));
}
+root_bus:
group = iommu_group_get(&dma_pdev->dev);
pci_dev_put(dma_pdev);
if (!group) {
diff --git a/drivers/iommu/intel_irq_remapping.c b/drivers/iommu/intel_irq_remapping.c
index e0b18f3..af8904d 100644
--- a/drivers/iommu/intel_irq_remapping.c
+++ b/drivers/iommu/intel_irq_remapping.c
@@ -736,6 +736,7 @@
{
struct dmar_drhd_unit *drhd;
int ir_supported = 0;
+ int ioapic_idx;
for_each_drhd_unit(drhd) {
struct intel_iommu *iommu = drhd->iommu;
@@ -748,13 +749,20 @@
}
}
- if (ir_supported && ir_ioapic_num != nr_ioapics) {
- printk(KERN_WARNING
- "Not all IO-APIC's listed under remapping hardware\n");
- return -1;
+ if (!ir_supported)
+ return 0;
+
+ for (ioapic_idx = 0; ioapic_idx < nr_ioapics; ioapic_idx++) {
+ int ioapic_id = mpc_ioapic_id(ioapic_idx);
+ if (!map_ioapic_to_ir(ioapic_id)) {
+ pr_err(FW_BUG "ioapic %d has no mapping iommu, "
+ "interrupt remapping will be disabled\n",
+ ioapic_id);
+ return -1;
+ }
}
- return ir_supported;
+ return 1;
}
int __init ir_dev_scope_init(void)
diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
index 4ba325a..2a4bb36 100644
--- a/drivers/iommu/tegra-smmu.c
+++ b/drivers/iommu/tegra-smmu.c
@@ -799,14 +799,14 @@
goto out;
}
}
- dev_err(smmu->dev, "Couldn't find %s\n", dev_name(c->dev));
+ dev_err(smmu->dev, "Couldn't find %s\n", dev_name(dev));
out:
spin_unlock(&as->client_lock);
}
static int smmu_iommu_domain_init(struct iommu_domain *domain)
{
- int i, err = -ENODEV;
+ int i, err = -EAGAIN;
unsigned long flags;
struct smmu_as *as;
struct smmu_device *smmu = smmu_handle;
@@ -814,11 +814,14 @@
/* Look for a free AS with lock held */
for (i = 0; i < smmu->num_as; i++) {
as = &smmu->as[i];
- if (!as->pdir_page) {
- err = alloc_pdir(as);
- if (!err)
- goto found;
- }
+
+ if (as->pdir_page)
+ continue;
+
+ err = alloc_pdir(as);
+ if (!err)
+ goto found;
+
if (err != -EAGAIN)
break;
}
diff --git a/drivers/isdn/isdnloop/isdnloop.c b/drivers/isdn/isdnloop/isdnloop.c
index 5405ec6..baf2686 100644
--- a/drivers/isdn/isdnloop/isdnloop.c
+++ b/drivers/isdn/isdnloop/isdnloop.c
@@ -16,7 +16,6 @@
#include <linux/sched.h>
#include "isdnloop.h"
-static char *revision = "$Revision: 1.11.6.7 $";
static char *isdnloop_id = "loop0";
MODULE_DESCRIPTION("ISDN4Linux: Pseudo Driver that simulates an ISDN card");
@@ -1494,17 +1493,6 @@
static int __init
isdnloop_init(void)
{
- char *p;
- char rev[10];
-
- if ((p = strchr(revision, ':'))) {
- strcpy(rev, p + 1);
- p = strchr(rev, '$');
- *p = 0;
- } else
- strcpy(rev, " ??? ");
- printk(KERN_NOTICE "isdnloop-ISDN-driver Rev%s\n", rev);
-
if (isdnloop_id)
return (isdnloop_addcard(isdnloop_id));
diff --git a/drivers/isdn/mISDN/layer2.c b/drivers/isdn/mISDN/layer2.c
index 0dc8abc..949cabb 100644
--- a/drivers/isdn/mISDN/layer2.c
+++ b/drivers/isdn/mISDN/layer2.c
@@ -2222,7 +2222,7 @@
InitWin(l2);
l2->l2m.fsm = &l2fsm;
if (test_bit(FLG_LAPB, &l2->flag) ||
- test_bit(FLG_PTP, &l2->flag) ||
+ test_bit(FLG_FIXED_TEI, &l2->flag) ||
test_bit(FLG_LAPD_NET, &l2->flag))
l2->l2m.state = ST_L2_4;
else
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 6157cbb..363975b 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -224,7 +224,7 @@
struct led_classdev *led_cdev;
led_cdev = list_entry(entry, struct led_classdev, trig_list);
- led_set_brightness(led_cdev, brightness);
+ __led_set_brightness(led_cdev, brightness);
}
read_unlock(&trig->leddev_list_lock);
}
diff --git a/drivers/leds/leds-lp8788.c b/drivers/leds/leds-lp8788.c
index 53bd136..0ade6eb 100644
--- a/drivers/leds/leds-lp8788.c
+++ b/drivers/leds/leds-lp8788.c
@@ -63,7 +63,7 @@
/* scale configuration */
addr = LP8788_ISINK_CTRL;
mask = 1 << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
- val = cfg->scale << cfg->num;
+ val = cfg->scale << (cfg->num + LP8788_ISINK_SCALE_OFFSET);
ret = lp8788_update_bits(led->lp, addr, mask, val);
if (ret)
return ret;
diff --git a/drivers/leds/leds-renesas-tpu.c b/drivers/leds/leds-renesas-tpu.c
index 9ee12c2..771ea06 100644
--- a/drivers/leds/leds-renesas-tpu.c
+++ b/drivers/leds/leds-renesas-tpu.c
@@ -247,7 +247,7 @@
if (!cfg) {
dev_err(&pdev->dev, "missing platform data\n");
- goto err0;
+ return -ENODEV;
}
p = devm_kzalloc(&pdev->dev, sizeof(*p), GFP_KERNEL);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index fcd0987..3f6203a 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -1108,8 +1108,11 @@
ret = 0;
}
rdev->sectors = rdev->sb_start;
- /* Limit to 4TB as metadata cannot record more than that */
- if (rdev->sectors >= (2ULL << 32))
+ /* Limit to 4TB as metadata cannot record more than that.
+ * (not needed for Linear and RAID0 as metadata doesn't
+ * record this size)
+ */
+ if (rdev->sectors >= (2ULL << 32) && sb->level >= 1)
rdev->sectors = (2ULL << 32) - 2;
if (rdev->sectors < ((sector_t)sb->size) * 2 && sb->level >= 1)
@@ -1400,7 +1403,7 @@
/* Limit to 4TB as metadata cannot record more than that.
* 4TB == 2^32 KB, or 2*2^32 sectors.
*/
- if (num_sectors >= (2ULL << 32))
+ if (num_sectors >= (2ULL << 32) && rdev->mddev->level >= 1)
num_sectors = (2ULL << 32) - 2;
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
rdev->sb_page);
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index de5ed6f..1c2eb38 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -659,7 +659,11 @@
max = biovec->bv_len;
if (mddev->merge_check_needed) {
- struct r10bio r10_bio;
+ struct {
+ struct r10bio r10_bio;
+ struct r10dev devs[conf->copies];
+ } on_stack;
+ struct r10bio *r10_bio = &on_stack.r10_bio;
int s;
if (conf->reshape_progress != MaxSector) {
/* Cannot give any guidance during reshape */
@@ -667,18 +671,18 @@
return biovec->bv_len;
return 0;
}
- r10_bio.sector = sector;
- raid10_find_phys(conf, &r10_bio);
+ r10_bio->sector = sector;
+ raid10_find_phys(conf, r10_bio);
rcu_read_lock();
for (s = 0; s < conf->copies; s++) {
- int disk = r10_bio.devs[s].devnum;
+ int disk = r10_bio->devs[s].devnum;
struct md_rdev *rdev = rcu_dereference(
conf->mirrors[disk].rdev);
if (rdev && !test_bit(Faulty, &rdev->flags)) {
struct request_queue *q =
bdev_get_queue(rdev->bdev);
if (q->merge_bvec_fn) {
- bvm->bi_sector = r10_bio.devs[s].addr
+ bvm->bi_sector = r10_bio->devs[s].addr
+ rdev->data_offset;
bvm->bi_bdev = rdev->bdev;
max = min(max, q->merge_bvec_fn(
@@ -690,7 +694,7 @@
struct request_queue *q =
bdev_get_queue(rdev->bdev);
if (q->merge_bvec_fn) {
- bvm->bi_sector = r10_bio.devs[s].addr
+ bvm->bi_sector = r10_bio->devs[s].addr
+ rdev->data_offset;
bvm->bi_bdev = rdev->bdev;
max = min(max, q->merge_bvec_fn(
@@ -4414,14 +4418,18 @@
{
/* Use sync reads to get the blocks from somewhere else */
int sectors = r10_bio->sectors;
- struct r10bio r10b;
struct r10conf *conf = mddev->private;
+ struct {
+ struct r10bio r10_bio;
+ struct r10dev devs[conf->copies];
+ } on_stack;
+ struct r10bio *r10b = &on_stack.r10_bio;
int slot = 0;
int idx = 0;
struct bio_vec *bvec = r10_bio->master_bio->bi_io_vec;
- r10b.sector = r10_bio->sector;
- __raid10_find_phys(&conf->prev, &r10b);
+ r10b->sector = r10_bio->sector;
+ __raid10_find_phys(&conf->prev, r10b);
while (sectors) {
int s = sectors;
@@ -4432,7 +4440,7 @@
s = PAGE_SIZE >> 9;
while (!success) {
- int d = r10b.devs[slot].devnum;
+ int d = r10b->devs[slot].devnum;
struct md_rdev *rdev = conf->mirrors[d].rdev;
sector_t addr;
if (rdev == NULL ||
@@ -4440,7 +4448,7 @@
!test_bit(In_sync, &rdev->flags))
goto failed;
- addr = r10b.devs[slot].addr + idx * PAGE_SIZE;
+ addr = r10b->devs[slot].addr + idx * PAGE_SIZE;
success = sync_page_io(rdev,
addr,
s << 9,
diff --git a/drivers/md/raid10.h b/drivers/md/raid10.h
index 007c2c6..1054cf6 100644
--- a/drivers/md/raid10.h
+++ b/drivers/md/raid10.h
@@ -110,7 +110,7 @@
* We choose the number when they are allocated.
* We sometimes need an extra bio to write to the replacement.
*/
- struct {
+ struct r10dev {
struct bio *bio;
union {
struct bio *repl_bio; /* used for resync and
diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
index 664e460..aac6222 100644
--- a/drivers/media/dvb/siano/smsusb.c
+++ b/drivers/media/dvb/siano/smsusb.c
@@ -481,7 +481,7 @@
return 0;
}
-static const struct usb_device_id smsusb_id_table[] __devinitconst = {
+static const struct usb_device_id smsusb_id_table[] = {
{ USB_DEVICE(0x187f, 0x0010),
.driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
{ USB_DEVICE(0x187f, 0x0100),
diff --git a/drivers/media/radio/radio-shark.c b/drivers/media/radio/radio-shark.c
index d0b6bb5..72ded29 100644
--- a/drivers/media/radio/radio-shark.c
+++ b/drivers/media/radio/radio-shark.c
@@ -35,6 +35,11 @@
#include <media/v4l2-device.h>
#include <sound/tea575x-tuner.h>
+#if defined(CONFIG_LEDS_CLASS) || \
+ (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK_MODULE))
+#define SHARK_USE_LEDS 1
+#endif
+
/*
* Version Information
*/
@@ -56,44 +61,18 @@
enum { BLUE_LED, BLUE_PULSE_LED, RED_LED, NO_LEDS };
-static void shark_led_set_blue(struct led_classdev *led_cdev,
- enum led_brightness value);
-static void shark_led_set_blue_pulse(struct led_classdev *led_cdev,
- enum led_brightness value);
-static void shark_led_set_red(struct led_classdev *led_cdev,
- enum led_brightness value);
-
-static const struct led_classdev shark_led_templates[NO_LEDS] = {
- [BLUE_LED] = {
- .name = "%s:blue:",
- .brightness = LED_OFF,
- .max_brightness = 127,
- .brightness_set = shark_led_set_blue,
- },
- [BLUE_PULSE_LED] = {
- .name = "%s:blue-pulse:",
- .brightness = LED_OFF,
- .max_brightness = 255,
- .brightness_set = shark_led_set_blue_pulse,
- },
- [RED_LED] = {
- .name = "%s:red:",
- .brightness = LED_OFF,
- .max_brightness = 1,
- .brightness_set = shark_led_set_red,
- },
-};
-
struct shark_device {
struct usb_device *usbdev;
struct v4l2_device v4l2_dev;
struct snd_tea575x tea;
+#ifdef SHARK_USE_LEDS
struct work_struct led_work;
struct led_classdev leds[NO_LEDS];
char led_names[NO_LEDS][32];
atomic_t brightness[NO_LEDS];
unsigned long brightness_new;
+#endif
u8 *transfer_buffer;
u32 last_val;
@@ -175,20 +154,13 @@
.read_val = shark_read_val,
};
+#ifdef SHARK_USE_LEDS
static void shark_led_work(struct work_struct *work)
{
struct shark_device *shark =
container_of(work, struct shark_device, led_work);
int i, res, brightness, actual_len;
- /*
- * We use the v4l2_dev lock and registered bit to ensure the device
- * does not get unplugged and unreffed while we're running.
- */
- mutex_lock(&shark->tea.mutex);
- if (!video_is_registered(&shark->tea.vd))
- goto leave;
-
for (i = 0; i < 3; i++) {
if (!test_and_clear_bit(i, &shark->brightness_new))
continue;
@@ -208,8 +180,6 @@
v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
shark->led_names[i], res);
}
-leave:
- mutex_unlock(&shark->tea.mutex);
}
static void shark_led_set_blue(struct led_classdev *led_cdev,
@@ -245,19 +215,78 @@
schedule_work(&shark->led_work);
}
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+ [BLUE_LED] = {
+ .name = "%s:blue:",
+ .brightness = LED_OFF,
+ .max_brightness = 127,
+ .brightness_set = shark_led_set_blue,
+ },
+ [BLUE_PULSE_LED] = {
+ .name = "%s:blue-pulse:",
+ .brightness = LED_OFF,
+ .max_brightness = 255,
+ .brightness_set = shark_led_set_blue_pulse,
+ },
+ [RED_LED] = {
+ .name = "%s:red:",
+ .brightness = LED_OFF,
+ .max_brightness = 1,
+ .brightness_set = shark_led_set_red,
+ },
+};
+
+static int shark_register_leds(struct shark_device *shark, struct device *dev)
+{
+ int i, retval;
+
+ INIT_WORK(&shark->led_work, shark_led_work);
+ for (i = 0; i < NO_LEDS; i++) {
+ shark->leds[i] = shark_led_templates[i];
+ snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+ shark->leds[i].name, shark->v4l2_dev.name);
+ shark->leds[i].name = shark->led_names[i];
+ retval = led_classdev_register(dev, &shark->leds[i]);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev,
+ "couldn't register led: %s\n",
+ shark->led_names[i]);
+ return retval;
+ }
+ }
+ return 0;
+}
+
+static void shark_unregister_leds(struct shark_device *shark)
+{
+ int i;
+
+ for (i = 0; i < NO_LEDS; i++)
+ led_classdev_unregister(&shark->leds[i]);
+
+ cancel_work_sync(&shark->led_work);
+}
+#else
+static int shark_register_leds(struct shark_device *shark, struct device *dev)
+{
+ v4l2_warn(&shark->v4l2_dev,
+ "CONFIG_LED_CLASS not enabled, LED support disabled\n");
+ return 0;
+}
+static inline void shark_unregister_leds(struct shark_device *shark) { }
+#endif
+
static void usb_shark_disconnect(struct usb_interface *intf)
{
struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
- int i;
mutex_lock(&shark->tea.mutex);
v4l2_device_disconnect(&shark->v4l2_dev);
snd_tea575x_exit(&shark->tea);
mutex_unlock(&shark->tea.mutex);
- for (i = 0; i < NO_LEDS; i++)
- led_classdev_unregister(&shark->leds[i]);
+ shark_unregister_leds(shark);
v4l2_device_put(&shark->v4l2_dev);
}
@@ -266,7 +295,6 @@
{
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
- cancel_work_sync(&shark->led_work);
v4l2_device_unregister(&shark->v4l2_dev);
kfree(shark->transfer_buffer);
kfree(shark);
@@ -276,7 +304,7 @@
const struct usb_device_id *id)
{
struct shark_device *shark;
- int i, retval = -ENOMEM;
+ int retval = -ENOMEM;
shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
if (!shark)
@@ -286,17 +314,13 @@
if (!shark->transfer_buffer)
goto err_alloc_buffer;
- /*
- * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
- * pointer in intfdata causing v4l2-device.c to not set it. Which
- * results in usb_shark_disconnect() referencing the dangling pointer
- *
- * REMOVE (as soon as the above bug is fixed, patch submitted)
- */
- usb_set_intfdata(intf, NULL);
+ v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+
+ retval = shark_register_leds(shark, &intf->dev);
+ if (retval)
+ goto err_reg_leds;
shark->v4l2_dev.release = usb_shark_release;
- v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
if (retval) {
v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
@@ -320,32 +344,13 @@
goto err_init_tea;
}
- INIT_WORK(&shark->led_work, shark_led_work);
- for (i = 0; i < NO_LEDS; i++) {
- shark->leds[i] = shark_led_templates[i];
- snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
- shark->leds[i].name, shark->v4l2_dev.name);
- shark->leds[i].name = shark->led_names[i];
- /*
- * We don't fail the probe if we fail to register the leds,
- * because once we've called snd_tea575x_init, the /dev/radio0
- * node may be opened from userspace holding a reference to us!
- *
- * Note we cannot register the leds first instead as
- * shark_led_work depends on the v4l2 mutex and registered bit.
- */
- retval = led_classdev_register(&intf->dev, &shark->leds[i]);
- if (retval)
- v4l2_err(&shark->v4l2_dev,
- "couldn't register led: %s\n",
- shark->led_names[i]);
- }
-
return 0;
err_init_tea:
v4l2_device_unregister(&shark->v4l2_dev);
err_reg_dev:
+ shark_unregister_leds(shark);
+err_reg_leds:
kfree(shark->transfer_buffer);
err_alloc_buffer:
kfree(shark);
diff --git a/drivers/media/radio/radio-shark2.c b/drivers/media/radio/radio-shark2.c
index b9575de..7b4efdf 100644
--- a/drivers/media/radio/radio-shark2.c
+++ b/drivers/media/radio/radio-shark2.c
@@ -35,6 +35,11 @@
#include <media/v4l2-device.h>
#include "radio-tea5777.h"
+#if defined(CONFIG_LEDS_CLASS) || \
+ (defined(CONFIG_LEDS_CLASS_MODULE) && defined(CONFIG_RADIO_SHARK2_MODULE))
+#define SHARK_USE_LEDS 1
+#endif
+
MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
MODULE_DESCRIPTION("Griffin radioSHARK2, USB radio receiver driver");
MODULE_LICENSE("GPL");
@@ -43,7 +48,6 @@
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
-
#define SHARK_IN_EP 0x83
#define SHARK_OUT_EP 0x05
@@ -54,36 +58,18 @@
enum { BLUE_LED, RED_LED, NO_LEDS };
-static void shark_led_set_blue(struct led_classdev *led_cdev,
- enum led_brightness value);
-static void shark_led_set_red(struct led_classdev *led_cdev,
- enum led_brightness value);
-
-static const struct led_classdev shark_led_templates[NO_LEDS] = {
- [BLUE_LED] = {
- .name = "%s:blue:",
- .brightness = LED_OFF,
- .max_brightness = 127,
- .brightness_set = shark_led_set_blue,
- },
- [RED_LED] = {
- .name = "%s:red:",
- .brightness = LED_OFF,
- .max_brightness = 1,
- .brightness_set = shark_led_set_red,
- },
-};
-
struct shark_device {
struct usb_device *usbdev;
struct v4l2_device v4l2_dev;
struct radio_tea5777 tea;
+#ifdef SHARK_USE_LEDS
struct work_struct led_work;
struct led_classdev leds[NO_LEDS];
char led_names[NO_LEDS][32];
atomic_t brightness[NO_LEDS];
unsigned long brightness_new;
+#endif
u8 *transfer_buffer;
};
@@ -161,18 +147,12 @@
.read_reg = shark_read_reg,
};
+#ifdef SHARK_USE_LEDS
static void shark_led_work(struct work_struct *work)
{
struct shark_device *shark =
container_of(work, struct shark_device, led_work);
int i, res, brightness, actual_len;
- /*
- * We use the v4l2_dev lock and registered bit to ensure the device
- * does not get unplugged and unreffed while we're running.
- */
- mutex_lock(&shark->tea.mutex);
- if (!video_is_registered(&shark->tea.vd))
- goto leave;
for (i = 0; i < 2; i++) {
if (!test_and_clear_bit(i, &shark->brightness_new))
@@ -191,8 +171,6 @@
v4l2_err(&shark->v4l2_dev, "set LED %s error: %d\n",
shark->led_names[i], res);
}
-leave:
- mutex_unlock(&shark->tea.mutex);
}
static void shark_led_set_blue(struct led_classdev *led_cdev,
@@ -217,19 +195,72 @@
schedule_work(&shark->led_work);
}
+static const struct led_classdev shark_led_templates[NO_LEDS] = {
+ [BLUE_LED] = {
+ .name = "%s:blue:",
+ .brightness = LED_OFF,
+ .max_brightness = 127,
+ .brightness_set = shark_led_set_blue,
+ },
+ [RED_LED] = {
+ .name = "%s:red:",
+ .brightness = LED_OFF,
+ .max_brightness = 1,
+ .brightness_set = shark_led_set_red,
+ },
+};
+
+static int shark_register_leds(struct shark_device *shark, struct device *dev)
+{
+ int i, retval;
+
+ INIT_WORK(&shark->led_work, shark_led_work);
+ for (i = 0; i < NO_LEDS; i++) {
+ shark->leds[i] = shark_led_templates[i];
+ snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
+ shark->leds[i].name, shark->v4l2_dev.name);
+ shark->leds[i].name = shark->led_names[i];
+ retval = led_classdev_register(dev, &shark->leds[i]);
+ if (retval) {
+ v4l2_err(&shark->v4l2_dev,
+ "couldn't register led: %s\n",
+ shark->led_names[i]);
+ return retval;
+ }
+ }
+ return 0;
+}
+
+static void shark_unregister_leds(struct shark_device *shark)
+{
+ int i;
+
+ for (i = 0; i < NO_LEDS; i++)
+ led_classdev_unregister(&shark->leds[i]);
+
+ cancel_work_sync(&shark->led_work);
+}
+#else
+static int shark_register_leds(struct shark_device *shark, struct device *dev)
+{
+ v4l2_warn(&shark->v4l2_dev,
+ "CONFIG_LED_CLASS not enabled, LED support disabled\n");
+ return 0;
+}
+static inline void shark_unregister_leds(struct shark_device *shark) { }
+#endif
+
static void usb_shark_disconnect(struct usb_interface *intf)
{
struct v4l2_device *v4l2_dev = usb_get_intfdata(intf);
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
- int i;
mutex_lock(&shark->tea.mutex);
v4l2_device_disconnect(&shark->v4l2_dev);
radio_tea5777_exit(&shark->tea);
mutex_unlock(&shark->tea.mutex);
- for (i = 0; i < NO_LEDS; i++)
- led_classdev_unregister(&shark->leds[i]);
+ shark_unregister_leds(shark);
v4l2_device_put(&shark->v4l2_dev);
}
@@ -238,7 +269,6 @@
{
struct shark_device *shark = v4l2_dev_to_shark(v4l2_dev);
- cancel_work_sync(&shark->led_work);
v4l2_device_unregister(&shark->v4l2_dev);
kfree(shark->transfer_buffer);
kfree(shark);
@@ -248,7 +278,7 @@
const struct usb_device_id *id)
{
struct shark_device *shark;
- int i, retval = -ENOMEM;
+ int retval = -ENOMEM;
shark = kzalloc(sizeof(struct shark_device), GFP_KERNEL);
if (!shark)
@@ -258,17 +288,13 @@
if (!shark->transfer_buffer)
goto err_alloc_buffer;
- /*
- * Work around a bug in usbhid/hid-core.c, where it leaves a dangling
- * pointer in intfdata causing v4l2-device.c to not set it. Which
- * results in usb_shark_disconnect() referencing the dangling pointer
- *
- * REMOVE (as soon as the above bug is fixed, patch submitted)
- */
- usb_set_intfdata(intf, NULL);
+ v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
+
+ retval = shark_register_leds(shark, &intf->dev);
+ if (retval)
+ goto err_reg_leds;
shark->v4l2_dev.release = usb_shark_release;
- v4l2_device_set_name(&shark->v4l2_dev, DRV_NAME, &shark_instance);
retval = v4l2_device_register(&intf->dev, &shark->v4l2_dev);
if (retval) {
v4l2_err(&shark->v4l2_dev, "couldn't register v4l2_device\n");
@@ -292,32 +318,13 @@
goto err_init_tea;
}
- INIT_WORK(&shark->led_work, shark_led_work);
- for (i = 0; i < NO_LEDS; i++) {
- shark->leds[i] = shark_led_templates[i];
- snprintf(shark->led_names[i], sizeof(shark->led_names[0]),
- shark->leds[i].name, shark->v4l2_dev.name);
- shark->leds[i].name = shark->led_names[i];
- /*
- * We don't fail the probe if we fail to register the leds,
- * because once we've called radio_tea5777_init, the /dev/radio0
- * node may be opened from userspace holding a reference to us!
- *
- * Note we cannot register the leds first instead as
- * shark_led_work depends on the v4l2 mutex and registered bit.
- */
- retval = led_classdev_register(&intf->dev, &shark->leds[i]);
- if (retval)
- v4l2_err(&shark->v4l2_dev,
- "couldn't register led: %s\n",
- shark->led_names[i]);
- }
-
return 0;
err_init_tea:
v4l2_device_unregister(&shark->v4l2_dev);
err_reg_dev:
+ shark_unregister_leds(shark);
+err_reg_leds:
kfree(shark->transfer_buffer);
err_alloc_buffer:
kfree(shark);
diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c
index 9e38132..9bb65e1 100644
--- a/drivers/media/radio/si470x/radio-si470x-common.c
+++ b/drivers/media/radio/si470x/radio-si470x-common.c
@@ -151,6 +151,7 @@
.index = 0,
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_FREQ_BANDS |
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
V4L2_TUNER_CAP_HWSEEK_WRAP,
.rangelow = 87500 * 16,
@@ -162,6 +163,7 @@
.index = 1,
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_FREQ_BANDS |
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
V4L2_TUNER_CAP_HWSEEK_WRAP,
.rangelow = 76000 * 16,
@@ -173,6 +175,7 @@
.index = 2,
.capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO |
V4L2_TUNER_CAP_RDS | V4L2_TUNER_CAP_RDS_BLOCK_IO |
+ V4L2_TUNER_CAP_FREQ_BANDS |
V4L2_TUNER_CAP_HWSEEK_BOUNDED |
V4L2_TUNER_CAP_HWSEEK_WRAP,
.rangelow = 76000 * 16,
diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c
index 643a6ff..f867f04 100644
--- a/drivers/media/radio/si470x/radio-si470x-i2c.c
+++ b/drivers/media/radio/si470x/radio-si470x-i2c.c
@@ -225,8 +225,9 @@
{
strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
- capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
- V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
+ capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c
index 146be42..be076f7 100644
--- a/drivers/media/radio/si470x/radio-si470x-usb.c
+++ b/drivers/media/radio/si470x/radio-si470x-usb.c
@@ -531,7 +531,7 @@
strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
usb_make_path(radio->usbdev, capability->bus_info,
sizeof(capability->bus_info));
- capability->device_caps = V4L2_CAP_HW_FREQ_SEEK |
+ capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_READWRITE |
V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE;
capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig
index 5180390..8be5763 100644
--- a/drivers/media/rc/Kconfig
+++ b/drivers/media/rc/Kconfig
@@ -261,6 +261,7 @@
config IR_IGUANA
tristate "IguanaWorks USB IR Transceiver"
+ depends on USB_ARCH_HAS_HCD
depends on RC_CORE
select USB
---help---
diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c
index cf9d9fc..2347771 100644
--- a/drivers/media/video/gspca/jl2005bcd.c
+++ b/drivers/media/video/gspca/jl2005bcd.c
@@ -512,7 +512,7 @@
};
/* -- module initialisation -- */
-static const __devinitdata struct usb_device_id device_table[] = {
+static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x0979, 0x0227)},
{}
};
diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
index 969bb5a..bab01c86 100644
--- a/drivers/media/video/gspca/spca506.c
+++ b/drivers/media/video/gspca/spca506.c
@@ -579,7 +579,7 @@
};
/* -- module initialisation -- */
-static const struct usb_device_id device_table[] __devinitconst = {
+static const struct usb_device_id device_table[] = {
{USB_DEVICE(0x06e1, 0xa190)},
/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
{USB_DEVICE(0x0733, 0x0430)}, */
diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c
index 7efe9ad..0b91a5c 100644
--- a/drivers/media/video/mem2mem_testdev.c
+++ b/drivers/media/video/mem2mem_testdev.c
@@ -431,7 +431,7 @@
strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
- cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
+ cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
return 0;
}
diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c
index d2e6f82..560a65a 100644
--- a/drivers/media/video/mx1_camera.c
+++ b/drivers/media/video/mx1_camera.c
@@ -403,7 +403,7 @@
dev_dbg(pcdev->icd->parent, "Activate device\n");
- clk_enable(pcdev->clk);
+ clk_prepare_enable(pcdev->clk);
/* enable CSI before doing anything else */
__raw_writel(csicr1, pcdev->base + CSICR1);
@@ -422,7 +422,7 @@
/* Disable all CSI interface */
__raw_writel(0x00, pcdev->base + CSICR1);
- clk_disable(pcdev->clk);
+ clk_disable_unprepare(pcdev->clk);
}
/*
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c
index 637bde8..ac17540 100644
--- a/drivers/media/video/mx2_camera.c
+++ b/drivers/media/video/mx2_camera.c
@@ -272,7 +272,7 @@
struct device *dev;
struct soc_camera_host soc_host;
struct soc_camera_device *icd;
- struct clk *clk_csi, *clk_emma;
+ struct clk *clk_csi, *clk_emma_ahb, *clk_emma_ipg;
unsigned int irq_csi, irq_emma;
void __iomem *base_csi, *base_emma;
@@ -407,7 +407,7 @@
{
unsigned long flags;
- clk_disable(pcdev->clk_csi);
+ clk_disable_unprepare(pcdev->clk_csi);
writel(0, pcdev->base_csi + CSICR1);
if (cpu_is_mx27()) {
writel(0, pcdev->base_emma + PRP_CNTL);
@@ -435,7 +435,7 @@
if (pcdev->icd)
return -EBUSY;
- ret = clk_enable(pcdev->clk_csi);
+ ret = clk_prepare_enable(pcdev->clk_csi);
if (ret < 0)
return ret;
@@ -1633,23 +1633,34 @@
goto exit_iounmap;
}
- pcdev->clk_emma = clk_get(NULL, "emma");
- if (IS_ERR(pcdev->clk_emma)) {
- err = PTR_ERR(pcdev->clk_emma);
+ pcdev->clk_emma_ipg = clk_get(pcdev->dev, "emma-ipg");
+ if (IS_ERR(pcdev->clk_emma_ipg)) {
+ err = PTR_ERR(pcdev->clk_emma_ipg);
goto exit_free_irq;
}
- clk_enable(pcdev->clk_emma);
+ clk_prepare_enable(pcdev->clk_emma_ipg);
+
+ pcdev->clk_emma_ahb = clk_get(pcdev->dev, "emma-ahb");
+ if (IS_ERR(pcdev->clk_emma_ahb)) {
+ err = PTR_ERR(pcdev->clk_emma_ahb);
+ goto exit_clk_emma_ipg_put;
+ }
+
+ clk_prepare_enable(pcdev->clk_emma_ahb);
err = mx27_camera_emma_prp_reset(pcdev);
if (err)
- goto exit_clk_emma_put;
+ goto exit_clk_emma_ahb_put;
return err;
-exit_clk_emma_put:
- clk_disable(pcdev->clk_emma);
- clk_put(pcdev->clk_emma);
+exit_clk_emma_ahb_put:
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ clk_put(pcdev->clk_emma_ahb);
+exit_clk_emma_ipg_put:
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_put(pcdev->clk_emma_ipg);
exit_free_irq:
free_irq(pcdev->irq_emma, pcdev);
exit_iounmap:
@@ -1685,7 +1696,7 @@
goto exit;
}
- pcdev->clk_csi = clk_get(&pdev->dev, NULL);
+ pcdev->clk_csi = clk_get(&pdev->dev, "ahb");
if (IS_ERR(pcdev->clk_csi)) {
dev_err(&pdev->dev, "Could not get csi clock\n");
err = PTR_ERR(pcdev->clk_csi);
@@ -1785,8 +1796,10 @@
eallocctx:
if (cpu_is_mx27()) {
free_irq(pcdev->irq_emma, pcdev);
- clk_disable(pcdev->clk_emma);
- clk_put(pcdev->clk_emma);
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_put(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ clk_put(pcdev->clk_emma_ahb);
iounmap(pcdev->base_emma);
release_mem_region(pcdev->res_emma->start, resource_size(pcdev->res_emma));
}
@@ -1825,8 +1838,10 @@
iounmap(pcdev->base_csi);
if (cpu_is_mx27()) {
- clk_disable(pcdev->clk_emma);
- clk_put(pcdev->clk_emma);
+ clk_disable_unprepare(pcdev->clk_emma_ipg);
+ clk_put(pcdev->clk_emma_ipg);
+ clk_disable_unprepare(pcdev->clk_emma_ahb);
+ clk_put(pcdev->clk_emma_ahb);
iounmap(pcdev->base_emma);
res = pcdev->res_emma;
release_mem_region(res->start, resource_size(res));
diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
index f13643d..af2297d 100644
--- a/drivers/media/video/mx3_camera.c
+++ b/drivers/media/video/mx3_camera.c
@@ -61,15 +61,9 @@
#define MAX_VIDEO_MEM 16
-enum csi_buffer_state {
- CSI_BUF_NEEDS_INIT,
- CSI_BUF_PREPARED,
-};
-
struct mx3_camera_buffer {
/* common v4l buffer stuff -- must be first */
struct vb2_buffer vb;
- enum csi_buffer_state state;
struct list_head queue;
/* One descriptot per scatterlist (per frame) */
@@ -285,7 +279,7 @@
goto error;
}
- if (buf->state == CSI_BUF_NEEDS_INIT) {
+ if (!buf->txd) {
sg_dma_address(sg) = vb2_dma_contig_plane_dma_addr(vb, 0);
sg_dma_len(sg) = new_size;
@@ -298,7 +292,6 @@
txd->callback_param = txd;
txd->callback = mx3_cam_dma_done;
- buf->state = CSI_BUF_PREPARED;
buf->txd = txd;
} else {
txd = buf->txd;
@@ -385,7 +378,6 @@
/* Doesn't hurt also if the list is empty */
list_del_init(&buf->queue);
- buf->state = CSI_BUF_NEEDS_INIT;
if (txd) {
buf->txd = NULL;
@@ -405,13 +397,13 @@
struct mx3_camera_dev *mx3_cam = ici->priv;
struct mx3_camera_buffer *buf = to_mx3_vb(vb);
- /* This is for locking debugging only */
- INIT_LIST_HEAD(&buf->queue);
- sg_init_table(&buf->sg, 1);
+ if (!buf->txd) {
+ /* This is for locking debugging only */
+ INIT_LIST_HEAD(&buf->queue);
+ sg_init_table(&buf->sg, 1);
- buf->state = CSI_BUF_NEEDS_INIT;
-
- mx3_cam->buf_total += vb2_plane_size(vb, 0);
+ mx3_cam->buf_total += vb2_plane_size(vb, 0);
+ }
return 0;
}
diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
index b03ffec..1bde255 100644
--- a/drivers/media/video/soc_camera.c
+++ b/drivers/media/video/soc_camera.c
@@ -171,7 +171,8 @@
dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n",
pixfmtstr(pix->pixelformat), pix->width, pix->height);
- if (!(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) {
+ if (pix->pixelformat != V4L2_PIX_FMT_JPEG &&
+ !(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) {
pix->bytesperline = 0;
pix->sizeimage = 0;
}
diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c
index 89dce09..a397812 100644
--- a/drivers/media/video/soc_mediabus.c
+++ b/drivers/media/video/soc_mediabus.c
@@ -378,6 +378,9 @@
s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf)
{
+ if (mf->fourcc == V4L2_PIX_FMT_JPEG)
+ return 0;
+
if (mf->layout != SOC_MBUS_LAYOUT_PACKED)
return width * mf->bits_per_sample / 8;
@@ -400,6 +403,9 @@
s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf,
u32 bytes_per_line, u32 height)
{
+ if (mf->fourcc == V4L2_PIX_FMT_JPEG)
+ return 0;
+
if (mf->layout == SOC_MBUS_LAYOUT_PACKED)
return bytes_per_line * height;
diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c
index 9288fbd..5577381 100644
--- a/drivers/media/video/uvc/uvc_queue.c
+++ b/drivers/media/video/uvc/uvc_queue.c
@@ -338,6 +338,7 @@
if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
buf->error = 0;
buf->state = UVC_BUF_STATE_QUEUED;
+ buf->bytesused = 0;
vb2_set_plane_payload(&buf->buf, 0, 0);
return buf;
}
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index c3b7b5f..6bc47fc 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -402,8 +402,10 @@
{
const struct v4l2_hw_freq_seek *p = arg;
- pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
- p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
+ pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u, "
+ "rangelow=%u, rangehigh=%u\n",
+ p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing,
+ p->rangelow, p->rangehigh);
}
static void v4l_print_requestbuffers(const void *arg, bool write_only)
@@ -1853,6 +1855,8 @@
.type = type,
};
+ if (p->index)
+ return -EINVAL;
err = ops->vidioc_g_tuner(file, fh, &t);
if (err)
return err;
@@ -1870,6 +1874,8 @@
if (type != V4L2_TUNER_RADIO)
return -EINVAL;
+ if (p->index)
+ return -EINVAL;
err = ops->vidioc_g_modulator(file, fh, &m);
if (err)
return err;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d1facef..b1a1462 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -395,7 +395,8 @@
config MFD_TC6393XB
bool "Support Toshiba TC6393XB"
- depends on GPIOLIB && ARM && HAVE_CLK
+ depends on ARM && HAVE_CLK
+ select GPIOLIB
select MFD_CORE
select MFD_TMIO
help
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 383421b..683e18a 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -925,6 +925,7 @@
goto out;
}
+ ret = 0;
if (pdata->leds) {
int i;
diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c
index 43a76c4..db662e2 100644
--- a/drivers/mfd/ezx-pcap.c
+++ b/drivers/mfd/ezx-pcap.c
@@ -202,7 +202,7 @@
}
local_irq_enable();
ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr);
- } while (gpio_get_value(irq_to_gpio(pcap->spi->irq)));
+ } while (gpio_get_value(pdata->gpio));
}
static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc)
diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c
index c6ffbbe..d78c05e 100644
--- a/drivers/misc/mei/interrupt.c
+++ b/drivers/misc/mei/interrupt.c
@@ -1253,7 +1253,7 @@
if (dev->wd_timeout)
*slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
else
- *slots -= mei_data2slots(MEI_START_WD_DATA_SIZE);
+ *slots -= mei_data2slots(MEI_WD_PARAMS_SIZE);
}
}
if (dev->stop)
diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c
index 0923302..7422c76 100644
--- a/drivers/misc/mei/main.c
+++ b/drivers/misc/mei/main.c
@@ -925,6 +925,27 @@
};
/**
+ * mei_quirk_probe - probe for devices that doesn't valid ME interface
+ * @pdev: PCI device structure
+ * @ent: entry into pci_device_table
+ *
+ * returns true if ME Interface is valid, false otherwise
+ */
+static bool __devinit mei_quirk_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ u32 reg;
+ if (ent->device == MEI_DEV_ID_PBG_1) {
+ pci_read_config_dword(pdev, 0x48, ®);
+ /* make sure that bit 9 is up and bit 10 is down */
+ if ((reg & 0x600) == 0x200) {
+ dev_info(&pdev->dev, "Device doesn't have valid ME Interface\n");
+ return false;
+ }
+ }
+ return true;
+}
+/**
* mei_probe - Device Initialization Routine
*
* @pdev: PCI device structure
@@ -939,6 +960,12 @@
int err;
mutex_lock(&mei_mutex);
+
+ if (!mei_quirk_probe(pdev, ent)) {
+ err = -ENODEV;
+ goto end;
+ }
+
if (mei_device) {
err = -EEXIST;
goto end;
diff --git a/drivers/misc/sgi-xp/xpc_uv.c b/drivers/misc/sgi-xp/xpc_uv.c
index 87b251a..b9e2000 100644
--- a/drivers/misc/sgi-xp/xpc_uv.c
+++ b/drivers/misc/sgi-xp/xpc_uv.c
@@ -18,6 +18,8 @@
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/device.h>
+#include <linux/cpu.h>
+#include <linux/module.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <asm/uv/uv_hub.h>
@@ -59,6 +61,8 @@
XPC_NOTIFY_MSG_SIZE_UV)
#define XPC_NOTIFY_IRQ_NAME "xpc_notify"
+static int xpc_mq_node = -1;
+
static struct xpc_gru_mq_uv *xpc_activate_mq_uv;
static struct xpc_gru_mq_uv *xpc_notify_mq_uv;
@@ -109,11 +113,8 @@
#if defined CONFIG_X86_64
mq->irq = uv_setup_irq(irq_name, cpu, mq->mmr_blade, mq->mmr_offset,
UV_AFFINITY_CPU);
- if (mq->irq < 0) {
- dev_err(xpc_part, "uv_setup_irq() returned error=%d\n",
- -mq->irq);
+ if (mq->irq < 0)
return mq->irq;
- }
mq->mmr_value = uv_read_global_mmr64(mmr_pnode, mq->mmr_offset);
@@ -238,8 +239,9 @@
mq->mmr_blade = uv_cpu_to_blade_id(cpu);
nid = cpu_to_node(cpu);
- page = alloc_pages_exact_node(nid, GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
- pg_order);
+ page = alloc_pages_exact_node(nid,
+ GFP_KERNEL | __GFP_ZERO | GFP_THISNODE,
+ pg_order);
if (page == NULL) {
dev_err(xpc_part, "xpc_create_gru_mq_uv() failed to alloc %d "
"bytes of memory on nid=%d for GRU mq\n", mq_size, nid);
@@ -1731,9 +1733,50 @@
.notify_senders_of_disconnect = xpc_notify_senders_of_disconnect_uv,
};
+static int
+xpc_init_mq_node(int nid)
+{
+ int cpu;
+
+ get_online_cpus();
+
+ for_each_cpu(cpu, cpumask_of_node(nid)) {
+ xpc_activate_mq_uv =
+ xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, nid,
+ XPC_ACTIVATE_IRQ_NAME,
+ xpc_handle_activate_IRQ_uv);
+ if (!IS_ERR(xpc_activate_mq_uv))
+ break;
+ }
+ if (IS_ERR(xpc_activate_mq_uv)) {
+ put_online_cpus();
+ return PTR_ERR(xpc_activate_mq_uv);
+ }
+
+ for_each_cpu(cpu, cpumask_of_node(nid)) {
+ xpc_notify_mq_uv =
+ xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, nid,
+ XPC_NOTIFY_IRQ_NAME,
+ xpc_handle_notify_IRQ_uv);
+ if (!IS_ERR(xpc_notify_mq_uv))
+ break;
+ }
+ if (IS_ERR(xpc_notify_mq_uv)) {
+ xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
+ put_online_cpus();
+ return PTR_ERR(xpc_notify_mq_uv);
+ }
+
+ put_online_cpus();
+ return 0;
+}
+
int
xpc_init_uv(void)
{
+ int nid;
+ int ret = 0;
+
xpc_arch_ops = xpc_arch_ops_uv;
if (sizeof(struct xpc_notify_mq_msghdr_uv) > XPC_MSG_HDR_MAX_SIZE) {
@@ -1742,21 +1785,21 @@
return -E2BIG;
}
- xpc_activate_mq_uv = xpc_create_gru_mq_uv(XPC_ACTIVATE_MQ_SIZE_UV, 0,
- XPC_ACTIVATE_IRQ_NAME,
- xpc_handle_activate_IRQ_uv);
- if (IS_ERR(xpc_activate_mq_uv))
- return PTR_ERR(xpc_activate_mq_uv);
+ if (xpc_mq_node < 0)
+ for_each_online_node(nid) {
+ ret = xpc_init_mq_node(nid);
- xpc_notify_mq_uv = xpc_create_gru_mq_uv(XPC_NOTIFY_MQ_SIZE_UV, 0,
- XPC_NOTIFY_IRQ_NAME,
- xpc_handle_notify_IRQ_uv);
- if (IS_ERR(xpc_notify_mq_uv)) {
- xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
- return PTR_ERR(xpc_notify_mq_uv);
- }
+ if (!ret)
+ break;
+ }
+ else
+ ret = xpc_init_mq_node(xpc_mq_node);
- return 0;
+ if (ret < 0)
+ dev_err(xpc_part, "xpc_init_mq_node() returned error=%d\n",
+ -ret);
+
+ return ret;
}
void
@@ -1765,3 +1808,6 @@
xpc_destroy_gru_mq_uv(xpc_notify_mq_uv);
xpc_destroy_gru_mq_uv(xpc_activate_mq_uv);
}
+
+module_param(xpc_mq_node, int, 0);
+MODULE_PARM_DESC(xpc_mq_node, "Node number on which to allocate message queues.");
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 1ff460a..93b4d67 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -87,7 +87,7 @@
/* communicate to platform about chip wakeup */
kim_data = st_data->kim_data;
pdata = kim_data->kim_pdev->dev.platform_data;
- if (pdata->chip_asleep)
+ if (pdata->chip_awake)
pdata->chip_awake(NULL);
}
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index cfff454..c3bb304 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -19,14 +19,13 @@
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
+#include <asm/sections.h>
/****************************************************************************/
-extern char _ebss;
-
struct map_info uclinux_ram_map = {
.name = "RAM",
- .phys = (unsigned long)&_ebss,
+ .phys = (unsigned long)__bss_stop,
.size = 0,
};
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 31bb7e5..8ca4176 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -480,7 +480,7 @@
config MTD_NAND_GPMI_NAND
bool "GPMI NAND Flash Controller driver"
- depends on MTD_NAND && (SOC_IMX23 || SOC_IMX28 || SOC_IMX6Q)
+ depends on MTD_NAND && MXS_DMA
help
Enables NAND Flash support for IMX23 or IMX28.
The GPMI controller is very powerful, with the help of BCH
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
index e9309b3..ac4fd75 100644
--- a/drivers/mtd/nand/omap2.c
+++ b/drivers/mtd/nand/omap2.c
@@ -1245,7 +1245,6 @@
goto out_release_mem_region;
} else {
struct dma_slave_config cfg;
- int rc;
memset(&cfg, 0, sizeof(cfg));
cfg.src_addr = info->phys_base;
@@ -1254,10 +1253,10 @@
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
cfg.src_maxburst = 16;
cfg.dst_maxburst = 16;
- rc = dmaengine_slave_config(info->dma, &cfg);
- if (rc) {
+ err = dmaengine_slave_config(info->dma, &cfg);
+ if (err) {
dev_err(&pdev->dev, "DMA engine slave config failed: %d\n",
- rc);
+ err);
goto out_release_mem_region;
}
info->nand.read_buf = omap_read_buf_dma_pref;
diff --git a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c
index 545c09e..cff6f02 100644
--- a/drivers/net/appletalk/cops.c
+++ b/drivers/net/appletalk/cops.c
@@ -996,9 +996,7 @@
printk(KERN_WARNING "%s: You shouldn't autoprobe with insmod\n",
cardname);
cops_dev = cops_probe(-1);
- if (IS_ERR(cops_dev))
- return PTR_ERR(cops_dev);
- return 0;
+ return PTR_RET(cops_dev);
}
static void __exit cops_module_exit(void)
diff --git a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c
index 0910dce..b5782cd 100644
--- a/drivers/net/appletalk/ltpc.c
+++ b/drivers/net/appletalk/ltpc.c
@@ -1243,9 +1243,7 @@
"ltpc: Autoprobing is not recommended for modules\n");
dev_ltpc = ltpc_probe();
- if (IS_ERR(dev_ltpc))
- return PTR_ERR(dev_ltpc);
- return 0;
+ return PTR_RET(dev_ltpc);
}
module_init(ltpc_module_init);
#endif
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 6fae5f3..d688a8a 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -398,7 +398,7 @@
sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
skb->queue_mapping = qdisc_skb_cb(skb)->slave_dev_queue_mapping;
- if (unlikely(netpoll_tx_running(slave_dev)))
+ if (unlikely(netpoll_tx_running(bond->dev)))
bond_netpoll_send_skb(bond_get_slave_by_dev(bond, slave_dev), skb);
else
dev_queue_xmit(skb);
@@ -1235,12 +1235,12 @@
struct netpoll *np;
int err = 0;
- np = kzalloc(sizeof(*np), GFP_KERNEL);
+ np = kzalloc(sizeof(*np), GFP_ATOMIC);
err = -ENOMEM;
if (!np)
goto out;
- err = __netpoll_setup(np, slave->dev);
+ err = __netpoll_setup(np, slave->dev, GFP_ATOMIC);
if (err) {
kfree(np);
goto out;
@@ -1257,9 +1257,7 @@
return;
slave->np = NULL;
- synchronize_rcu_bh();
- __netpoll_cleanup(np);
- kfree(np);
+ __netpoll_free_rcu(np);
}
static inline bool slave_dev_support_netpoll(struct net_device *slave_dev)
{
@@ -1292,7 +1290,7 @@
read_unlock(&bond->lock);
}
-static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
+static int bond_netpoll_setup(struct net_device *dev, struct netpoll_info *ni, gfp_t gfp)
{
struct bonding *bond = netdev_priv(dev);
struct slave *slave;
diff --git a/drivers/net/cris/eth_v10.c b/drivers/net/cris/eth_v10.c
index f0c8bd5..021d69c 100644
--- a/drivers/net/cris/eth_v10.c
+++ b/drivers/net/cris/eth_v10.c
@@ -1712,7 +1712,7 @@
static void
e100_netpoll(struct net_device* netdev)
{
- e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev, NULL);
+ e100rxtx_interrupt(NETWORK_DMA_TX_IRQ_NBR, netdev);
}
#endif
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
index 77bcd4c..463b9ec 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
@@ -1278,7 +1278,7 @@
#define BNX2X_FW_RX_ALIGN_START (1UL << BNX2X_RX_ALIGN_SHIFT)
#define BNX2X_FW_RX_ALIGN_END \
- max(1UL << BNX2X_RX_ALIGN_SHIFT, \
+ max_t(u64, 1UL << BNX2X_RX_ALIGN_SHIFT, \
SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))
#define BNX2X_PXP_DRAM_ALIGN (BNX2X_RX_ALIGN_SHIFT - 5)
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
index dd451c3..02b5a34 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
@@ -4041,20 +4041,6 @@
return val != 0;
}
-/*
- * Reset the load status for the current engine.
- */
-static void bnx2x_clear_load_status(struct bnx2x *bp)
-{
- u32 val;
- u32 mask = (BP_PATH(bp) ? BNX2X_PATH1_LOAD_CNT_MASK :
- BNX2X_PATH0_LOAD_CNT_MASK);
- bnx2x_acquire_hw_lock(bp, HW_LOCK_RESOURCE_RECOVERY_REG);
- val = REG_RD(bp, BNX2X_RECOVERY_GLOB_REG);
- REG_WR(bp, BNX2X_RECOVERY_GLOB_REG, val & (~mask));
- bnx2x_release_hw_lock(bp, HW_LOCK_RESOURCE_RECOVERY_REG);
-}
-
static void _print_next_block(int idx, const char *blk)
{
pr_cont("%s%s", idx ? ", " : "", blk);
@@ -9384,32 +9370,24 @@
return rc;
}
-static bool __devinit bnx2x_can_flr(struct bnx2x *bp)
-{
- int pos;
- u32 cap;
- struct pci_dev *dev = bp->pdev;
-
- pos = pci_pcie_cap(dev);
- if (!pos)
- return false;
-
- pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap);
- if (!(cap & PCI_EXP_DEVCAP_FLR))
- return false;
-
- return true;
-}
-
static int __devinit bnx2x_do_flr(struct bnx2x *bp)
{
int i, pos;
u16 status;
struct pci_dev *dev = bp->pdev;
- /* probe the capability first */
- if (bnx2x_can_flr(bp))
- return -ENOTTY;
+
+ if (CHIP_IS_E1x(bp)) {
+ BNX2X_DEV_INFO("FLR not supported in E1/E1H\n");
+ return -EINVAL;
+ }
+
+ /* only bootcode REQ_BC_VER_4_INITIATE_FLR and onwards support flr */
+ if (bp->common.bc_ver < REQ_BC_VER_4_INITIATE_FLR) {
+ BNX2X_ERR("FLR not supported by BC_VER: 0x%x\n",
+ bp->common.bc_ver);
+ return -EINVAL;
+ }
pos = pci_pcie_cap(dev);
if (!pos)
@@ -9429,12 +9407,8 @@
"transaction is not cleared; proceeding with reset anyway\n");
clear:
- if (bp->common.bc_ver < REQ_BC_VER_4_INITIATE_FLR) {
- BNX2X_ERR("FLR not supported by BC_VER: 0x%x\n",
- bp->common.bc_ver);
- return -EINVAL;
- }
+ BNX2X_DEV_INFO("Initiating FLR\n");
bnx2x_fw_command(bp, DRV_MSG_CODE_INITIATE_FLR, 0);
return 0;
@@ -9454,8 +9428,21 @@
* the one required, then FLR will be sufficient to clean any residue
* left by previous driver
*/
- if (bnx2x_test_firmware_version(bp, false) && bnx2x_can_flr(bp))
- return bnx2x_do_flr(bp);
+ rc = bnx2x_test_firmware_version(bp, false);
+
+ if (!rc) {
+ /* fw version is good */
+ BNX2X_DEV_INFO("FW version matches our own. Attempting FLR\n");
+ rc = bnx2x_do_flr(bp);
+ }
+
+ if (!rc) {
+ /* FLR was performed */
+ BNX2X_DEV_INFO("FLR successful\n");
+ return 0;
+ }
+
+ BNX2X_DEV_INFO("Could not FLR\n");
/* Close the MCP request, return failure*/
rc = bnx2x_prev_mcp_done(bp);
@@ -11427,9 +11414,6 @@
if (!chip_is_e1x)
REG_WR(bp, PGLUE_B_REG_INTERNAL_PFID_ENABLE_TARGET_READ, 1);
- /* Reset the load counter */
- bnx2x_clear_load_status(bp);
-
dev->watchdog_timeo = TX_TIMEOUT;
dev->netdev_ops = &bnx2x_netdev_ops;
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
index 734fd87..62f754b 100644
--- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
+++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.c
@@ -2485,6 +2485,7 @@
break;
default:
+ kfree(new_cmd);
BNX2X_ERR("Unknown command: %d\n", cmd);
return -EINVAL;
}
diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c
index c60de89..90a903d8 100644
--- a/drivers/net/ethernet/emulex/benet/be_main.c
+++ b/drivers/net/ethernet/emulex/benet/be_main.c
@@ -1948,7 +1948,7 @@
if (adapter->num_rx_qs != MAX_RX_QS)
dev_info(&adapter->pdev->dev,
- "Created only %d receive queues", adapter->num_rx_qs);
+ "Created only %d receive queues\n", adapter->num_rx_qs);
return 0;
}
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
index 0f2d1a7..1514533 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-bitbang.c
@@ -174,8 +174,10 @@
new_bus->phy_mask = ~0;
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq)
+ if (!new_bus->irq) {
+ ret = -ENOMEM;
goto out_unmap_regs;
+ }
new_bus->parent = &ofdev->dev;
dev_set_drvdata(&ofdev->dev, new_bus);
diff --git a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
index 55bb8672..cdf702a 100644
--- a/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
+++ b/drivers/net/ethernet/freescale/fs_enet/mii-fec.c
@@ -137,8 +137,10 @@
snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", res.start);
fec->fecp = ioremap(res.start, resource_size(&res));
- if (!fec->fecp)
+ if (!fec->fecp) {
+ ret = -ENOMEM;
goto out_fec;
+ }
if (get_bus_freq) {
clock = get_bus_freq(ofdev->dev.of_node);
@@ -172,8 +174,10 @@
new_bus->phy_mask = ~0;
new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL);
- if (!new_bus->irq)
+ if (!new_bus->irq) {
+ ret = -ENOMEM;
goto out_unmap_regs;
+ }
new_bus->parent = &ofdev->dev;
dev_set_drvdata(&ofdev->dev, new_bus);
diff --git a/drivers/net/ethernet/intel/e1000e/82571.c b/drivers/net/ethernet/intel/e1000e/82571.c
index 0b3bade..080c890 100644
--- a/drivers/net/ethernet/intel/e1000e/82571.c
+++ b/drivers/net/ethernet/intel/e1000e/82571.c
@@ -999,7 +999,7 @@
**/
static s32 e1000_reset_hw_82571(struct e1000_hw *hw)
{
- u32 ctrl, ctrl_ext, eecd;
+ u32 ctrl, ctrl_ext, eecd, tctl;
s32 ret_val;
/*
@@ -1014,7 +1014,9 @@
ew32(IMC, 0xffffffff);
ew32(RCTL, 0);
- ew32(TCTL, E1000_TCTL_PSP);
+ tctl = er32(TCTL);
+ tctl &= ~E1000_TCTL_EN;
+ ew32(TCTL, tctl);
e1e_flush();
usleep_range(10000, 20000);
@@ -1601,10 +1603,8 @@
* auto-negotiation in the TXCW register and disable
* forced link in the Device Control register in an
* attempt to auto-negotiate with our link partner.
- * If the partner code word is null, stop forcing
- * and restart auto negotiation.
*/
- if ((rxcw & E1000_RXCW_C) || !(rxcw & E1000_RXCW_CW)) {
+ if (rxcw & E1000_RXCW_C) {
/* Enable autoneg, and unforce link up */
ew32(TXCW, mac->txcw);
ew32(CTRL, (ctrl & ~E1000_CTRL_SLU));
diff --git a/drivers/net/ethernet/intel/e1000e/netdev.c b/drivers/net/ethernet/intel/e1000e/netdev.c
index 95b2453..46c3b1f 100644
--- a/drivers/net/ethernet/intel/e1000e/netdev.c
+++ b/drivers/net/ethernet/intel/e1000e/netdev.c
@@ -178,6 +178,24 @@
pr_info("%-15s %08x %08x\n", rname, regs[0], regs[1]);
}
+static void e1000e_dump_ps_pages(struct e1000_adapter *adapter,
+ struct e1000_buffer *bi)
+{
+ int i;
+ struct e1000_ps_page *ps_page;
+
+ for (i = 0; i < adapter->rx_ps_pages; i++) {
+ ps_page = &bi->ps_pages[i];
+
+ if (ps_page->page) {
+ pr_info("packet dump for ps_page %d:\n", i);
+ print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS,
+ 16, 1, page_address(ps_page->page),
+ PAGE_SIZE, true);
+ }
+ }
+}
+
/*
* e1000e_dump - Print registers, Tx-ring and Rx-ring
*/
@@ -299,10 +317,10 @@
(unsigned long long)buffer_info->time_stamp,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter) && buffer_info->dma != 0)
+ if (netif_msg_pktdata(adapter) && buffer_info->skb)
print_hex_dump(KERN_INFO, "", DUMP_PREFIX_ADDRESS,
- 16, 1, phys_to_virt(buffer_info->dma),
- buffer_info->length, true);
+ 16, 1, buffer_info->skb->data,
+ buffer_info->skb->len, true);
}
/* Print Rx Ring Summary */
@@ -381,10 +399,8 @@
buffer_info->skb, next_desc);
if (netif_msg_pktdata(adapter))
- print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS, 16, 1,
- phys_to_virt(buffer_info->dma),
- adapter->rx_ps_bsize0, true);
+ e1000e_dump_ps_pages(adapter,
+ buffer_info);
}
}
break;
@@ -444,12 +460,12 @@
(unsigned long long)buffer_info->dma,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter))
+ if (netif_msg_pktdata(adapter) &&
+ buffer_info->skb)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS, 16,
1,
- phys_to_virt
- (buffer_info->dma),
+ buffer_info->skb->data,
adapter->rx_buffer_len,
true);
}
diff --git a/drivers/net/ethernet/intel/igb/e1000_82575.c b/drivers/net/ethernet/intel/igb/e1000_82575.c
index 5e84eaa..ba994fb 100644
--- a/drivers/net/ethernet/intel/igb/e1000_82575.c
+++ b/drivers/net/ethernet/intel/igb/e1000_82575.c
@@ -254,6 +254,14 @@
*/
size += NVM_WORD_SIZE_BASE_SHIFT;
+ /*
+ * Check for invalid size
+ */
+ if ((hw->mac.type == e1000_82576) && (size > 15)) {
+ pr_notice("The NVM size is not valid, defaulting to 32K\n");
+ size = 15;
+ }
+
nvm->word_size = 1 << size;
if (hw->mac.type < e1000_i210) {
nvm->opcode_bits = 8;
@@ -281,14 +289,6 @@
} else
nvm->type = e1000_nvm_flash_hw;
- /*
- * Check for invalid size
- */
- if ((hw->mac.type == e1000_82576) && (size > 15)) {
- pr_notice("The NVM size is not valid, defaulting to 32K\n");
- size = 15;
- }
-
/* NVM Function Pointers */
switch (hw->mac.type) {
case e1000_82580:
diff --git a/drivers/net/ethernet/intel/igb/e1000_regs.h b/drivers/net/ethernet/intel/igb/e1000_regs.h
index 10efcd8..28394be 100644
--- a/drivers/net/ethernet/intel/igb/e1000_regs.h
+++ b/drivers/net/ethernet/intel/igb/e1000_regs.h
@@ -156,8 +156,12 @@
: (0x0E018 + ((_n) * 0x40)))
#define E1000_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) \
: (0x0E028 + ((_n) * 0x40)))
-#define E1000_DCA_TXCTRL(_n) (0x03814 + (_n << 8))
-#define E1000_DCA_RXCTRL(_n) (0x02814 + (_n << 8))
+#define E1000_RXCTL(_n) ((_n) < 4 ? (0x02814 + ((_n) * 0x100)) : \
+ (0x0C014 + ((_n) * 0x40)))
+#define E1000_DCA_RXCTRL(_n) E1000_RXCTL(_n)
+#define E1000_TXCTL(_n) ((_n) < 4 ? (0x03814 + ((_n) * 0x100)) : \
+ (0x0E014 + ((_n) * 0x40)))
+#define E1000_DCA_TXCTRL(_n) E1000_TXCTL(_n)
#define E1000_TDWBAL(_n) ((_n) < 4 ? (0x03838 + ((_n) * 0x100)) \
: (0x0E038 + ((_n) * 0x40)))
#define E1000_TDWBAH(_n) ((_n) < 4 ? (0x0383C + ((_n) * 0x100)) \
diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c
index a19c84c..7059111 100644
--- a/drivers/net/ethernet/intel/igb/igb_ethtool.c
+++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c
@@ -209,8 +209,8 @@
/* When SoL/IDER sessions are active, autoneg/speed/duplex
* cannot be changed */
if (igb_check_reset_block(hw)) {
- dev_err(&adapter->pdev->dev, "Cannot change link "
- "characteristics when SoL/IDER is active.\n");
+ dev_err(&adapter->pdev->dev,
+ "Cannot change link characteristics when SoL/IDER is active.\n");
return -EINVAL;
}
@@ -1089,8 +1089,8 @@
wr32(reg, (_test[pat] & write));
val = rd32(reg) & mask;
if (val != (_test[pat] & write & mask)) {
- dev_err(&adapter->pdev->dev, "pattern test reg %04X "
- "failed: got 0x%08X expected 0x%08X\n",
+ dev_err(&adapter->pdev->dev,
+ "pattern test reg %04X failed: got 0x%08X expected 0x%08X\n",
reg, val, (_test[pat] & write & mask));
*data = reg;
return 1;
@@ -1108,8 +1108,8 @@
wr32(reg, write & mask);
val = rd32(reg);
if ((write & mask) != (val & mask)) {
- dev_err(&adapter->pdev->dev, "set/check reg %04X test failed:"
- " got 0x%08X expected 0x%08X\n", reg,
+ dev_err(&adapter->pdev->dev,
+ "set/check reg %04X test failed: got 0x%08X expected 0x%08X\n", reg,
(val & mask), (write & mask));
*data = reg;
return 1;
@@ -1171,8 +1171,9 @@
wr32(E1000_STATUS, toggle);
after = rd32(E1000_STATUS) & toggle;
if (value != after) {
- dev_err(&adapter->pdev->dev, "failed STATUS register test "
- "got: 0x%08X expected: 0x%08X\n", after, value);
+ dev_err(&adapter->pdev->dev,
+ "failed STATUS register test got: 0x%08X expected: 0x%08X\n",
+ after, value);
*data = 1;
return 1;
}
@@ -1497,6 +1498,9 @@
break;
}
+ /* add small delay to avoid loopback test failure */
+ msleep(50);
+
/* force 1000, set loopback */
igb_write_phy_reg(hw, PHY_CONTROL, 0x4140);
@@ -1777,16 +1781,14 @@
* sessions are active */
if (igb_check_reset_block(&adapter->hw)) {
dev_err(&adapter->pdev->dev,
- "Cannot do PHY loopback test "
- "when SoL/IDER is active.\n");
+ "Cannot do PHY loopback test when SoL/IDER is active.\n");
*data = 0;
goto out;
}
if ((adapter->hw.mac.type == e1000_i210)
- || (adapter->hw.mac.type == e1000_i210)) {
+ || (adapter->hw.mac.type == e1000_i211)) {
dev_err(&adapter->pdev->dev,
- "Loopback test not supported "
- "on this part at this time.\n");
+ "Loopback test not supported on this part at this time.\n");
*data = 0;
goto out;
}
diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c
index b7c2d50..48cc4fb 100644
--- a/drivers/net/ethernet/intel/igb/igb_main.c
+++ b/drivers/net/ethernet/intel/igb/igb_main.c
@@ -462,10 +462,10 @@
(u64)buffer_info->time_stamp,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter) && buffer_info->dma != 0)
+ if (netif_msg_pktdata(adapter) && buffer_info->skb)
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS,
- 16, 1, phys_to_virt(buffer_info->dma),
+ 16, 1, buffer_info->skb->data,
buffer_info->length, true);
}
}
@@ -547,18 +547,17 @@
(u64)buffer_info->dma,
buffer_info->skb, next_desc);
- if (netif_msg_pktdata(adapter)) {
+ if (netif_msg_pktdata(adapter) &&
+ buffer_info->dma && buffer_info->skb) {
print_hex_dump(KERN_INFO, "",
- DUMP_PREFIX_ADDRESS,
- 16, 1,
- phys_to_virt(buffer_info->dma),
- IGB_RX_HDR_LEN, true);
+ DUMP_PREFIX_ADDRESS,
+ 16, 1, buffer_info->skb->data,
+ IGB_RX_HDR_LEN, true);
print_hex_dump(KERN_INFO, "",
DUMP_PREFIX_ADDRESS,
16, 1,
- phys_to_virt(
- buffer_info->page_dma +
- buffer_info->page_offset),
+ page_address(buffer_info->page) +
+ buffer_info->page_offset,
PAGE_SIZE/2, true);
}
}
diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
index 50fc137..18bf08c 100644
--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
+++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_82599.c
@@ -804,12 +804,13 @@
link_mode == IXGBE_AUTOC_LMS_KX4_KX_KR_SGMII) {
/* Set KX4/KX/KR support according to speed requested */
autoc &= ~(IXGBE_AUTOC_KX4_KX_SUPP_MASK | IXGBE_AUTOC_KR_SUPP);
- if (speed & IXGBE_LINK_SPEED_10GB_FULL)
+ if (speed & IXGBE_LINK_SPEED_10GB_FULL) {
if (orig_autoc & IXGBE_AUTOC_KX4_SUPP)
autoc |= IXGBE_AUTOC_KX4_SUPP;
if ((orig_autoc & IXGBE_AUTOC_KR_SUPP) &&
(hw->phy.smart_speed_active == false))
autoc |= IXGBE_AUTOC_KR_SUPP;
+ }
if (speed & IXGBE_LINK_SPEED_1GB_FULL)
autoc |= IXGBE_AUTOC_KX_SUPP;
} else if ((pma_pmd_1g == IXGBE_AUTOC_1G_SFI) &&
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_rx.c b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
index f32e703..5aba5ec 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_rx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_rx.c
@@ -614,8 +614,8 @@
/* If source MAC is equal to our own MAC and not performing
* the selftest or flb disabled - drop the packet */
if (s_mac == priv->mac &&
- (!(dev->features & NETIF_F_LOOPBACK) ||
- !priv->validate_loopback))
+ !((dev->features & NETIF_F_LOOPBACK) ||
+ priv->validate_loopback))
goto next;
/*
diff --git a/drivers/net/ethernet/mellanox/mlx4/en_tx.c b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
index 019d856..10bba09 100644
--- a/drivers/net/ethernet/mellanox/mlx4/en_tx.c
+++ b/drivers/net/ethernet/mellanox/mlx4/en_tx.c
@@ -164,7 +164,6 @@
ring->cons = 0xffffffff;
ring->last_nr_txbb = 1;
ring->poll_cnt = 0;
- ring->blocked = 0;
memset(ring->tx_info, 0, ring->size * sizeof(struct mlx4_en_tx_info));
memset(ring->buf, 0, ring->buf_size);
@@ -365,14 +364,13 @@
ring->cons += txbbs_skipped;
netdev_tx_completed_queue(ring->tx_queue, packets, bytes);
- /* Wakeup Tx queue if this ring stopped it */
- if (unlikely(ring->blocked)) {
- if ((u32) (ring->prod - ring->cons) <=
- ring->size - HEADROOM - MAX_DESC_TXBBS) {
- ring->blocked = 0;
- netif_tx_wake_queue(ring->tx_queue);
- priv->port_stats.wake_queue++;
- }
+ /*
+ * Wakeup Tx queue if this stopped, and at least 1 packet
+ * was completed
+ */
+ if (netif_tx_queue_stopped(ring->tx_queue) && txbbs_skipped > 0) {
+ netif_tx_wake_queue(ring->tx_queue);
+ priv->port_stats.wake_queue++;
}
}
@@ -592,7 +590,6 @@
ring->size - HEADROOM - MAX_DESC_TXBBS)) {
/* every full Tx ring stops queue */
netif_tx_stop_queue(ring->tx_queue);
- ring->blocked = 1;
priv->port_stats.queue_stopped++;
return NETDEV_TX_BUSY;
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.c b/drivers/net/ethernet/mellanox/mlx4/icm.c
index 88b7b3e7..daf4179 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.c
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.c
@@ -358,13 +358,14 @@
}
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- u64 virt, int obj_size, int nobj, int reserved,
+ u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent)
{
int obj_per_chunk;
int num_icm;
unsigned chunk_size;
int i;
+ u64 size;
obj_per_chunk = MLX4_TABLE_CHUNK_SIZE / obj_size;
num_icm = (nobj + obj_per_chunk - 1) / obj_per_chunk;
@@ -380,10 +381,12 @@
table->coherent = use_coherent;
mutex_init(&table->mutex);
+ size = (u64) nobj * obj_size;
for (i = 0; i * MLX4_TABLE_CHUNK_SIZE < reserved * obj_size; ++i) {
chunk_size = MLX4_TABLE_CHUNK_SIZE;
- if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > nobj * obj_size)
- chunk_size = PAGE_ALIGN(nobj * obj_size - i * MLX4_TABLE_CHUNK_SIZE);
+ if ((i + 1) * MLX4_TABLE_CHUNK_SIZE > size)
+ chunk_size = PAGE_ALIGN(size -
+ i * MLX4_TABLE_CHUNK_SIZE);
table->icm[i] = mlx4_alloc_icm(dev, chunk_size >> PAGE_SHIFT,
(use_lowmem ? GFP_KERNEL : GFP_HIGHUSER) |
diff --git a/drivers/net/ethernet/mellanox/mlx4/icm.h b/drivers/net/ethernet/mellanox/mlx4/icm.h
index 19e4efc..a67744f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/icm.h
+++ b/drivers/net/ethernet/mellanox/mlx4/icm.h
@@ -78,7 +78,7 @@
void mlx4_table_put_range(struct mlx4_dev *dev, struct mlx4_icm_table *table,
int start, int end);
int mlx4_init_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table,
- u64 virt, int obj_size, int nobj, int reserved,
+ u64 virt, int obj_size, u32 nobj, int reserved,
int use_lowmem, int use_coherent);
void mlx4_cleanup_icm_table(struct mlx4_dev *dev, struct mlx4_icm_table *table);
void *mlx4_table_find(struct mlx4_icm_table *table, int obj, dma_addr_t *dma_handle);
diff --git a/drivers/net/ethernet/mellanox/mlx4/main.c b/drivers/net/ethernet/mellanox/mlx4/main.c
index 48d0e90..827b72d 100644
--- a/drivers/net/ethernet/mellanox/mlx4/main.c
+++ b/drivers/net/ethernet/mellanox/mlx4/main.c
@@ -157,9 +157,6 @@
"on this HCA, aborting.\n");
return -EINVAL;
}
- if (port_type[i] == MLX4_PORT_TYPE_ETH &&
- port_type[i + 1] == MLX4_PORT_TYPE_IB)
- return -EINVAL;
}
}
diff --git a/drivers/net/ethernet/mellanox/mlx4/mcg.c b/drivers/net/ethernet/mellanox/mlx4/mcg.c
index 4ec3835..a018ea2 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mcg.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mcg.c
@@ -432,8 +432,10 @@
if ((be32_to_cpu(mgm->qp[i]) & MGM_QPN_MASK) == qpn) {
/* Entry already exists, add to duplicates */
dqp = kmalloc(sizeof *dqp, GFP_KERNEL);
- if (!dqp)
+ if (!dqp) {
+ err = -ENOMEM;
goto out_mailbox;
+ }
dqp->qpn = qpn;
list_add_tail(&dqp->list, &entry->duplicates);
found = true;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4.h b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
index 59ebc03..4d9df8f 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4.h
@@ -249,7 +249,7 @@
struct mlx4_buddy {
unsigned long **bits;
unsigned int *num_free;
- int max_order;
+ u32 max_order;
spinlock_t lock;
};
@@ -258,7 +258,7 @@
struct mlx4_icm_table {
u64 virt;
int num_icm;
- int num_obj;
+ u32 num_obj;
int obj_size;
int lowmem;
int coherent;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
index 5f1ab10..9d27e42 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
+++ b/drivers/net/ethernet/mellanox/mlx4/mlx4_en.h
@@ -248,7 +248,6 @@
u32 doorbell_qpn;
void *buf;
u16 poll_cnt;
- int blocked;
struct mlx4_en_tx_info *tx_info;
u8 *bounce_buf;
u32 last_nr_txbb;
diff --git a/drivers/net/ethernet/mellanox/mlx4/mr.c b/drivers/net/ethernet/mellanox/mlx4/mr.c
index af55b7c..c202d3a 100644
--- a/drivers/net/ethernet/mellanox/mlx4/mr.c
+++ b/drivers/net/ethernet/mellanox/mlx4/mr.c
@@ -37,6 +37,7 @@
#include <linux/export.h>
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/vmalloc.h>
#include <linux/mlx4/cmd.h>
@@ -120,7 +121,7 @@
buddy->max_order = max_order;
spin_lock_init(&buddy->lock);
- buddy->bits = kzalloc((buddy->max_order + 1) * sizeof (long *),
+ buddy->bits = kcalloc(buddy->max_order + 1, sizeof (long *),
GFP_KERNEL);
buddy->num_free = kcalloc((buddy->max_order + 1), sizeof *buddy->num_free,
GFP_KERNEL);
@@ -129,10 +130,12 @@
for (i = 0; i <= buddy->max_order; ++i) {
s = BITS_TO_LONGS(1 << (buddy->max_order - i));
- buddy->bits[i] = kmalloc(s * sizeof (long), GFP_KERNEL);
- if (!buddy->bits[i])
- goto err_out_free;
- bitmap_zero(buddy->bits[i], 1 << (buddy->max_order - i));
+ buddy->bits[i] = kcalloc(s, sizeof (long), GFP_KERNEL | __GFP_NOWARN);
+ if (!buddy->bits[i]) {
+ buddy->bits[i] = vzalloc(s * sizeof(long));
+ if (!buddy->bits[i])
+ goto err_out_free;
+ }
}
set_bit(0, buddy->bits[buddy->max_order]);
@@ -142,7 +145,10 @@
err_out_free:
for (i = 0; i <= buddy->max_order; ++i)
- kfree(buddy->bits[i]);
+ if (buddy->bits[i] && is_vmalloc_addr(buddy->bits[i]))
+ vfree(buddy->bits[i]);
+ else
+ kfree(buddy->bits[i]);
err_out:
kfree(buddy->bits);
@@ -156,7 +162,10 @@
int i;
for (i = 0; i <= buddy->max_order; ++i)
- kfree(buddy->bits[i]);
+ if (is_vmalloc_addr(buddy->bits[i]))
+ vfree(buddy->bits[i]);
+ else
+ kfree(buddy->bits[i]);
kfree(buddy->bits);
kfree(buddy->num_free);
@@ -668,7 +677,7 @@
return err;
err = mlx4_buddy_init(&mr_table->mtt_buddy,
- ilog2(dev->caps.num_mtts /
+ ilog2((u32)dev->caps.num_mtts /
(1 << log_mtts_per_seg)));
if (err)
goto err_buddy;
@@ -678,7 +687,7 @@
mlx4_alloc_mtt_range(dev,
fls(dev->caps.reserved_mtts - 1));
if (priv->reserved_mtts < 0) {
- mlx4_warn(dev, "MTT table of order %d is too small.\n",
+ mlx4_warn(dev, "MTT table of order %u is too small.\n",
mr_table->mtt_buddy.max_order);
err = -ENOMEM;
goto err_reserve_mtts;
diff --git a/drivers/net/ethernet/mellanox/mlx4/profile.c b/drivers/net/ethernet/mellanox/mlx4/profile.c
index 9ee4725..8e0c3cc 100644
--- a/drivers/net/ethernet/mellanox/mlx4/profile.c
+++ b/drivers/net/ethernet/mellanox/mlx4/profile.c
@@ -76,7 +76,7 @@
u64 size;
u64 start;
int type;
- int num;
+ u32 num;
int log_num;
};
@@ -105,7 +105,7 @@
si_meminfo(&si);
request->num_mtt =
roundup_pow_of_two(max_t(unsigned, request->num_mtt,
- min(1UL << 31,
+ min(1UL << (31 - log_mtts_per_seg),
si.totalram >> (log_mtts_per_seg - 1))));
profile[MLX4_RES_QP].size = dev_cap->qpc_entry_sz;
diff --git a/drivers/net/ethernet/mellanox/mlx4/sense.c b/drivers/net/ethernet/mellanox/mlx4/sense.c
index 8024982..34ee09b 100644
--- a/drivers/net/ethernet/mellanox/mlx4/sense.c
+++ b/drivers/net/ethernet/mellanox/mlx4/sense.c
@@ -81,20 +81,6 @@
}
/*
- * Adjust port configuration:
- * If port 1 sensed nothing and port 2 is IB, set both as IB
- * If port 2 sensed nothing and port 1 is Eth, set both as Eth
- */
- if (stype[0] == MLX4_PORT_TYPE_ETH) {
- for (i = 1; i < dev->caps.num_ports; i++)
- stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_ETH;
- }
- if (stype[dev->caps.num_ports - 1] == MLX4_PORT_TYPE_IB) {
- for (i = 0; i < dev->caps.num_ports - 1; i++)
- stype[i] = stype[i] ? stype[i] : MLX4_PORT_TYPE_IB;
- }
-
- /*
* If sensed nothing, remain in current configuration.
*/
for (i = 0; i < dev->caps.num_ports; i++)
diff --git a/drivers/net/ethernet/nxp/lpc_eth.c b/drivers/net/ethernet/nxp/lpc_eth.c
index 4069eda..53743f7 100644
--- a/drivers/net/ethernet/nxp/lpc_eth.c
+++ b/drivers/net/ethernet/nxp/lpc_eth.c
@@ -346,28 +346,15 @@
"phy-mode", NULL);
if (mode && !strcmp(mode, "mii"))
return PHY_INTERFACE_MODE_MII;
- return PHY_INTERFACE_MODE_RMII;
}
-
- /* non-DT */
-#ifdef CONFIG_ARCH_LPC32XX_MII_SUPPORT
- return PHY_INTERFACE_MODE_MII;
-#else
return PHY_INTERFACE_MODE_RMII;
-#endif
}
static bool use_iram_for_net(struct device *dev)
{
if (dev && dev->of_node)
return of_property_read_bool(dev->of_node, "use-iram");
-
- /* non-DT */
-#ifdef CONFIG_ARCH_LPC32XX_IRAM_FOR_NET
- return true;
-#else
return false;
-#endif
}
/* Receive Status information word */
diff --git a/drivers/net/ethernet/renesas/Kconfig b/drivers/net/ethernet/renesas/Kconfig
index 46df3a0..24c2305 100644
--- a/drivers/net/ethernet/renesas/Kconfig
+++ b/drivers/net/ethernet/renesas/Kconfig
@@ -8,7 +8,7 @@
(CPU_SUBTYPE_SH7710 || CPU_SUBTYPE_SH7712 || \
CPU_SUBTYPE_SH7763 || CPU_SUBTYPE_SH7619 || \
CPU_SUBTYPE_SH7724 || CPU_SUBTYPE_SH7734 || \
- CPU_SUBTYPE_SH7757 || ARCH_R8A7740)
+ CPU_SUBTYPE_SH7757 || ARCH_R8A7740 || ARCH_R8A7779)
select CRC32
select NET_CORE
select MII
@@ -18,4 +18,4 @@
Renesas SuperH Ethernet device driver.
This driver supporting CPUs are:
- SH7619, SH7710, SH7712, SH7724, SH7734, SH7763, SH7757,
- and R8A7740.
+ R8A7740 and R8A7779.
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index af0b867..bad8f2e 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -78,7 +78,7 @@
#endif
/* There is CPU dependent code */
-#if defined(CONFIG_CPU_SUBTYPE_SH7724)
+#if defined(CONFIG_CPU_SUBTYPE_SH7724) || defined(CONFIG_ARCH_R8A7779)
#define SH_ETH_RESET_DEFAULT 1
static void sh_eth_set_duplex(struct net_device *ndev)
{
@@ -93,13 +93,18 @@
static void sh_eth_set_rate(struct net_device *ndev)
{
struct sh_eth_private *mdp = netdev_priv(ndev);
+ unsigned int bits = ECMR_RTM;
+
+#if defined(CONFIG_ARCH_R8A7779)
+ bits |= ECMR_ELB;
+#endif
switch (mdp->speed) {
case 10: /* 10BASE */
- sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~ECMR_RTM, ECMR);
+ sh_eth_write(ndev, sh_eth_read(ndev, ECMR) & ~bits, ECMR);
break;
case 100:/* 100BASE */
- sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | ECMR_RTM, ECMR);
+ sh_eth_write(ndev, sh_eth_read(ndev, ECMR) | bits, ECMR);
break;
default:
break;
diff --git a/drivers/net/ethernet/sfc/efx.c b/drivers/net/ethernet/sfc/efx.c
index 70554a1..65a8d49 100644
--- a/drivers/net/ethernet/sfc/efx.c
+++ b/drivers/net/ethernet/sfc/efx.c
@@ -1503,6 +1503,11 @@
goto fail2;
}
+ BUILD_BUG_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_RXQ_MIN_ENT);
+ if (WARN_ON(EFX_DEFAULT_DMAQ_SIZE < EFX_TXQ_MIN_ENT(efx))) {
+ rc = -EINVAL;
+ goto fail3;
+ }
efx->rxq_entries = efx->txq_entries = EFX_DEFAULT_DMAQ_SIZE;
rc = efx_probe_filters(efx);
@@ -2070,6 +2075,7 @@
net_dev->irq = efx->pci_dev->irq;
net_dev->netdev_ops = &efx_netdev_ops;
SET_ETHTOOL_OPS(net_dev, &efx_ethtool_ops);
+ net_dev->gso_max_segs = EFX_TSO_MAX_SEGS;
rtnl_lock();
diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h
index be8f915..70755c9 100644
--- a/drivers/net/ethernet/sfc/efx.h
+++ b/drivers/net/ethernet/sfc/efx.h
@@ -30,6 +30,7 @@
efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb);
extern void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index);
extern int efx_setup_tc(struct net_device *net_dev, u8 num_tc);
+extern unsigned int efx_tx_max_skb_descs(struct efx_nic *efx);
/* RX */
extern int efx_probe_rx_queue(struct efx_rx_queue *rx_queue);
@@ -52,10 +53,15 @@
#define EFX_MAX_EVQ_SIZE 16384UL
#define EFX_MIN_EVQ_SIZE 512UL
-/* The smallest [rt]xq_entries that the driver supports. Callers of
- * efx_wake_queue() assume that they can subsequently send at least one
- * skb. Falcon/A1 may require up to three descriptors per skb_frag. */
-#define EFX_MIN_RING_SIZE (roundup_pow_of_two(2 * 3 * MAX_SKB_FRAGS))
+/* Maximum number of TCP segments we support for soft-TSO */
+#define EFX_TSO_MAX_SEGS 100
+
+/* The smallest [rt]xq_entries that the driver supports. RX minimum
+ * is a bit arbitrary. For TX, we must have space for at least 2
+ * TSO skbs.
+ */
+#define EFX_RXQ_MIN_ENT 128U
+#define EFX_TXQ_MIN_ENT(efx) (2 * efx_tx_max_skb_descs(efx))
/* Filters */
extern int efx_probe_filters(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c
index 10536f9..8cba2df 100644
--- a/drivers/net/ethernet/sfc/ethtool.c
+++ b/drivers/net/ethernet/sfc/ethtool.c
@@ -680,21 +680,27 @@
struct ethtool_ringparam *ring)
{
struct efx_nic *efx = netdev_priv(net_dev);
+ u32 txq_entries;
if (ring->rx_mini_pending || ring->rx_jumbo_pending ||
ring->rx_pending > EFX_MAX_DMAQ_SIZE ||
ring->tx_pending > EFX_MAX_DMAQ_SIZE)
return -EINVAL;
- if (ring->rx_pending < EFX_MIN_RING_SIZE ||
- ring->tx_pending < EFX_MIN_RING_SIZE) {
+ if (ring->rx_pending < EFX_RXQ_MIN_ENT) {
netif_err(efx, drv, efx->net_dev,
- "TX and RX queues cannot be smaller than %ld\n",
- EFX_MIN_RING_SIZE);
+ "RX queues cannot be smaller than %u\n",
+ EFX_RXQ_MIN_ENT);
return -EINVAL;
}
- return efx_realloc_channels(efx, ring->rx_pending, ring->tx_pending);
+ txq_entries = max(ring->tx_pending, EFX_TXQ_MIN_ENT(efx));
+ if (txq_entries != ring->tx_pending)
+ netif_warn(efx, drv, efx->net_dev,
+ "increasing TX queue size to minimum of %u\n",
+ txq_entries);
+
+ return efx_realloc_channels(efx, ring->rx_pending, txq_entries);
}
static int efx_ethtool_set_pauseparam(struct net_device *net_dev,
diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c
index 9b225a7..1871343 100644
--- a/drivers/net/ethernet/sfc/tx.c
+++ b/drivers/net/ethernet/sfc/tx.c
@@ -119,6 +119,25 @@
return len;
}
+unsigned int efx_tx_max_skb_descs(struct efx_nic *efx)
+{
+ /* Header and payload descriptor for each output segment, plus
+ * one for every input fragment boundary within a segment
+ */
+ unsigned int max_descs = EFX_TSO_MAX_SEGS * 2 + MAX_SKB_FRAGS;
+
+ /* Possibly one more per segment for the alignment workaround */
+ if (EFX_WORKAROUND_5391(efx))
+ max_descs += EFX_TSO_MAX_SEGS;
+
+ /* Possibly more for PCIe page boundaries within input fragments */
+ if (PAGE_SIZE > EFX_PAGE_SIZE)
+ max_descs += max_t(unsigned int, MAX_SKB_FRAGS,
+ DIV_ROUND_UP(GSO_MAX_SIZE, EFX_PAGE_SIZE));
+
+ return max_descs;
+}
+
/*
* Add a socket buffer to a TX queue
*
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
index fd8882f..c136162 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
@@ -2077,7 +2077,7 @@
goto error_netdev_register;
}
- priv->stmmac_clk = clk_get(priv->device, NULL);
+ priv->stmmac_clk = clk_get(priv->device, STMMAC_RESOURCE_NAME);
if (IS_ERR(priv->stmmac_clk)) {
pr_warning("%s: warning: cannot get CSR clock\n", __func__);
goto error_clk_get;
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
index cd01ee7..b93245c 100644
--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
@@ -74,7 +74,7 @@
* the necessary resources and invokes the main to init
* the net device, register the mdio bus etc.
*/
-static int stmmac_pltfr_probe(struct platform_device *pdev)
+static int __devinit stmmac_pltfr_probe(struct platform_device *pdev)
{
int ret = 0;
struct resource *res;
diff --git a/drivers/net/ethernet/ti/davinci_cpdma.c b/drivers/net/ethernet/ti/davinci_cpdma.c
index 3b5c457..d15c888 100644
--- a/drivers/net/ethernet/ti/davinci_cpdma.c
+++ b/drivers/net/ethernet/ti/davinci_cpdma.c
@@ -538,11 +538,12 @@
int cpdma_chan_destroy(struct cpdma_chan *chan)
{
- struct cpdma_ctlr *ctlr = chan->ctlr;
+ struct cpdma_ctlr *ctlr;
unsigned long flags;
if (!chan)
return -EINVAL;
+ ctlr = chan->ctlr;
spin_lock_irqsave(&ctlr->lock, flags);
if (chan->state != CPDMA_STATE_IDLE)
diff --git a/drivers/net/ethernet/xscale/ixp4xx_eth.c b/drivers/net/ethernet/xscale/ixp4xx_eth.c
index 482648f..98934bd 100644
--- a/drivers/net/ethernet/xscale/ixp4xx_eth.c
+++ b/drivers/net/ethernet/xscale/ixp4xx_eth.c
@@ -1003,6 +1003,7 @@
}
int ixp46x_phc_index = -1;
+EXPORT_SYMBOL_GPL(ixp46x_phc_index);
static int ixp4xx_get_ts_info(struct net_device *dev,
struct ethtool_ts_info *info)
diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c
index 6cee291..4a1a5f5 100644
--- a/drivers/net/hyperv/netvsc.c
+++ b/drivers/net/hyperv/netvsc.c
@@ -383,13 +383,6 @@
unsigned long flags;
net_device = hv_get_drvdata(device);
- spin_lock_irqsave(&device->channel->inbound_lock, flags);
- net_device->destroy = true;
- spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
-
- /* Wait for all send completions */
- wait_event(net_device->wait_drain,
- atomic_read(&net_device->num_outstanding_sends) == 0);
netvsc_disconnect_vsp(net_device);
diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c
index e5d6146..1e88a10 100644
--- a/drivers/net/hyperv/rndis_filter.c
+++ b/drivers/net/hyperv/rndis_filter.c
@@ -718,6 +718,9 @@
{
struct rndis_request *request;
struct rndis_halt_request *halt;
+ struct netvsc_device *nvdev = dev->net_dev;
+ struct hv_device *hdev = nvdev->dev;
+ ulong flags;
/* Attempt to do a rndis device halt */
request = get_rndis_request(dev, RNDIS_MSG_HALT,
@@ -735,6 +738,14 @@
dev->state = RNDIS_DEV_UNINITIALIZED;
cleanup:
+ spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
+ nvdev->destroy = true;
+ spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
+
+ /* Wait for all send completions */
+ wait_event(nvdev->wait_drain,
+ atomic_read(&nvdev->num_outstanding_sends) == 0);
+
if (request)
put_rndis_request(dev, request);
return;
diff --git a/drivers/net/irda/bfin_sir.c b/drivers/net/irda/bfin_sir.c
index a561ae4..c6a0299 100644
--- a/drivers/net/irda/bfin_sir.c
+++ b/drivers/net/irda/bfin_sir.c
@@ -158,7 +158,7 @@
/* If not add the 'RPOLC', we can't catch the receive interrupt.
* It's related with the HW layout and the IR transiver.
*/
- val |= IREN | RPOLC;
+ val |= UMOD_IRDA | RPOLC;
UART_PUT_GCTL(port, val);
return ret;
}
@@ -432,7 +432,7 @@
bfin_sir_stop_rx(port);
val = UART_GET_GCTL(port);
- val &= ~(UCEN | IREN | RPOLC);
+ val &= ~(UCEN | UMOD_MASK | RPOLC);
UART_PUT_GCTL(port, val);
#ifdef CONFIG_SIR_BFIN_DMA
@@ -518,10 +518,10 @@
* reset all the UART.
*/
val = UART_GET_GCTL(port);
- val &= ~(IREN | RPOLC);
+ val &= ~(UMOD_MASK | RPOLC);
UART_PUT_GCTL(port, val);
SSYNC();
- val |= IREN | RPOLC;
+ val |= UMOD_IRDA | RPOLC;
UART_PUT_GCTL(port, val);
SSYNC();
/* bfin_sir_set_speed(port, self->speed); */
diff --git a/drivers/net/irda/ks959-sir.c b/drivers/net/irda/ks959-sir.c
index 824e2a9..5f3aeac 100644
--- a/drivers/net/irda/ks959-sir.c
+++ b/drivers/net/irda/ks959-sir.c
@@ -542,6 +542,7 @@
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
if (!kingsun->irlap) {
+ err = -ENOMEM;
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
goto free_mem;
}
diff --git a/drivers/net/irda/ksdazzle-sir.c b/drivers/net/irda/ksdazzle-sir.c
index 5a278ab..2d4b6a1 100644
--- a/drivers/net/irda/ksdazzle-sir.c
+++ b/drivers/net/irda/ksdazzle-sir.c
@@ -436,6 +436,7 @@
sprintf(hwname, "usb#%d", kingsun->usbdev->devnum);
kingsun->irlap = irlap_open(netdev, &kingsun->qos, hwname);
if (!kingsun->irlap) {
+ err = -ENOMEM;
dev_err(&kingsun->usbdev->dev, "irlap_open failed\n");
goto free_mem;
}
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index 0737bd4..0f0f9ce 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -94,7 +94,8 @@
int i;
for (i = 0; i < MAX_MACVTAP_QUEUES; i++) {
- if (rcu_dereference(vlan->taps[i]) == q)
+ if (rcu_dereference_protected(vlan->taps[i],
+ lockdep_is_held(&macvtap_lock)) == q)
return i;
}
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index f9347ea..b332112 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -640,15 +640,9 @@
* rtnl_lock already held
*/
if (nt->np.dev) {
- spin_unlock_irqrestore(
- &target_list_lock,
- flags);
__netpoll_cleanup(&nt->np);
- spin_lock_irqsave(&target_list_lock,
- flags);
dev_put(nt->np.dev);
nt->np.dev = NULL;
- netconsole_target_put(nt);
}
nt->enabled = 0;
stopped = true;
diff --git a/drivers/net/phy/mdio-mux-gpio.c b/drivers/net/phy/mdio-mux-gpio.c
index e0cc4ef..eefe49e 100644
--- a/drivers/net/phy/mdio-mux-gpio.c
+++ b/drivers/net/phy/mdio-mux-gpio.c
@@ -101,7 +101,6 @@
n--;
gpio_free(s->gpio[n]);
}
- devm_kfree(&pdev->dev, s);
return r;
}
diff --git a/drivers/net/phy/mdio-mux.c b/drivers/net/phy/mdio-mux.c
index 5c12018..4d4d25e 100644
--- a/drivers/net/phy/mdio-mux.c
+++ b/drivers/net/phy/mdio-mux.c
@@ -132,7 +132,7 @@
pb->mii_bus = parent_bus;
ret_val = -ENODEV;
- for_each_child_of_node(dev->of_node, child_bus_node) {
+ for_each_available_child_of_node(dev->of_node, child_bus_node) {
u32 v;
r = of_property_read_u32(child_bus_node, "reg", &v);
diff --git a/drivers/net/ppp/pptp.c b/drivers/net/ppp/pptp.c
index 1c98321..162464f 100644
--- a/drivers/net/ppp/pptp.c
+++ b/drivers/net/ppp/pptp.c
@@ -189,7 +189,7 @@
if (sk_pppox(po)->sk_state & PPPOX_DEAD)
goto tx_error;
- rt = ip_route_output_ports(&init_net, &fl4, NULL,
+ rt = ip_route_output_ports(sock_net(sk), &fl4, NULL,
opt->dst_addr.sin_addr.s_addr,
opt->src_addr.sin_addr.s_addr,
0, 0, IPPROTO_GRE,
@@ -468,7 +468,7 @@
po->chan.private = sk;
po->chan.ops = &pptp_chan_ops;
- rt = ip_route_output_ports(&init_net, &fl4, sk,
+ rt = ip_route_output_ports(sock_net(sk), &fl4, sk,
opt->dst_addr.sin_addr.s_addr,
opt->src_addr.sin_addr.s_addr,
0, 0,
diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c
index 87707ab..341b65d 100644
--- a/drivers/net/team/team.c
+++ b/drivers/net/team/team.c
@@ -795,16 +795,17 @@
}
#ifdef CONFIG_NET_POLL_CONTROLLER
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port,
+ gfp_t gfp)
{
struct netpoll *np;
int err;
- np = kzalloc(sizeof(*np), GFP_KERNEL);
+ np = kzalloc(sizeof(*np), gfp);
if (!np)
return -ENOMEM;
- err = __netpoll_setup(np, port->dev);
+ err = __netpoll_setup(np, port->dev, gfp);
if (err) {
kfree(np);
return err;
@@ -833,7 +834,8 @@
}
#else
-static int team_port_enable_netpoll(struct team *team, struct team_port *port)
+static int team_port_enable_netpoll(struct team *team, struct team_port *port,
+ gfp_t gfp)
{
return 0;
}
@@ -913,7 +915,7 @@
}
if (team_netpoll_info(team)) {
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(team, port, GFP_KERNEL);
if (err) {
netdev_err(dev, "Failed to enable netpoll on device %s\n",
portname);
@@ -1443,7 +1445,7 @@
}
static int team_netpoll_setup(struct net_device *dev,
- struct netpoll_info *npifo)
+ struct netpoll_info *npifo, gfp_t gfp)
{
struct team *team = netdev_priv(dev);
struct team_port *port;
@@ -1451,7 +1453,7 @@
mutex_lock(&team->lock);
list_for_each_entry(port, &team->port_list, list) {
- err = team_port_enable_netpoll(team, port);
+ err = team_port_enable_netpoll(team, port, gfp);
if (err) {
__team_netpoll_cleanup(team);
break;
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index 926d4db..3a16d4f 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -187,7 +187,6 @@
netif_tx_lock_bh(tun->dev);
netif_carrier_off(tun->dev);
tun->tfile = NULL;
- tun->socket.file = NULL;
netif_tx_unlock_bh(tun->dev);
/* Drop read queue */
diff --git a/drivers/net/usb/cdc-phonet.c b/drivers/net/usb/cdc-phonet.c
index 6461004..7d78669 100644
--- a/drivers/net/usb/cdc-phonet.c
+++ b/drivers/net/usb/cdc-phonet.c
@@ -232,6 +232,7 @@
struct urb *req = usb_alloc_urb(0, GFP_KERNEL);
if (!req || rx_submit(pnd, req, GFP_KERNEL | __GFP_COLD)) {
+ usb_free_urb(req);
usbpn_close(dev);
return -ENOMEM;
}
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index f4ce5957..4cd582a 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -1225,6 +1225,26 @@
.driver_info = (unsigned long) &wwan_info,
},
+ /* Dell branded MBM devices like DW5550 */
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_VENDOR,
+ .idVendor = 0x413c,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .driver_info = (unsigned long) &wwan_info,
+ },
+
+ /* Toshiba branded MBM devices */
+ { .match_flags = USB_DEVICE_ID_MATCH_INT_INFO
+ | USB_DEVICE_ID_MATCH_VENDOR,
+ .idVendor = 0x0930,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_NCM,
+ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+ .driver_info = (unsigned long) &wwan_info,
+ },
+
/* Generic CDC-NCM devices */
{ USB_INTERFACE_INFO(USB_CLASS_COMM,
USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE),
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 2ea126a..328397c 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -247,30 +247,12 @@
*/
static int qmi_wwan_bind_shared(struct usbnet *dev, struct usb_interface *intf)
{
- int rv;
struct qmi_wwan_state *info = (void *)&dev->data;
- /* ZTE makes devices where the interface descriptors and endpoint
- * configurations of two or more interfaces are identical, even
- * though the functions are completely different. If set, then
- * driver_info->data is a bitmap of acceptable interface numbers
- * allowing us to bind to one such interface without binding to
- * all of them
- */
- if (dev->driver_info->data &&
- !test_bit(intf->cur_altsetting->desc.bInterfaceNumber, &dev->driver_info->data)) {
- dev_info(&intf->dev, "not on our whitelist - ignored");
- rv = -ENODEV;
- goto err;
- }
-
/* control and data is shared */
info->control = intf;
info->data = intf;
- rv = qmi_wwan_register_subdriver(dev);
-
-err:
- return rv;
+ return qmi_wwan_register_subdriver(dev);
}
static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -356,214 +338,64 @@
.manage_power = qmi_wwan_manage_power,
};
-static const struct driver_info qmi_wwan_force_int0 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(0), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int1 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(1), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int2 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(2), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int3 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(3), /* interface whitelist bitmap */
-};
-
-static const struct driver_info qmi_wwan_force_int4 = {
- .description = "Qualcomm WWAN/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(4), /* interface whitelist bitmap */
-};
-
-/* Sierra Wireless provide equally useless interface descriptors
- * Devices in QMI mode can be switched between two different
- * configurations:
- * a) USB interface #8 is QMI/wwan
- * b) USB interfaces #8, #19 and #20 are QMI/wwan
- *
- * Both configurations provide a number of other interfaces (serial++),
- * some of which have the same endpoint configuration as we expect, so
- * a whitelist or blacklist is necessary.
- *
- * FIXME: The below whitelist should include BIT(20). It does not
- * because I cannot get it to work...
- */
-static const struct driver_info qmi_wwan_sierra = {
- .description = "Sierra Wireless wwan/QMI device",
- .flags = FLAG_WWAN,
- .bind = qmi_wwan_bind_shared,
- .unbind = qmi_wwan_unbind,
- .manage_power = qmi_wwan_manage_power,
- .data = BIT(8) | BIT(19), /* interface whitelist bitmap */
-};
-
#define HUAWEI_VENDOR_ID 0x12D1
+/* map QMI/wwan function by a fixed interface number */
+#define QMI_FIXED_INTF(vend, prod, num) \
+ USB_DEVICE_INTERFACE_NUMBER(vend, prod, num), \
+ .driver_info = (unsigned long)&qmi_wwan_shared
+
/* Gobi 1000 QMI/wwan interface number is 3 according to qcserial */
#define QMI_GOBI1K_DEVICE(vend, prod) \
- USB_DEVICE(vend, prod), \
- .driver_info = (unsigned long)&qmi_wwan_force_int3
+ QMI_FIXED_INTF(vend, prod, 3)
-/* Gobi 2000 and Gobi 3000 QMI/wwan interface number is 0 according to qcserial */
+/* Gobi 2000/3000 QMI/wwan interface number is 0 according to qcserial */
#define QMI_GOBI_DEVICE(vend, prod) \
- USB_DEVICE(vend, prod), \
- .driver_info = (unsigned long)&qmi_wwan_force_int0
+ QMI_FIXED_INTF(vend, prod, 0)
static const struct usb_device_id products[] = {
+ /* 1. CDC ECM like devices match on the control interface */
{ /* Huawei E392, E398 and possibly others sharing both device id and more... */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 9, /* CDC Ethernet *control* interface */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 9),
.driver_info = (unsigned long)&qmi_wwan_info,
},
{ /* Vodafone/Huawei K5005 (12d1:14c8) and similar modems */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 57, /* CDC Ethernet *control* interface */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 57),
.driver_info = (unsigned long)&qmi_wwan_info,
},
- { /* Huawei E392, E398 and possibly others in "Windows mode"
- * using a combined control and data interface without any CDC
- * functional descriptors
- */
- .match_flags = USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = HUAWEI_VENDOR_ID,
- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
- .bInterfaceSubClass = 1,
- .bInterfaceProtocol = 17,
+
+ /* 2. Combined interface devices matching on class+protocol */
+ { /* Huawei E392, E398 and possibly others in "Windows mode" */
+ USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, USB_CLASS_VENDOR_SPEC, 1, 17),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
{ /* Pantech UML290 */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x106c,
- .idProduct = 0x3718,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xf0,
- .bInterfaceProtocol = 0xff,
+ USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf0, 0xff),
.driver_info = (unsigned long)&qmi_wwan_shared,
},
- { /* ZTE MF820D */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0167,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE MF821D */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0326,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3520-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0055,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int1,
- },
- { /* ZTE (Vodafone) K3565-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0063,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3570-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1008,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3571-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1010,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K3765-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x2002,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE (Vodafone) K4505-Z */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x0104,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int4,
- },
- { /* ZTE MF60 */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x19d2,
- .idProduct = 0x1402,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_force_int2,
- },
- { /* Sierra Wireless MC77xx in QMI mode */
- .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO,
- .idVendor = 0x1199,
- .idProduct = 0x68a2,
- .bInterfaceClass = 0xff,
- .bInterfaceSubClass = 0xff,
- .bInterfaceProtocol = 0xff,
- .driver_info = (unsigned long)&qmi_wwan_sierra,
+ { /* Pantech UML290 - newer firmware */
+ USB_DEVICE_AND_INTERFACE_INFO(0x106c, 0x3718, USB_CLASS_VENDOR_SPEC, 0xf1, 0xff),
+ .driver_info = (unsigned long)&qmi_wwan_shared,
},
- /* Gobi 1000 devices */
+ /* 3. Combined interface devices matching on interface number */
+ {QMI_FIXED_INTF(0x19d2, 0x0055, 1)}, /* ZTE (Vodafone) K3520-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0063, 4)}, /* ZTE (Vodafone) K3565-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0104, 4)}, /* ZTE (Vodafone) K4505-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x0167, 4)}, /* ZTE MF820D */
+ {QMI_FIXED_INTF(0x19d2, 0x0326, 4)}, /* ZTE MF821D */
+ {QMI_FIXED_INTF(0x19d2, 0x1008, 4)}, /* ZTE (Vodafone) K3570-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1010, 4)}, /* ZTE (Vodafone) K3571-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1018, 3)}, /* ZTE (Vodafone) K5006-Z */
+ {QMI_FIXED_INTF(0x19d2, 0x1402, 2)}, /* ZTE MF60 */
+ {QMI_FIXED_INTF(0x19d2, 0x2002, 4)}, /* ZTE (Vodafone) K3765-Z */
+ {QMI_FIXED_INTF(0x0f3d, 0x68a2, 8)}, /* Sierra Wireless MC7700 */
+ {QMI_FIXED_INTF(0x114f, 0x68a2, 8)}, /* Sierra Wireless MC7750 */
+ {QMI_FIXED_INTF(0x1199, 0x68a2, 8)}, /* Sierra Wireless MC7710 in QMI mode */
+ {QMI_FIXED_INTF(0x1199, 0x68a2, 19)}, /* Sierra Wireless MC7710 in QMI mode */
+ {QMI_FIXED_INTF(0x1199, 0x901c, 8)}, /* Sierra Wireless EM7700 */
+
+ /* 4. Gobi 1000 devices */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
{QMI_GOBI1K_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
{QMI_GOBI1K_DEVICE(0x03f0, 0x371d)}, /* HP un2430 Mobile Broadband Module */
@@ -579,7 +411,7 @@
{QMI_GOBI1K_DEVICE(0x05c6, 0x9222)}, /* Generic Gobi Modem device */
{QMI_GOBI1K_DEVICE(0x05c6, 0x9009)}, /* Generic Gobi Modem device */
- /* Gobi 2000 and 3000 devices */
+ /* 5. Gobi 2000 and 3000 devices */
{QMI_GOBI_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
{QMI_GOBI_DEVICE(0x05c6, 0x920b)}, /* Generic Gobi 2000 Modem device */
{QMI_GOBI_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
@@ -589,6 +421,8 @@
{QMI_GOBI_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
{QMI_GOBI_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
{QMI_GOBI_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
+ {QMI_GOBI_DEVICE(0x1199, 0x68a5)}, /* Sierra Wireless Modem */
+ {QMI_GOBI_DEVICE(0x1199, 0x68a9)}, /* Sierra Wireless Modem */
{QMI_GOBI_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
@@ -600,11 +434,14 @@
{QMI_GOBI_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
{QMI_GOBI_DEVICE(0x1199, 0x9011)}, /* Sierra Wireless Gobi 2000 Modem device (MC8305) */
+ {QMI_FIXED_INTF(0x1199, 0x9011, 5)}, /* alternate interface number!? */
{QMI_GOBI_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
{QMI_GOBI_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
{QMI_GOBI_DEVICE(0x1199, 0x9013)}, /* Sierra Wireless Gobi 3000 Modem device (MC8355) */
{QMI_GOBI_DEVICE(0x1199, 0x9015)}, /* Sierra Wireless Gobi 3000 Modem device */
{QMI_GOBI_DEVICE(0x1199, 0x9019)}, /* Sierra Wireless Gobi 3000 Modem device */
+ {QMI_GOBI_DEVICE(0x1199, 0x901b)}, /* Sierra Wireless MC7770 */
+
{ } /* END */
};
MODULE_DEVICE_TABLE(usb, products);
diff --git a/drivers/net/usb/sierra_net.c b/drivers/net/usb/sierra_net.c
index d75d1f5..7be49ea 100644
--- a/drivers/net/usb/sierra_net.c
+++ b/drivers/net/usb/sierra_net.c
@@ -68,15 +68,8 @@
*/
#define SIERRA_NET_USBCTL_BUF_LEN 1024
-/* list of interface numbers - used for constructing interface lists */
-struct sierra_net_iface_info {
- const u32 infolen; /* number of interface numbers on list */
- const u8 *ifaceinfo; /* pointer to the array holding the numbers */
-};
-
struct sierra_net_info_data {
u16 rx_urb_size;
- struct sierra_net_iface_info whitelist;
};
/* Private data structure */
@@ -637,21 +630,6 @@
return usbnet_change_mtu(net, new_mtu);
}
-static int is_whitelisted(const u8 ifnum,
- const struct sierra_net_iface_info *whitelist)
-{
- if (whitelist) {
- const u8 *list = whitelist->ifaceinfo;
- int i;
-
- for (i = 0; i < whitelist->infolen; i++) {
- if (list[i] == ifnum)
- return 1;
- }
- }
- return 0;
-}
-
static int sierra_net_get_fw_attr(struct usbnet *dev, u16 *datap)
{
int result = 0;
@@ -706,11 +684,6 @@
dev_dbg(&dev->udev->dev, "%s", __func__);
ifacenum = intf->cur_altsetting->desc.bInterfaceNumber;
- /* We only accept certain interfaces */
- if (!is_whitelisted(ifacenum, &data->whitelist)) {
- dev_dbg(&dev->udev->dev, "Ignoring interface: %d", ifacenum);
- return -ENODEV;
- }
numendpoints = intf->cur_altsetting->desc.bNumEndpoints;
/* We have three endpoints, bulk in and out, and a status */
if (numendpoints != 3) {
@@ -945,13 +918,8 @@
return NULL;
}
-static const u8 sierra_net_ifnum_list[] = { 7, 10, 11 };
static const struct sierra_net_info_data sierra_net_info_data_direct_ip = {
.rx_urb_size = 8 * 1024,
- .whitelist = {
- .infolen = ARRAY_SIZE(sierra_net_ifnum_list),
- .ifaceinfo = sierra_net_ifnum_list
- }
};
static const struct driver_info sierra_net_info_direct_ip = {
@@ -965,15 +933,19 @@
.data = (unsigned long)&sierra_net_info_data_direct_ip,
};
+#define DIRECT_IP_DEVICE(vend, prod) \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 7), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}, \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 10), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}, \
+ {USB_DEVICE_INTERFACE_NUMBER(vend, prod, 11), \
+ .driver_info = (unsigned long)&sierra_net_info_direct_ip}
+
static const struct usb_device_id products[] = {
- {USB_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x0F3D, 0x68A3), /* AT&T Direct IP modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x1199, 0x68AA), /* Sierra Wireless Direct IP LTE modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
- {USB_DEVICE(0x0F3D, 0x68AA), /* AT&T Direct IP LTE modem */
- .driver_info = (unsigned long) &sierra_net_info_direct_ip},
+ DIRECT_IP_DEVICE(0x1199, 0x68A3), /* Sierra Wireless USB-to-WWAN modem */
+ DIRECT_IP_DEVICE(0x0F3D, 0x68A3), /* AT&T Direct IP modem */
+ DIRECT_IP_DEVICE(0x1199, 0x68AA), /* Sierra Wireless Direct IP LTE modem */
+ DIRECT_IP_DEVICE(0x0F3D, 0x68AA), /* AT&T Direct IP LTE modem */
{}, /* last item */
};
diff --git a/drivers/net/vmxnet3/vmxnet3_drv.c b/drivers/net/vmxnet3/vmxnet3_drv.c
index 93e0cfb..ce9d4f2 100644
--- a/drivers/net/vmxnet3/vmxnet3_drv.c
+++ b/drivers/net/vmxnet3/vmxnet3_drv.c
@@ -3019,6 +3019,7 @@
netdev->watchdog_timeo = 5 * HZ;
INIT_WORK(&adapter->work, vmxnet3_reset_work);
+ set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
if (adapter->intr.type == VMXNET3_IT_MSIX) {
int i;
@@ -3043,7 +3044,6 @@
goto err_register;
}
- set_bit(VMXNET3_STATE_BIT_QUIESCED, &adapter->state);
vmxnet3_check_link(adapter, false);
atomic_inc(&devices_found);
return 0;
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 9eb6479..ef36caf 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -774,14 +774,15 @@
}
/* Global interrupt queue */
writel((u32)(((IRQ_RING_SIZE >> 5) - 1) << 20), ioaddr + IQLENR1);
+
+ rc = -ENOMEM;
+
priv->iqcfg = (__le32 *) pci_alloc_consistent(pdev,
IRQ_RING_SIZE*sizeof(__le32), &priv->iqcfg_dma);
if (!priv->iqcfg)
goto err_free_irq_5;
writel(priv->iqcfg_dma, ioaddr + IQCFG);
- rc = -ENOMEM;
-
/*
* SCC 0-3 private rx/tx irq structures
* IQRX/TXi needs to be set soon. Learned it the hard way...
diff --git a/drivers/net/wimax/i2400m/fw.c b/drivers/net/wimax/i2400m/fw.c
index 283237f..def12b3 100644
--- a/drivers/net/wimax/i2400m/fw.c
+++ b/drivers/net/wimax/i2400m/fw.c
@@ -326,8 +326,10 @@
unsigned barker;
options_orig = kstrdup(_options, GFP_KERNEL);
- if (options_orig == NULL)
+ if (options_orig == NULL) {
+ result = -ENOMEM;
goto error_parse;
+ }
options = options_orig;
while ((token = strsep(&options, ",")) != NULL) {
diff --git a/drivers/net/wireless/at76c50x-usb.c b/drivers/net/wireless/at76c50x-usb.c
index efc162e..88b8d64 100644
--- a/drivers/net/wireless/at76c50x-usb.c
+++ b/drivers/net/wireless/at76c50x-usb.c
@@ -342,7 +342,7 @@
return ret;
}
-static u8 at76_dfu_get_state(struct usb_device *udev, u8 *state)
+static int at76_dfu_get_state(struct usb_device *udev, u8 *state)
{
int ret;
diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c
index 8c4c040..2aab20e 100644
--- a/drivers/net/wireless/ath/ath5k/base.c
+++ b/drivers/net/wireless/ath/ath5k/base.c
@@ -2056,9 +2056,7 @@
void
ath5k_beacon_config(struct ath5k_hw *ah)
{
- unsigned long flags;
-
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ah->bmisscount = 0;
ah->imask &= ~(AR5K_INT_BMISS | AR5K_INT_SWBA);
@@ -2085,7 +2083,7 @@
ath5k_hw_set_imr(ah, ah->imask);
mmiowb();
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
static void ath5k_tasklet_beacon(unsigned long data)
diff --git a/drivers/net/wireless/ath/ath5k/mac80211-ops.c b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
index 260e7dc..d56453e 100644
--- a/drivers/net/wireless/ath/ath5k/mac80211-ops.c
+++ b/drivers/net/wireless/ath/ath5k/mac80211-ops.c
@@ -254,7 +254,6 @@
struct ath5k_vif *avf = (void *)vif->drv_priv;
struct ath5k_hw *ah = hw->priv;
struct ath_common *common = ath5k_hw_common(ah);
- unsigned long flags;
mutex_lock(&ah->lock);
@@ -300,9 +299,9 @@
}
if (changes & BSS_CHANGED_BEACON) {
- spin_lock_irqsave(&ah->block, flags);
+ spin_lock_bh(&ah->block);
ath5k_beacon_update(hw, vif);
- spin_unlock_irqrestore(&ah->block, flags);
+ spin_unlock_bh(&ah->block);
}
if (changes & BSS_CHANGED_BEACON_ENABLED)
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index cfa91ab..60b6a9d 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -730,6 +730,7 @@
case AR9300_DEVID_QCA955X:
case AR9300_DEVID_AR9580:
case AR9300_DEVID_AR9462:
+ case AR9485_DEVID_AR1111:
break;
default:
if (common->bus_ops->ath_bus_type == ATH_USB)
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index dd0c146..ce7332c 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -49,6 +49,7 @@
#define AR9300_DEVID_AR9462 0x0034
#define AR9300_DEVID_AR9330 0x0035
#define AR9300_DEVID_QCA955X 0x0038
+#define AR9485_DEVID_AR1111 0x0037
#define AR5416_AR9100_DEVID 0x000b
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 7990cd5..b42be91 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -773,15 +773,10 @@
}
EXPORT_SYMBOL(ath9k_hw_intrpend);
-void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+void ath9k_hw_kill_interrupts(struct ath_hw *ah)
{
struct ath_common *common = ath9k_hw_common(ah);
- if (!(ah->imask & ATH9K_INT_GLOBAL))
- atomic_set(&ah->intr_ref_cnt, -1);
- else
- atomic_dec(&ah->intr_ref_cnt);
-
ath_dbg(common, INTERRUPT, "disable IER\n");
REG_WRITE(ah, AR_IER, AR_IER_DISABLE);
(void) REG_READ(ah, AR_IER);
@@ -793,6 +788,17 @@
(void) REG_READ(ah, AR_INTR_SYNC_ENABLE);
}
}
+EXPORT_SYMBOL(ath9k_hw_kill_interrupts);
+
+void ath9k_hw_disable_interrupts(struct ath_hw *ah)
+{
+ if (!(ah->imask & ATH9K_INT_GLOBAL))
+ atomic_set(&ah->intr_ref_cnt, -1);
+ else
+ atomic_dec(&ah->intr_ref_cnt);
+
+ ath9k_hw_kill_interrupts(ah);
+}
EXPORT_SYMBOL(ath9k_hw_disable_interrupts);
void ath9k_hw_enable_interrupts(struct ath_hw *ah)
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index 0eba36d..4a745e6 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -738,6 +738,7 @@
void ath9k_hw_set_interrupts(struct ath_hw *ah);
void ath9k_hw_enable_interrupts(struct ath_hw *ah);
void ath9k_hw_disable_interrupts(struct ath_hw *ah);
+void ath9k_hw_kill_interrupts(struct ath_hw *ah);
void ar9002_hw_attach_mac_ops(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 6049d8b..a22df74 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -462,8 +462,10 @@
if (!ath9k_hw_intrpend(ah))
return IRQ_NONE;
- if(test_bit(SC_OP_HW_RESET, &sc->sc_flags))
+ if (test_bit(SC_OP_HW_RESET, &sc->sc_flags)) {
+ ath9k_hw_kill_interrupts(ah);
return IRQ_HANDLED;
+ }
/*
* Figure out the reason(s) for the interrupt. Note
diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c
index 87b89d5..a978984 100644
--- a/drivers/net/wireless/ath/ath9k/pci.c
+++ b/drivers/net/wireless/ath/ath9k/pci.c
@@ -37,6 +37,7 @@
{ PCI_VDEVICE(ATHEROS, 0x0032) }, /* PCI-E AR9485 */
{ PCI_VDEVICE(ATHEROS, 0x0033) }, /* PCI-E AR9580 */
{ PCI_VDEVICE(ATHEROS, 0x0034) }, /* PCI-E AR9462 */
+ { PCI_VDEVICE(ATHEROS, 0x0037) }, /* PCI-E AR1111/AR9485 */
{ 0 }
};
@@ -320,6 +321,7 @@
* Otherwise the chip never moved to full sleep,
* when no interface is up.
*/
+ ath9k_stop_btcoex(sc);
ath9k_hw_disable(sc->sc_ah);
ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP);
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 12aca02..4480c0c 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1044,7 +1044,6 @@
struct ieee80211_hw *hw = sc->hw;
struct ieee80211_hdr *hdr;
int retval;
- bool decrypt_error = false;
struct ath_rx_status rs;
enum ath9k_rx_qtype qtype;
bool edma = !!(ah->caps.hw_caps & ATH9K_HW_CAP_EDMA);
@@ -1066,6 +1065,7 @@
tsf_lower = tsf & 0xffffffff;
do {
+ bool decrypt_error = false;
/* If handling rx interrupt and flush is in progress => exit */
if (test_bit(SC_OP_RXFLUSH, &sc->sc_flags) && (flush == 0))
break;
diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c
index b80352b..a140165 100644
--- a/drivers/net/wireless/b43/main.c
+++ b/drivers/net/wireless/b43/main.c
@@ -2719,32 +2719,37 @@
if (dev->dev->chip_id == 0x4301) {
mask |= 0x0060;
set |= 0x0060;
+ } else if (dev->dev->chip_id == 0x5354) {
+ /* Don't allow overtaking buttons GPIOs */
+ set &= 0x2; /* 0x2 is LED GPIO on BCM5354 */
}
- if (dev->dev->chip_id == 0x5354)
- set &= 0xff02;
+
if (0 /* FIXME: conditional unknown */ ) {
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0100);
- mask |= 0x0180;
- set |= 0x0180;
+ /* BT Coexistance Input */
+ mask |= 0x0080;
+ set |= 0x0080;
+ /* BT Coexistance Out */
+ mask |= 0x0100;
+ set |= 0x0100;
}
if (dev->dev->bus_sprom->boardflags_lo & B43_BFL_PACTRL) {
+ /* PA is controlled by gpio 9, let ucode handle it */
b43_write16(dev, B43_MMIO_GPIO_MASK,
b43_read16(dev, B43_MMIO_GPIO_MASK)
| 0x0200);
mask |= 0x0200;
set |= 0x0200;
}
- if (dev->dev->core_rev >= 2)
- mask |= 0x0010; /* FIXME: This is redundant. */
switch (dev->dev->bus_type) {
#ifdef CONFIG_B43_BCMA
case B43_BUS_BCMA:
bcma_cc_write32(&dev->dev->bdev->bus->drv_cc, BCMA_CC_GPIOCTL,
(bcma_cc_read32(&dev->dev->bdev->bus->drv_cc,
- BCMA_CC_GPIOCTL) & mask) | set);
+ BCMA_CC_GPIOCTL) & ~mask) | set);
break;
#endif
#ifdef CONFIG_B43_SSB
@@ -2753,7 +2758,7 @@
if (gpiodev)
ssb_write32(gpiodev, B43_GPIO_CONTROL,
(ssb_read32(gpiodev, B43_GPIO_CONTROL)
- & mask) | set);
+ & ~mask) | set);
break;
#endif
}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
index 9a4c63f..7ed7d75 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/channel.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -382,9 +382,7 @@
{
struct brcms_c_info *wlc = wlc_cm->wlc;
struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.channel;
- const struct ieee80211_reg_rule *reg_rule;
struct txpwr_limits txpwr;
- int ret;
brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
@@ -393,8 +391,7 @@
);
/* set or restore gmode as required by regulatory */
- ret = freq_reg_info(wlc->wiphy, ch->center_freq, 0, ®_rule);
- if (!ret && (reg_rule->flags & NL80211_RRF_NO_OFDM))
+ if (ch->flags & IEEE80211_CHAN_NO_OFDM)
brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
else
brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
index 9e79d47..192ad5c 100644
--- a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -121,7 +121,8 @@
IEEE80211_CHAN_NO_HT40PLUS),
CHAN2GHZ(14, 2484,
IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
- IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+ IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS |
+ IEEE80211_CHAN_NO_OFDM)
};
static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
diff --git a/drivers/net/wireless/iwlwifi/dvm/rs.c b/drivers/net/wireless/iwlwifi/dvm/rs.c
index 6fddd27..a82f46c1 100644
--- a/drivers/net/wireless/iwlwifi/dvm/rs.c
+++ b/drivers/net/wireless/iwlwifi/dvm/rs.c
@@ -707,11 +707,14 @@
*/
static bool rs_use_green(struct ieee80211_sta *sta)
{
- struct iwl_station_priv *sta_priv = (void *)sta->drv_priv;
- struct iwl_rxon_context *ctx = sta_priv->ctx;
-
- return (sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD) &&
- !(ctx->ht.non_gf_sta_present);
+ /*
+ * There's a bug somewhere in this code that causes the
+ * scaling to get stuck because GF+SGI can't be combined
+ * in SISO rates. Until we find that bug, disable GF, it
+ * has only limited benefit and we still interoperate with
+ * GF APs since we can always receive GF transmissions.
+ */
+ return false;
}
/**
diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index eb5de80..1c10b54 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -1254,6 +1254,7 @@
netif_tx_wake_all_queues(priv->dev);
}
+ kfree(cmd);
done:
lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
return ret;
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 76caeba..e970897 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -1314,6 +1314,7 @@
kfree(packet);
}
+ kfree(card);
lbs_deb_leave(LBS_DEB_SDIO);
}
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 5804818..fe1ea43 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -571,7 +571,10 @@
netdev_info(dev, "Timeout submitting command 0x%04x\n",
le16_to_cpu(cmdnode->cmdbuf->command));
lbs_complete_command(priv, cmdnode, -ETIMEDOUT);
- if (priv->reset_card)
+
+ /* Reset card, but only when it isn't in the process
+ * of being shutdown anyway. */
+ if (!dev->dismantle && priv->reset_card)
priv->reset_card(priv);
}
priv->cmd_timed_out = 0;
diff --git a/drivers/net/wireless/p54/p54usb.c b/drivers/net/wireless/p54/p54usb.c
index 7f207b6..effb044 100644
--- a/drivers/net/wireless/p54/p54usb.c
+++ b/drivers/net/wireless/p54/p54usb.c
@@ -42,7 +42,7 @@
* whenever you add a new device.
*/
-static struct usb_device_id p54u_table[] __devinitdata = {
+static struct usb_device_id p54u_table[] = {
/* Version 1 devices (pci chip + net2280) */
{USB_DEVICE(0x0411, 0x0050)}, /* Buffalo WLI2-USB2-G54 */
{USB_DEVICE(0x045e, 0x00c2)}, /* Microsoft MN-710 */
diff --git a/drivers/net/wireless/rndis_wlan.c b/drivers/net/wireless/rndis_wlan.c
index 241162e..7a4ae9e 100644
--- a/drivers/net/wireless/rndis_wlan.c
+++ b/drivers/net/wireless/rndis_wlan.c
@@ -1803,6 +1803,7 @@
struct cfg80211_pmksa *pmksa,
int max_pmkids)
{
+ struct ndis_80211_pmkid *new_pmkids;
int i, err, newlen;
unsigned int count;
@@ -1833,11 +1834,12 @@
/* add new pmkid */
newlen = sizeof(*pmkids) + (count + 1) * sizeof(pmkids->bssid_info[0]);
- pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
- if (!pmkids) {
+ new_pmkids = krealloc(pmkids, newlen, GFP_KERNEL);
+ if (!new_pmkids) {
err = -ENOMEM;
goto error;
}
+ pmkids = new_pmkids;
pmkids->length = cpu_to_le32(newlen);
pmkids->bssid_info_count = cpu_to_le32(count + 1);
diff --git a/drivers/net/wireless/rt2x00/rt2800lib.c b/drivers/net/wireless/rt2x00/rt2800lib.c
index 88455b1..cb8c2ac 100644
--- a/drivers/net/wireless/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/rt2x00/rt2800lib.c
@@ -221,6 +221,67 @@
mutex_unlock(&rt2x00dev->csr_mutex);
}
+static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
+{
+ u32 reg;
+ int i, count;
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®);
+ if (rt2x00_get_field32(reg, WLAN_EN))
+ return 0;
+
+ rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
+ rt2x00_set_field32(®, FRC_WL_ANT_SET, 1);
+ rt2x00_set_field32(®, WLAN_CLK_EN, 0);
+ rt2x00_set_field32(®, WLAN_EN, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+
+ udelay(REGISTER_BUSY_DELAY);
+
+ count = 0;
+ do {
+ /*
+ * Check PLL_LD & XTAL_RDY.
+ */
+ for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
+ rt2800_register_read(rt2x00dev, CMB_CTRL, ®);
+ if (rt2x00_get_field32(reg, PLL_LD) &&
+ rt2x00_get_field32(reg, XTAL_RDY))
+ break;
+ udelay(REGISTER_BUSY_DELAY);
+ }
+
+ if (i >= REGISTER_BUSY_COUNT) {
+
+ if (count >= 10)
+ return -EIO;
+
+ rt2800_register_write(rt2x00dev, 0x58, 0x018);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x418);
+ udelay(REGISTER_BUSY_DELAY);
+ rt2800_register_write(rt2x00dev, 0x58, 0x618);
+ udelay(REGISTER_BUSY_DELAY);
+ count++;
+ } else {
+ count = 0;
+ }
+
+ rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®);
+ rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0);
+ rt2x00_set_field32(®, WLAN_CLK_EN, 1);
+ rt2x00_set_field32(®, WLAN_RESET, 1);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2x00_set_field32(®, WLAN_RESET, 0);
+ rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
+ udelay(10);
+ rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
+ } while (count != 0);
+
+ return 0;
+}
+
void rt2800_mcu_request(struct rt2x00_dev *rt2x00dev,
const u8 command, const u8 token,
const u8 arg0, const u8 arg1)
@@ -400,6 +461,13 @@
{
unsigned int i;
u32 reg;
+ int retval;
+
+ if (rt2x00_rt(rt2x00dev, RT3290)) {
+ retval = rt2800_enable_wlan_rt3290(rt2x00dev);
+ if (retval)
+ return -EBUSY;
+ }
/*
* If driver doesn't wake up firmware here,
diff --git a/drivers/net/wireless/rt2x00/rt2800pci.c b/drivers/net/wireless/rt2x00/rt2800pci.c
index 235376e..98aa426 100644
--- a/drivers/net/wireless/rt2x00/rt2800pci.c
+++ b/drivers/net/wireless/rt2x00/rt2800pci.c
@@ -980,66 +980,6 @@
return rt2800_validate_eeprom(rt2x00dev);
}
-static int rt2800_enable_wlan_rt3290(struct rt2x00_dev *rt2x00dev)
-{
- u32 reg;
- int i, count;
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®);
- if (rt2x00_get_field32(reg, WLAN_EN))
- return 0;
-
- rt2x00_set_field32(®, WLAN_GPIO_OUT_OE_BIT_ALL, 0xff);
- rt2x00_set_field32(®, FRC_WL_ANT_SET, 1);
- rt2x00_set_field32(®, WLAN_CLK_EN, 0);
- rt2x00_set_field32(®, WLAN_EN, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
-
- udelay(REGISTER_BUSY_DELAY);
-
- count = 0;
- do {
- /*
- * Check PLL_LD & XTAL_RDY.
- */
- for (i = 0; i < REGISTER_BUSY_COUNT; i++) {
- rt2800_register_read(rt2x00dev, CMB_CTRL, ®);
- if (rt2x00_get_field32(reg, PLL_LD) &&
- rt2x00_get_field32(reg, XTAL_RDY))
- break;
- udelay(REGISTER_BUSY_DELAY);
- }
-
- if (i >= REGISTER_BUSY_COUNT) {
-
- if (count >= 10)
- return -EIO;
-
- rt2800_register_write(rt2x00dev, 0x58, 0x018);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x418);
- udelay(REGISTER_BUSY_DELAY);
- rt2800_register_write(rt2x00dev, 0x58, 0x618);
- udelay(REGISTER_BUSY_DELAY);
- count++;
- } else {
- count = 0;
- }
-
- rt2800_register_read(rt2x00dev, WLAN_FUN_CTRL, ®);
- rt2x00_set_field32(®, PCIE_APP0_CLK_REQ, 0);
- rt2x00_set_field32(®, WLAN_CLK_EN, 1);
- rt2x00_set_field32(®, WLAN_RESET, 1);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2x00_set_field32(®, WLAN_RESET, 0);
- rt2800_register_write(rt2x00dev, WLAN_FUN_CTRL, reg);
- udelay(10);
- rt2800_register_write(rt2x00dev, INT_SOURCE_CSR, 0x7fffffff);
- } while (count != 0);
-
- return 0;
-}
static int rt2800pci_probe_hw(struct rt2x00_dev *rt2x00dev)
{
int retval;
@@ -1063,17 +1003,6 @@
return retval;
/*
- * In probe phase call rt2800_enable_wlan_rt3290 to enable wlan
- * clk for rt3290. That avoid the MCU fail in start phase.
- */
- if (rt2x00_rt(rt2x00dev, RT3290)) {
- retval = rt2800_enable_wlan_rt3290(rt2x00dev);
-
- if (retval)
- return retval;
- }
-
- /*
* This device has multiple filters for control frames
* and has a separate filter for PS Poll frames.
*/
diff --git a/drivers/net/wireless/rt2x00/rt61pci.c b/drivers/net/wireless/rt2x00/rt61pci.c
index f322596..3f7bc5c 100644
--- a/drivers/net/wireless/rt2x00/rt61pci.c
+++ b/drivers/net/wireless/rt2x00/rt61pci.c
@@ -2243,8 +2243,7 @@
static void rt61pci_wakeup(struct rt2x00_dev *rt2x00dev)
{
- struct ieee80211_conf conf = { .flags = 0 };
- struct rt2x00lib_conf libconf = { .conf = &conf };
+ struct rt2x00lib_conf libconf = { .conf = &rt2x00dev->hw->conf };
rt61pci_config(rt2x00dev, &libconf, IEEE80211_CONF_CHANGE_PS);
}
diff --git a/drivers/net/wireless/rtl818x/rtl8187/dev.c b/drivers/net/wireless/rtl818x/rtl8187/dev.c
index 71a30b0..5330240 100644
--- a/drivers/net/wireless/rtl818x/rtl8187/dev.c
+++ b/drivers/net/wireless/rtl818x/rtl8187/dev.c
@@ -44,7 +44,7 @@
MODULE_DESCRIPTION("RTL8187/RTL8187B USB wireless driver");
MODULE_LICENSE("GPL");
-static struct usb_device_id rtl8187_table[] __devinitdata = {
+static struct usb_device_id rtl8187_table[] = {
/* Asus */
{USB_DEVICE(0x0b05, 0x171d), .driver_info = DEVICE_RTL8187},
/* Belkin */
diff --git a/drivers/of/base.c b/drivers/of/base.c
index c181b94..d4a1c9a 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -364,6 +364,33 @@
EXPORT_SYMBOL(of_get_next_child);
/**
+ * of_get_next_available_child - Find the next available child node
+ * @node: parent node
+ * @prev: previous child of the parent node, or NULL to get first
+ *
+ * This function is like of_get_next_child(), except that it
+ * automatically skips any disabled nodes (i.e. status = "disabled").
+ */
+struct device_node *of_get_next_available_child(const struct device_node *node,
+ struct device_node *prev)
+{
+ struct device_node *next;
+
+ read_lock(&devtree_lock);
+ next = prev ? prev->sibling : node->child;
+ for (; next; next = next->sibling) {
+ if (!of_device_is_available(next))
+ continue;
+ if (of_node_get(next))
+ break;
+ }
+ of_node_put(prev);
+ read_unlock(&devtree_lock);
+ return next;
+}
+EXPORT_SYMBOL(of_get_next_available_child);
+
+/**
* of_find_node_by_path - Find a node matching a full OF path
* @path: The full path to match
*
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index fbf7b26..c5792d6 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -266,8 +266,8 @@
}
if (!error)
- dev_printk(KERN_INFO, &dev->dev,
- "power state changed by ACPI to D%d\n", state);
+ dev_info(&dev->dev, "power state changed by ACPI to %s\n",
+ pci_power_name(state));
return error;
}
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 185be37..5270f1a 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -959,6 +959,13 @@
if (!pci_dev->state_saved && !pci_is_bridge(pci_dev))
pci_prepare_to_sleep(pci_dev);
+ /*
+ * The reason for doing this here is the same as for the analogous code
+ * in pci_pm_suspend_noirq().
+ */
+ if (pci_dev->class == PCI_CLASS_SERIAL_USB_EHCI)
+ pci_write_config_word(pci_dev, PCI_COMMAND, 0);
+
return 0;
}
diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c
index fb7f3be..dc5c126 100644
--- a/drivers/pinctrl/core.c
+++ b/drivers/pinctrl/core.c
@@ -657,11 +657,7 @@
if (p != NULL)
return ERR_PTR(-EBUSY);
- p = create_pinctrl(dev);
- if (IS_ERR(p))
- return p;
-
- return p;
+ return create_pinctrl(dev);
}
/**
@@ -738,11 +734,8 @@
dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
name);
state = create_state(p, name);
- if (IS_ERR(state))
- return state;
- } else {
- return ERR_PTR(-ENODEV);
- }
+ } else
+ state = ERR_PTR(-ENODEV);
}
return state;
diff --git a/drivers/pinctrl/pinctrl-imx23.c b/drivers/pinctrl/pinctrl-imx23.c
index 75d3eff..3674d87 100644
--- a/drivers/pinctrl/pinctrl-imx23.c
+++ b/drivers/pinctrl/pinctrl-imx23.c
@@ -292,7 +292,7 @@
{
return platform_driver_register(&imx23_pinctrl_driver);
}
-arch_initcall(imx23_pinctrl_init);
+postcore_initcall(imx23_pinctrl_init);
static void __exit imx23_pinctrl_exit(void)
{
diff --git a/drivers/pinctrl/pinctrl-imx28.c b/drivers/pinctrl/pinctrl-imx28.c
index b973026..0f5b212 100644
--- a/drivers/pinctrl/pinctrl-imx28.c
+++ b/drivers/pinctrl/pinctrl-imx28.c
@@ -408,7 +408,7 @@
{
return platform_driver_register(&imx28_pinctrl_driver);
}
-arch_initcall(imx28_pinctrl_init);
+postcore_initcall(imx28_pinctrl_init);
static void __exit imx28_pinctrl_exit(void)
{
diff --git a/drivers/pinctrl/pinctrl-imx51.c b/drivers/pinctrl/pinctrl-imx51.c
index 689b3c8..9fd0216 100644
--- a/drivers/pinctrl/pinctrl-imx51.c
+++ b/drivers/pinctrl/pinctrl-imx51.c
@@ -974,7 +974,7 @@
IMX_PIN_REG(MX51_PAD_EIM_DA13, NO_PAD, 0x050, 0, 0x000, 0), /* MX51_PAD_EIM_DA13__EIM_DA13 */
IMX_PIN_REG(MX51_PAD_EIM_DA14, NO_PAD, 0x054, 0, 0x000, 0), /* MX51_PAD_EIM_DA14__EIM_DA14 */
IMX_PIN_REG(MX51_PAD_EIM_DA15, NO_PAD, 0x058, 0, 0x000, 0), /* MX51_PAD_EIM_DA15__EIM_DA15 */
- IMX_PIN_REG(MX51_PAD_SD2_CMD, NO_PAD, 0x3b4, 2, 0x91c, 3), /* MX51_PAD_SD2_CMD__CSPI_MOSI */
+ IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 2, 0x91c, 3), /* MX51_PAD_SD2_CMD__CSPI_MOSI */
IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 1, 0x9b0, 2), /* MX51_PAD_SD2_CMD__I2C1_SCL */
IMX_PIN_REG(MX51_PAD_SD2_CMD, 0x7bc, 0x3b4, 0, 0x000, 0), /* MX51_PAD_SD2_CMD__SD2_CMD */
IMX_PIN_REG(MX51_PAD_SD2_CLK, 0x7c0, 0x3b8, 2, 0x914, 3), /* MX51_PAD_SD2_CLK__CSPI_SCLK */
diff --git a/drivers/pinctrl/pinctrl-nomadik-db8500.c b/drivers/pinctrl/pinctrl-nomadik-db8500.c
index 6f99769..a39fb7a 100644
--- a/drivers/pinctrl/pinctrl-nomadik-db8500.c
+++ b/drivers/pinctrl/pinctrl-nomadik-db8500.c
@@ -505,6 +505,8 @@
DB8500_PIN_J3, DB8500_PIN_H2, DB8500_PIN_J2, DB8500_PIN_H1,
DB8500_PIN_F4, DB8500_PIN_E3, DB8500_PIN_E4, DB8500_PIN_D2,
DB8500_PIN_C1, DB8500_PIN_D3, DB8500_PIN_C2, DB8500_PIN_D5 };
+static const unsigned kp_b_2_pins[] = { DB8500_PIN_F3, DB8500_PIN_F1,
+ DB8500_PIN_G3, DB8500_PIN_G2, DB8500_PIN_F4, DB8500_PIN_E3};
static const unsigned sm_b_1_pins[] = { DB8500_PIN_C6, DB8500_PIN_B3,
DB8500_PIN_C4, DB8500_PIN_E6, DB8500_PIN_A3, DB8500_PIN_B6,
DB8500_PIN_D6, DB8500_PIN_B7, DB8500_PIN_D7, DB8500_PIN_D8,
@@ -662,6 +664,7 @@
DB8500_PIN_GROUP(spi3_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(msp1txrx_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(kp_b_1, NMK_GPIO_ALT_B),
+ DB8500_PIN_GROUP(kp_b_2, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(sm_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(smcs0_b_1, NMK_GPIO_ALT_B),
DB8500_PIN_GROUP(smcs1_b_1, NMK_GPIO_ALT_B),
@@ -751,7 +754,7 @@
DB8500_FUNC_GROUPS(lcdb, "lcdb_a_1");
DB8500_FUNC_GROUPS(lcd, "lcdvsi0_a_1", "lcdvsi1_a_1", "lcd_d0_d7_a_1",
"lcd_d8_d11_a_1", "lcd_d12_d23_a_1", "lcd_b_1");
-DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_c_1", "kp_oc1_1");
+DB8500_FUNC_GROUPS(kp, "kp_a_1", "kp_b_1", "kp_b_2", "kp_c_1", "kp_oc1_1");
DB8500_FUNC_GROUPS(mc2, "mc2_a_1", "mc2rstn_c_1");
DB8500_FUNC_GROUPS(ssp1, "ssp1_a_1");
DB8500_FUNC_GROUPS(ssp0, "ssp0_a_1");
@@ -766,7 +769,7 @@
DB8500_FUNC_GROUPS(msp2, "msp2sck_a_1", "msp2_a_1");
DB8500_FUNC_GROUPS(mc4, "mc4_a_1", "mc4rstn_c_1");
DB8500_FUNC_GROUPS(mc1, "mc1_a_1", "mc1dir_a_1");
-DB8500_FUNC_GROUPS(hsi, "hsir1_a_1", "hsit1_a_1", "hsit_a_2");
+DB8500_FUNC_GROUPS(hsi, "hsir_a_1", "hsit_a_1", "hsit_a_2");
DB8500_FUNC_GROUPS(clkout, "clkout_a_1", "clkout_a_2", "clkout_c_1");
DB8500_FUNC_GROUPS(usb, "usb_a_1");
DB8500_FUNC_GROUPS(trig, "trig_b_1");
diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c
index 53b0d49..3dde653 100644
--- a/drivers/pinctrl/pinctrl-nomadik.c
+++ b/drivers/pinctrl/pinctrl-nomadik.c
@@ -1292,7 +1292,7 @@
NOMADIK_GPIO_TO_IRQ(pdata->first_gpio),
0, &nmk_gpio_irq_simple_ops, nmk_chip);
if (!nmk_chip->domain) {
- pr_err("%s: Failed to create irqdomain\n", np->full_name);
+ dev_err(&dev->dev, "failed to create irqdomain\n");
ret = -ENOSYS;
goto out;
}
@@ -1731,7 +1731,6 @@
for (i = 0; i < npct->soc->gpio_num_ranges; i++) {
if (!nmk_gpio_chips[i]) {
dev_warn(&pdev->dev, "GPIO chip %d not registered yet\n", i);
- devm_kfree(&pdev->dev, npct);
return -EPROBE_DEFER;
}
npct->soc->gpio_ranges[i].gc = &nmk_gpio_chips[i]->chip;
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 2aae8a8..7fca6ce 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -1217,7 +1217,6 @@
iounmap(spmx->gpio_virtbase);
out_no_gpio_remap:
platform_set_drvdata(pdev, NULL);
- devm_kfree(&pdev->dev, spmx);
return ret;
}
diff --git a/drivers/pinctrl/pinctrl-u300.c b/drivers/pinctrl/pinctrl-u300.c
index a7ad8c1..309f5b9 100644
--- a/drivers/pinctrl/pinctrl-u300.c
+++ b/drivers/pinctrl/pinctrl-u300.c
@@ -1121,10 +1121,8 @@
upmx->dev = &pdev->dev;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENOENT;
- goto out_no_resource;
- }
+ if (!res)
+ return -ENOENT;
upmx->phybase = res->start;
upmx->physize = resource_size(res);
@@ -1165,8 +1163,6 @@
platform_set_drvdata(pdev, NULL);
out_no_memregion:
release_mem_region(upmx->phybase, upmx->physize);
-out_no_resource:
- devm_kfree(&pdev->dev, upmx);
return ret;
}
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index 2a262f5..c86bae8 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -289,6 +289,7 @@
tristate "Lenovo IdeaPad Laptop Extras"
depends on ACPI
depends on RFKILL && INPUT
+ depends on SERIO_I8042
select INPUT_SPARSEKMAP
help
This is a driver for the rfkill switches on Lenovo IdeaPad netbooks.
@@ -758,8 +759,11 @@
config APPLE_GMUX
tristate "Apple Gmux Driver"
+ depends on ACPI
depends on PNP
- select BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_CLASS_DEVICE
+ depends on BACKLIGHT_APPLE=n || BACKLIGHT_APPLE
+ depends on ACPI_VIDEO=n || ACPI_VIDEO
---help---
This driver provides support for the gmux device found on many
Apple laptops, which controls the display mux for the hybrid
diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c
index 905fa01..dfb1a92 100644
--- a/drivers/platform/x86/apple-gmux.c
+++ b/drivers/platform/x86/apple-gmux.c
@@ -2,6 +2,7 @@
* Gmux driver for Apple laptops
*
* Copyright (C) Canonical Ltd. <seth.forshee@canonical.com>
+ * Copyright (C) 2010-2012 Andreas Heider <andreas@meetr.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -18,16 +19,30 @@
#include <linux/pnp.h>
#include <linux/apple_bl.h>
#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/vga_switcheroo.h>
#include <acpi/video.h>
#include <asm/io.h>
struct apple_gmux_data {
unsigned long iostart;
unsigned long iolen;
+ bool indexed;
+ struct mutex index_lock;
struct backlight_device *bdev;
+
+ /* switcheroo data */
+ acpi_handle dhandle;
+ int gpe;
+ enum vga_switcheroo_client_id resume_client_id;
+ enum vga_switcheroo_state power_state;
+ struct completion powerchange_done;
};
+static struct apple_gmux_data *apple_gmux_data;
+
/*
* gmux port offsets. Many of these are not yet used, but may be in the
* future, and it's useful to have them documented here anyhow.
@@ -45,6 +60,9 @@
#define GMUX_PORT_DISCRETE_POWER 0x50
#define GMUX_PORT_MAX_BRIGHTNESS 0x70
#define GMUX_PORT_BRIGHTNESS 0x74
+#define GMUX_PORT_VALUE 0xc2
+#define GMUX_PORT_READ 0xd0
+#define GMUX_PORT_WRITE 0xd4
#define GMUX_MIN_IO_LEN (GMUX_PORT_BRIGHTNESS + 4)
@@ -59,22 +77,172 @@
#define GMUX_BRIGHTNESS_MASK 0x00ffffff
#define GMUX_MAX_BRIGHTNESS GMUX_BRIGHTNESS_MASK
-static inline u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+static u8 gmux_pio_read8(struct apple_gmux_data *gmux_data, int port)
{
return inb(gmux_data->iostart + port);
}
-static inline void gmux_write8(struct apple_gmux_data *gmux_data, int port,
+static void gmux_pio_write8(struct apple_gmux_data *gmux_data, int port,
u8 val)
{
outb(val, gmux_data->iostart + port);
}
-static inline u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+static u32 gmux_pio_read32(struct apple_gmux_data *gmux_data, int port)
{
return inl(gmux_data->iostart + port);
}
+static void gmux_pio_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, port + i);
+ }
+}
+
+static int gmux_index_wait_ready(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && (gwr & 0x01)) {
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ return !!i;
+}
+
+static int gmux_index_wait_complete(struct apple_gmux_data *gmux_data)
+{
+ int i = 200;
+ u8 gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+
+ while (i && !(gwr & 0x01)) {
+ gwr = inb(gmux_data->iostart + GMUX_PORT_WRITE);
+ udelay(100);
+ i--;
+ }
+
+ if (gwr & 0x01)
+ inb(gmux_data->iostart + GMUX_PORT_READ);
+
+ return !!i;
+}
+
+static u8 gmux_index_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ u8 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_ready(gmux_data);
+ val = inb(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write8(struct apple_gmux_data *gmux_data, int port,
+ u8 val)
+{
+ mutex_lock(&gmux_data->index_lock);
+ outb(val, gmux_data->iostart + GMUX_PORT_VALUE);
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u32 gmux_index_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ u32 val;
+
+ mutex_lock(&gmux_data->index_lock);
+ outb((port & 0xff), gmux_data->iostart + GMUX_PORT_READ);
+ gmux_index_wait_ready(gmux_data);
+ val = inl(gmux_data->iostart + GMUX_PORT_VALUE);
+ mutex_unlock(&gmux_data->index_lock);
+
+ return val;
+}
+
+static void gmux_index_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ int i;
+ u8 tmpval;
+
+ mutex_lock(&gmux_data->index_lock);
+
+ for (i = 0; i < 4; i++) {
+ tmpval = (val >> (i * 8)) & 0xff;
+ outb(tmpval, gmux_data->iostart + GMUX_PORT_VALUE + i);
+ }
+
+ gmux_index_wait_ready(gmux_data);
+ outb(port & 0xff, gmux_data->iostart + GMUX_PORT_WRITE);
+ gmux_index_wait_complete(gmux_data);
+ mutex_unlock(&gmux_data->index_lock);
+}
+
+static u8 gmux_read8(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read8(gmux_data, port);
+ else
+ return gmux_pio_read8(gmux_data, port);
+}
+
+static void gmux_write8(struct apple_gmux_data *gmux_data, int port, u8 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write8(gmux_data, port, val);
+ else
+ gmux_pio_write8(gmux_data, port, val);
+}
+
+static u32 gmux_read32(struct apple_gmux_data *gmux_data, int port)
+{
+ if (gmux_data->indexed)
+ return gmux_index_read32(gmux_data, port);
+ else
+ return gmux_pio_read32(gmux_data, port);
+}
+
+static void gmux_write32(struct apple_gmux_data *gmux_data, int port,
+ u32 val)
+{
+ if (gmux_data->indexed)
+ gmux_index_write32(gmux_data, port, val);
+ else
+ gmux_pio_write32(gmux_data, port, val);
+}
+
+static bool gmux_is_indexed(struct apple_gmux_data *gmux_data)
+{
+ u16 val;
+
+ outb(0xaa, gmux_data->iostart + 0xcc);
+ outb(0x55, gmux_data->iostart + 0xcd);
+ outb(0x00, gmux_data->iostart + 0xce);
+
+ val = inb(gmux_data->iostart + 0xcc) |
+ (inb(gmux_data->iostart + 0xcd) << 8);
+
+ if (val == 0x55aa)
+ return true;
+
+ return false;
+}
+
static int gmux_get_brightness(struct backlight_device *bd)
{
struct apple_gmux_data *gmux_data = bl_get_data(bd);
@@ -90,16 +258,7 @@
if (bd->props.state & BL_CORE_SUSPENDED)
return 0;
- /*
- * Older gmux versions require writing out lower bytes first then
- * setting the upper byte to 0 to flush the values. Newer versions
- * accept a single u32 write, but the old method also works, so we
- * just use the old method for all gmux versions.
- */
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 1, brightness >> 8);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 2, brightness >> 16);
- gmux_write8(gmux_data, GMUX_PORT_BRIGHTNESS + 3, 0);
+ gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness);
return 0;
}
@@ -110,6 +269,146 @@
.update_status = gmux_update_status,
};
+static int gmux_switchto(enum vga_switcheroo_client_id id)
+{
+ if (id == VGA_SWITCHEROO_IGD) {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 1);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 2);
+ } else {
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DDC, 2);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_DISPLAY, 3);
+ gmux_write8(apple_gmux_data, GMUX_PORT_SWITCH_EXTERNAL, 3);
+ }
+
+ return 0;
+}
+
+static int gmux_set_discrete_state(struct apple_gmux_data *gmux_data,
+ enum vga_switcheroo_state state)
+{
+ INIT_COMPLETION(gmux_data->powerchange_done);
+
+ if (state == VGA_SWITCHEROO_ON) {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 3);
+ pr_debug("Discrete card powered up\n");
+ } else {
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 1);
+ gmux_write8(gmux_data, GMUX_PORT_DISCRETE_POWER, 0);
+ pr_debug("Discrete card powered down\n");
+ }
+
+ gmux_data->power_state = state;
+
+ if (gmux_data->gpe >= 0 &&
+ !wait_for_completion_interruptible_timeout(&gmux_data->powerchange_done,
+ msecs_to_jiffies(200)))
+ pr_warn("Timeout waiting for gmux switch to complete\n");
+
+ return 0;
+}
+
+static int gmux_set_power_state(enum vga_switcheroo_client_id id,
+ enum vga_switcheroo_state state)
+{
+ if (id == VGA_SWITCHEROO_IGD)
+ return 0;
+
+ return gmux_set_discrete_state(apple_gmux_data, state);
+}
+
+static int gmux_get_client_id(struct pci_dev *pdev)
+{
+ /*
+ * Early Macbook Pros with switchable graphics use nvidia
+ * integrated graphics. Hardcode that the 9400M is integrated.
+ */
+ if (pdev->vendor == PCI_VENDOR_ID_INTEL)
+ return VGA_SWITCHEROO_IGD;
+ else if (pdev->vendor == PCI_VENDOR_ID_NVIDIA &&
+ pdev->device == 0x0863)
+ return VGA_SWITCHEROO_IGD;
+ else
+ return VGA_SWITCHEROO_DIS;
+}
+
+static enum vga_switcheroo_client_id
+gmux_active_client(struct apple_gmux_data *gmux_data)
+{
+ if (gmux_read8(gmux_data, GMUX_PORT_SWITCH_DISPLAY) == 2)
+ return VGA_SWITCHEROO_IGD;
+
+ return VGA_SWITCHEROO_DIS;
+}
+
+static struct vga_switcheroo_handler gmux_handler = {
+ .switchto = gmux_switchto,
+ .power_state = gmux_set_power_state,
+ .get_client_id = gmux_get_client_id,
+};
+
+static inline void gmux_disable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_DISABLE);
+}
+
+static inline void gmux_enable_interrupts(struct apple_gmux_data *gmux_data)
+{
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_ENABLE,
+ GMUX_INTERRUPT_ENABLE);
+}
+
+static inline u8 gmux_interrupt_get_status(struct apple_gmux_data *gmux_data)
+{
+ return gmux_read8(gmux_data, GMUX_PORT_INTERRUPT_STATUS);
+}
+
+static void gmux_clear_interrupts(struct apple_gmux_data *gmux_data)
+{
+ u8 status;
+
+ /* to clear interrupts write back current status */
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_write8(gmux_data, GMUX_PORT_INTERRUPT_STATUS, status);
+}
+
+static void gmux_notify_handler(acpi_handle device, u32 value, void *context)
+{
+ u8 status;
+ struct pnp_dev *pnp = (struct pnp_dev *)context;
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+
+ status = gmux_interrupt_get_status(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ pr_debug("Notify handler called: status %d\n", status);
+
+ gmux_clear_interrupts(gmux_data);
+ gmux_enable_interrupts(gmux_data);
+
+ if (status & GMUX_INTERRUPT_STATUS_POWER)
+ complete(&gmux_data->powerchange_done);
+}
+
+static int gmux_suspend(struct pnp_dev *pnp, pm_message_t state)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ gmux_data->resume_client_id = gmux_active_client(gmux_data);
+ gmux_disable_interrupts(gmux_data);
+ return 0;
+}
+
+static int gmux_resume(struct pnp_dev *pnp)
+{
+ struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ gmux_enable_interrupts(gmux_data);
+ gmux_switchto(gmux_data->resume_client_id);
+ if (gmux_data->power_state == VGA_SWITCHEROO_OFF)
+ gmux_set_discrete_state(gmux_data, gmux_data->power_state);
+ return 0;
+}
+
static int __devinit gmux_probe(struct pnp_dev *pnp,
const struct pnp_device_id *id)
{
@@ -119,6 +418,11 @@
struct backlight_device *bdev;
u8 ver_major, ver_minor, ver_release;
int ret = -ENXIO;
+ acpi_status status;
+ unsigned long long gpe;
+
+ if (apple_gmux_data)
+ return -EBUSY;
gmux_data = kzalloc(sizeof(*gmux_data), GFP_KERNEL);
if (!gmux_data)
@@ -147,22 +451,29 @@
}
/*
- * On some machines the gmux is in ACPI even thought the machine
- * doesn't really have a gmux. Check for invalid version information
- * to detect this.
+ * Invalid version information may indicate either that the gmux
+ * device isn't present or that it's a new one that uses indexed
+ * io
*/
+
ver_major = gmux_read8(gmux_data, GMUX_PORT_VERSION_MAJOR);
ver_minor = gmux_read8(gmux_data, GMUX_PORT_VERSION_MINOR);
ver_release = gmux_read8(gmux_data, GMUX_PORT_VERSION_RELEASE);
if (ver_major == 0xff && ver_minor == 0xff && ver_release == 0xff) {
- pr_info("gmux device not present\n");
- ret = -ENODEV;
- goto err_release;
+ if (gmux_is_indexed(gmux_data)) {
+ mutex_init(&gmux_data->index_lock);
+ gmux_data->indexed = true;
+ } else {
+ pr_info("gmux device not present\n");
+ ret = -ENODEV;
+ goto err_release;
+ }
+ pr_info("Found indexed gmux\n");
+ } else {
+ pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
+ ver_release);
}
- pr_info("Found gmux version %d.%d.%d\n", ver_major, ver_minor,
- ver_release);
-
memset(&props, 0, sizeof(props));
props.type = BACKLIGHT_PLATFORM;
props.max_brightness = gmux_read32(gmux_data, GMUX_PORT_MAX_BRIGHTNESS);
@@ -194,13 +505,67 @@
* Disable the other backlight choices.
*/
acpi_video_dmi_promote_vendor();
-#ifdef CONFIG_ACPI_VIDEO
+#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
acpi_video_unregister();
#endif
apple_bl_unregister();
+ gmux_data->power_state = VGA_SWITCHEROO_ON;
+
+ gmux_data->dhandle = DEVICE_ACPI_HANDLE(&pnp->dev);
+ if (!gmux_data->dhandle) {
+ pr_err("Cannot find acpi handle for pnp device %s\n",
+ dev_name(&pnp->dev));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_evaluate_integer(gmux_data->dhandle, "GMGP", NULL, &gpe);
+ if (ACPI_SUCCESS(status)) {
+ gmux_data->gpe = (int)gpe;
+
+ status = acpi_install_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler, pnp);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Install notify handler failed: %s\n",
+ acpi_format_exception(status));
+ ret = -ENODEV;
+ goto err_notify;
+ }
+
+ status = acpi_enable_gpe(NULL, gmux_data->gpe);
+ if (ACPI_FAILURE(status)) {
+ pr_err("Cannot enable gpe: %s\n",
+ acpi_format_exception(status));
+ goto err_enable_gpe;
+ }
+ } else {
+ pr_warn("No GPE found for gmux\n");
+ gmux_data->gpe = -1;
+ }
+
+ if (vga_switcheroo_register_handler(&gmux_handler)) {
+ ret = -ENODEV;
+ goto err_register_handler;
+ }
+
+ init_completion(&gmux_data->powerchange_done);
+ apple_gmux_data = gmux_data;
+ gmux_enable_interrupts(gmux_data);
+
return 0;
+err_register_handler:
+ if (gmux_data->gpe >= 0)
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+err_enable_gpe:
+ if (gmux_data->gpe >= 0)
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+err_notify:
+ backlight_device_unregister(bdev);
err_release:
release_region(gmux_data->iostart, gmux_data->iolen);
err_free:
@@ -212,12 +577,23 @@
{
struct apple_gmux_data *gmux_data = pnp_get_drvdata(pnp);
+ vga_switcheroo_unregister_handler();
+ gmux_disable_interrupts(gmux_data);
+ if (gmux_data->gpe >= 0) {
+ acpi_disable_gpe(NULL, gmux_data->gpe);
+ acpi_remove_notify_handler(gmux_data->dhandle,
+ ACPI_DEVICE_NOTIFY,
+ &gmux_notify_handler);
+ }
+
backlight_device_unregister(gmux_data->bdev);
+
release_region(gmux_data->iostart, gmux_data->iolen);
+ apple_gmux_data = NULL;
kfree(gmux_data);
acpi_video_dmi_demote_vendor();
-#ifdef CONFIG_ACPI_VIDEO
+#if defined (CONFIG_ACPI_VIDEO) || defined (CONFIG_ACPI_VIDEO_MODULE)
acpi_video_register();
#endif
apple_bl_register();
@@ -233,6 +609,8 @@
.probe = gmux_probe,
.remove = __devexit_p(gmux_remove),
.id_table = gmux_device_ids,
+ .suspend = gmux_suspend,
+ .resume = gmux_resume
};
static int __init apple_gmux_init(void)
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index c7a36f6..2eb9fe8 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -101,6 +101,7 @@
#define ASUS_WMI_DEVID_WIRELESS_LED 0x00010002
#define ASUS_WMI_DEVID_CWAP 0x00010003
#define ASUS_WMI_DEVID_WLAN 0x00010011
+#define ASUS_WMI_DEVID_WLAN_LED 0x00010012
#define ASUS_WMI_DEVID_BLUETOOTH 0x00010013
#define ASUS_WMI_DEVID_GPS 0x00010015
#define ASUS_WMI_DEVID_WIMAX 0x00010017
@@ -731,8 +732,21 @@
{
struct asus_rfkill *priv = data;
u32 ctrl_param = !blocked;
+ u32 dev_id = priv->dev_id;
- return asus_wmi_set_devstate(priv->dev_id, ctrl_param, NULL);
+ /*
+ * If the user bit is set, BIOS can't set and record the wlan status,
+ * it will report the value read from id ASUS_WMI_DEVID_WLAN_LED
+ * while we query the wlan status through WMI(ASUS_WMI_DEVID_WLAN).
+ * So, we have to record wlan status in id ASUS_WMI_DEVID_WLAN_LED
+ * while setting the wlan status through WMI.
+ * This is also the behavior that windows app will do.
+ */
+ if ((dev_id == ASUS_WMI_DEVID_WLAN) &&
+ priv->asus->driver->wlan_ctrl_by_user)
+ dev_id = ASUS_WMI_DEVID_WLAN_LED;
+
+ return asus_wmi_set_devstate(dev_id, ctrl_param, NULL);
}
static void asus_rfkill_query(struct rfkill *rfkill, void *data)
@@ -1653,6 +1667,7 @@
struct asus_wmi *asus;
acpi_status status;
int err;
+ u32 result;
asus = kzalloc(sizeof(struct asus_wmi), GFP_KERNEL);
if (!asus)
@@ -1711,6 +1726,10 @@
if (err)
goto fail_debugfs;
+ asus_wmi_get_devstate(asus, ASUS_WMI_DEVID_WLAN, &result);
+ if (result & (ASUS_WMI_DSTS_PRESENCE_BIT | ASUS_WMI_DSTS_USER_BIT))
+ asus->driver->wlan_ctrl_by_user = 1;
+
return 0;
fail_debugfs:
diff --git a/drivers/platform/x86/asus-wmi.h b/drivers/platform/x86/asus-wmi.h
index 9c1da8b..4c9bd38 100644
--- a/drivers/platform/x86/asus-wmi.h
+++ b/drivers/platform/x86/asus-wmi.h
@@ -46,6 +46,7 @@
struct asus_wmi_driver {
int brightness;
int panel_power;
+ int wlan_ctrl_by_user;
const char *name;
struct module *owner;
diff --git a/drivers/platform/x86/classmate-laptop.c b/drivers/platform/x86/classmate-laptop.c
index 2ca7dd1..c87ff16 100644
--- a/drivers/platform/x86/classmate-laptop.c
+++ b/drivers/platform/x86/classmate-laptop.c
@@ -350,6 +350,7 @@
inputdev->close = cmpc_accel_close_v4;
}
+#ifdef CONFIG_PM_SLEEP
static int cmpc_accel_suspend_v4(struct device *dev)
{
struct input_dev *inputdev;
@@ -384,6 +385,7 @@
return 0;
}
+#endif
static int cmpc_accel_add_v4(struct acpi_device *acpi)
{
@@ -723,8 +725,10 @@
struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
if (event == 0x81) {
- if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
}
}
@@ -737,8 +741,10 @@
set_bit(SW_TABLET_MODE, inputdev->swbit);
acpi = to_acpi_device(inputdev->dev.parent);
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
}
static int cmpc_tablet_add(struct acpi_device *acpi)
@@ -752,15 +758,19 @@
return cmpc_remove_acpi_notify_device(acpi);
}
+#ifdef CONFIG_PM_SLEEP
static int cmpc_tablet_resume(struct device *dev)
{
struct input_dev *inputdev = dev_get_drvdata(dev);
unsigned long long val = 0;
- if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val)))
+ if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
input_report_switch(inputdev, SW_TABLET_MODE, !val);
+ input_sync(inputdev);
+ }
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
index 4e96e8c..927c33a 100644
--- a/drivers/platform/x86/dell-laptop.c
+++ b/drivers/platform/x86/dell-laptop.c
@@ -211,7 +211,7 @@
.ident = "Dell Inspiron 5420",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5420"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5420"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -220,7 +220,7 @@
.ident = "Dell Inspiron 5520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5520"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5520"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -229,7 +229,7 @@
.ident = "Dell Inspiron 5720",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 5720"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 5720"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -238,7 +238,7 @@
.ident = "Dell Inspiron 7420",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7420"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7420"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -247,7 +247,7 @@
.ident = "Dell Inspiron 7520",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7520"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7520"),
},
.driver_data = &quirk_dell_vostro_v130,
},
@@ -256,7 +256,7 @@
.ident = "Dell Inspiron 7720",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Isnpiron 7720"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7720"),
},
.driver_data = &quirk_dell_vostro_v130,
},
diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c
index d2e4173..7acae3f 100644
--- a/drivers/platform/x86/fujitsu-tablet.c
+++ b/drivers/platform/x86/fujitsu-tablet.c
@@ -440,11 +440,13 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int acpi_fujitsu_resume(struct device *dev)
{
fujitsu_reset();
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(acpi_fujitsu_pm, NULL, acpi_fujitsu_resume);
diff --git a/drivers/platform/x86/hdaps.c b/drivers/platform/x86/hdaps.c
index d9ab6f6..777c7e3 100644
--- a/drivers/platform/x86/hdaps.c
+++ b/drivers/platform/x86/hdaps.c
@@ -305,10 +305,12 @@
return 0;
}
+#ifdef CONFIG_PM_SLEEP
static int hdaps_resume(struct device *dev)
{
return hdaps_device_init();
}
+#endif
static SIMPLE_DEV_PM_OPS(hdaps_pm, NULL, hdaps_resume);
diff --git a/drivers/platform/x86/hp_accel.c b/drivers/platform/x86/hp_accel.c
index f4d9115..6b9af98 100644
--- a/drivers/platform/x86/hp_accel.c
+++ b/drivers/platform/x86/hp_accel.c
@@ -352,7 +352,7 @@
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int lis3lv02d_suspend(struct device *dev)
{
/* make sure the device is off when we suspend */
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 17f6dfd..dae7abe 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -36,6 +36,7 @@
#include <linux/fb.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
+#include <linux/i8042.h>
#define IDEAPAD_RFKILL_DEV_NUM (3)
@@ -63,8 +64,11 @@
VPCCMD_R_3G,
VPCCMD_W_3G,
VPCCMD_R_ODD, /* 0x21 */
- VPCCMD_R_RF = 0x23,
+ VPCCMD_W_FAN,
+ VPCCMD_R_RF,
VPCCMD_W_RF,
+ VPCCMD_R_FAN = 0x2B,
+ VPCCMD_R_SPECIAL_BUTTONS = 0x31,
VPCCMD_W_BL_POWER = 0x33,
};
@@ -356,14 +360,46 @@
return -EINVAL;
ret = write_ec_cmd(ideapad_handle, VPCCMD_W_CAMERA, state);
if (ret < 0)
- return ret;
+ return -EIO;
return count;
}
static DEVICE_ATTR(camera_power, 0644, show_ideapad_cam, store_ideapad_cam);
+static ssize_t show_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long result;
+
+ if (read_ec_data(ideapad_handle, VPCCMD_R_FAN, &result))
+ return sprintf(buf, "-1\n");
+ return sprintf(buf, "%lu\n", result);
+}
+
+static ssize_t store_ideapad_fan(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int ret, state;
+
+ if (!count)
+ return 0;
+ if (sscanf(buf, "%i", &state) != 1)
+ return -EINVAL;
+ if (state < 0 || state > 4 || state == 3)
+ return -EINVAL;
+ ret = write_ec_cmd(ideapad_handle, VPCCMD_W_FAN, state);
+ if (ret < 0)
+ return -EIO;
+ return count;
+}
+
+static DEVICE_ATTR(fan_mode, 0644, show_ideapad_fan, store_ideapad_fan);
+
static struct attribute *ideapad_attributes[] = {
&dev_attr_camera_power.attr,
+ &dev_attr_fan_mode.attr,
NULL
};
@@ -377,7 +413,10 @@
if (attr == &dev_attr_camera_power.attr)
supported = test_bit(CFG_CAMERA_BIT, &(priv->cfg));
- else
+ else if (attr == &dev_attr_fan_mode.attr) {
+ unsigned long value;
+ supported = !read_ec_data(ideapad_handle, VPCCMD_R_FAN, &value);
+ } else
supported = true;
return supported ? attr->mode : 0;
@@ -518,9 +557,15 @@
*/
static const struct key_entry ideapad_keymap[] = {
{ KE_KEY, 6, { KEY_SWITCHVIDEOMODE } },
+ { KE_KEY, 7, { KEY_CAMERA } },
+ { KE_KEY, 11, { KEY_F16 } },
{ KE_KEY, 13, { KEY_WLAN } },
{ KE_KEY, 16, { KEY_PROG1 } },
{ KE_KEY, 17, { KEY_PROG2 } },
+ { KE_KEY, 64, { KEY_PROG3 } },
+ { KE_KEY, 65, { KEY_PROG4 } },
+ { KE_KEY, 66, { KEY_TOUCHPAD_OFF } },
+ { KE_KEY, 67, { KEY_TOUCHPAD_ON } },
{ KE_END, 0 },
};
@@ -587,6 +632,28 @@
ideapad_input_report(priv, 16);
}
+static void ideapad_check_special_buttons(struct ideapad_private *priv)
+{
+ unsigned long bit, value;
+
+ read_ec_data(ideapad_handle, VPCCMD_R_SPECIAL_BUTTONS, &value);
+
+ for (bit = 0; bit < 16; bit++) {
+ if (test_bit(bit, &value)) {
+ switch (bit) {
+ case 6:
+ /* Thermal Management button */
+ ideapad_input_report(priv, 65);
+ break;
+ case 1:
+ /* OneKey Theater button */
+ ideapad_input_report(priv, 64);
+ break;
+ }
+ }
+ }
+}
+
/*
* backlight
*/
@@ -691,6 +758,24 @@
};
MODULE_DEVICE_TABLE(acpi, ideapad_device_ids);
+static void ideapad_sync_touchpad_state(struct acpi_device *adevice)
+{
+ struct ideapad_private *priv = dev_get_drvdata(&adevice->dev);
+ unsigned long value;
+
+ /* Without reading from EC touchpad LED doesn't switch state */
+ if (!read_ec_data(adevice->handle, VPCCMD_R_TOUCHPAD, &value)) {
+ /* Some IdeaPads don't really turn off touchpad - they only
+ * switch the LED state. We (de)activate KBC AUX port to turn
+ * touchpad off and on. We send KEY_TOUCHPAD_OFF and
+ * KEY_TOUCHPAD_ON to not to get out of sync with LED */
+ unsigned char param;
+ i8042_command(¶m, value ? I8042_CMD_AUX_ENABLE :
+ I8042_CMD_AUX_DISABLE);
+ ideapad_input_report(priv, value ? 67 : 66);
+ }
+}
+
static int __devinit ideapad_acpi_add(struct acpi_device *adevice)
{
int ret, i;
@@ -727,6 +812,7 @@
priv->rfk[i] = NULL;
}
ideapad_sync_rfk_state(priv);
+ ideapad_sync_touchpad_state(adevice);
if (!acpi_video_backlight_support()) {
ret = ideapad_backlight_init(priv);
@@ -785,9 +871,14 @@
ideapad_sync_rfk_state(priv);
break;
case 13:
+ case 11:
+ case 7:
case 6:
ideapad_input_report(priv, vpc_bit);
break;
+ case 5:
+ ideapad_sync_touchpad_state(adevice);
+ break;
case 4:
ideapad_backlight_notify_brightness(priv);
break;
@@ -797,6 +888,9 @@
case 2:
ideapad_backlight_notify_power(priv);
break;
+ case 0:
+ ideapad_check_special_buttons(priv);
+ break;
default:
pr_info("Unknown event: %lu\n", vpc_bit);
}
@@ -804,6 +898,15 @@
}
}
+static int ideapad_acpi_resume(struct device *device)
+{
+ ideapad_sync_rfk_state(ideapad_priv);
+ ideapad_sync_touchpad_state(to_acpi_device(device));
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ideapad_pm, NULL, ideapad_acpi_resume);
+
static struct acpi_driver ideapad_acpi_driver = {
.name = "ideapad_acpi",
.class = "IdeaPad",
@@ -811,6 +914,7 @@
.ops.add = ideapad_acpi_add,
.ops.remove = ideapad_acpi_remove,
.ops.notify = ideapad_acpi_notify,
+ .drv.pm = &ideapad_pm,
.owner = THIS_MODULE,
};
diff --git a/drivers/platform/x86/msi-laptop.c b/drivers/platform/x86/msi-laptop.c
index f644418..2111dbb 100644
--- a/drivers/platform/x86/msi-laptop.c
+++ b/drivers/platform/x86/msi-laptop.c
@@ -85,7 +85,9 @@
#define MSI_STANDARD_EC_TOUCHPAD_ADDRESS 0xe4
#define MSI_STANDARD_EC_TOUCHPAD_MASK (1 << 4)
+#ifdef CONFIG_PM_SLEEP
static int msi_laptop_resume(struct device *device);
+#endif
static SIMPLE_DEV_PM_OPS(msi_laptop_pm, NULL, msi_laptop_resume);
#define MSI_STANDARD_EC_DEVICES_EXISTS_ADDRESS 0x2f
@@ -753,6 +755,7 @@
return retval;
}
+#ifdef CONFIG_PM_SLEEP
static int msi_laptop_resume(struct device *device)
{
u8 data;
@@ -773,6 +776,7 @@
return 0;
}
+#endif
static int __init msi_laptop_input_setup(void)
{
diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c
index 2448007..8e8caa7 100644
--- a/drivers/platform/x86/panasonic-laptop.c
+++ b/drivers/platform/x86/panasonic-laptop.c
@@ -188,7 +188,9 @@
};
MODULE_DEVICE_TABLE(acpi, pcc_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int acpi_pcc_hotkey_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(acpi_pcc_hotkey_pm, NULL, acpi_pcc_hotkey_resume);
static struct acpi_driver acpi_pcc_driver = {
@@ -540,6 +542,7 @@
/* kernel module interface */
+#ifdef CONFIG_PM_SLEEP
static int acpi_pcc_hotkey_resume(struct device *dev)
{
struct pcc_acpi *pcc;
@@ -556,6 +559,7 @@
return acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, pcc->sticky_mode);
}
+#endif
static int acpi_pcc_hotkey_add(struct acpi_device *device)
{
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c
index 9363969..daaddec 100644
--- a/drivers/platform/x86/sony-laptop.c
+++ b/drivers/platform/x86/sony-laptop.c
@@ -140,7 +140,10 @@
"1 for 30 seconds, 2 for 60 seconds and 3 to disable timeout "
"(default: 0)");
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_kbd_backlight_resume(void);
+static void sony_nc_thermal_resume(void);
+#endif
static int sony_nc_kbd_backlight_setup(struct platform_device *pd,
unsigned int handle);
static void sony_nc_kbd_backlight_cleanup(struct platform_device *pd);
@@ -151,7 +154,6 @@
static int sony_nc_thermal_setup(struct platform_device *pd);
static void sony_nc_thermal_cleanup(struct platform_device *pd);
-static void sony_nc_thermal_resume(void);
static int sony_nc_lid_resume_setup(struct platform_device *pd);
static void sony_nc_lid_resume_cleanup(struct platform_device *pd);
@@ -1431,6 +1433,7 @@
sony_nc_handles_cleanup(pd);
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_function_resume(void)
{
unsigned int i, result, bitmask, arg;
@@ -1508,6 +1511,7 @@
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(sony_nc_pm, NULL, sony_nc_resume);
@@ -1872,6 +1876,7 @@
}
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_kbd_backlight_resume(void)
{
int ignore = 0;
@@ -1888,6 +1893,7 @@
(kbdbl_ctl->base + 0x200) |
(kbdbl_ctl->timeout << 0x10), &ignore);
}
+#endif
struct battery_care_control {
struct device_attribute attrs[2];
@@ -2210,6 +2216,7 @@
}
}
+#ifdef CONFIG_PM_SLEEP
static void sony_nc_thermal_resume(void)
{
unsigned int status = sony_nc_thermal_mode_get();
@@ -2217,6 +2224,7 @@
if (status != th_handle->mode)
sony_nc_thermal_mode_set(th_handle->mode);
}
+#endif
/* resume on LID open */
struct snc_lid_resume_control {
@@ -4287,6 +4295,7 @@
return result;
}
+#ifdef CONFIG_PM_SLEEP
static int sony_pic_suspend(struct device *dev)
{
if (sony_pic_disable(to_acpi_device(dev)))
@@ -4300,6 +4309,7 @@
spic_dev.cur_ioport, spic_dev.cur_irq);
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(sony_pic_pm, sony_pic_suspend, sony_pic_resume);
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index e7f7328..80e3779 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -922,6 +922,7 @@
static struct mutex tpacpi_inputdev_send_mutex;
static LIST_HEAD(tpacpi_all_drivers);
+#ifdef CONFIG_PM_SLEEP
static int tpacpi_suspend_handler(struct device *dev)
{
struct ibm_struct *ibm, *itmp;
@@ -949,6 +950,7 @@
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(tpacpi_pm,
tpacpi_suspend_handler, tpacpi_resume_handler);
@@ -8662,6 +8664,13 @@
tp->model_str = kstrdup(s, GFP_KERNEL);
if (!tp->model_str)
return -ENOMEM;
+ } else {
+ s = dmi_get_system_info(DMI_BIOS_VENDOR);
+ if (s && !(strnicmp(s, "Lenovo", 6))) {
+ tp->model_str = kstrdup(s, GFP_KERNEL);
+ if (!tp->model_str)
+ return -ENOMEM;
+ }
}
s = dmi_get_system_info(DMI_PRODUCT_NAME);
diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
index c13ba5b..5f1256d 100644
--- a/drivers/platform/x86/toshiba_acpi.c
+++ b/drivers/platform/x86/toshiba_acpi.c
@@ -1296,6 +1296,7 @@
}
}
+#ifdef CONFIG_PM_SLEEP
static int toshiba_acpi_suspend(struct device *device)
{
struct toshiba_acpi_dev *dev = acpi_driver_data(to_acpi_device(device));
@@ -1317,6 +1318,7 @@
return 0;
}
+#endif
static SIMPLE_DEV_PM_OPS(toshiba_acpi_pm,
toshiba_acpi_suspend, toshiba_acpi_resume);
diff --git a/drivers/platform/x86/toshiba_bluetooth.c b/drivers/platform/x86/toshiba_bluetooth.c
index 715a43c..5e5d631 100644
--- a/drivers/platform/x86/toshiba_bluetooth.c
+++ b/drivers/platform/x86/toshiba_bluetooth.c
@@ -41,7 +41,9 @@
};
MODULE_DEVICE_TABLE(acpi, bt_device_ids);
+#ifdef CONFIG_PM_SLEEP
static int toshiba_bt_resume(struct device *dev);
+#endif
static SIMPLE_DEV_PM_OPS(toshiba_bt_pm, NULL, toshiba_bt_resume);
static struct acpi_driver toshiba_bt_rfkill_driver = {
@@ -90,10 +92,12 @@
toshiba_bluetooth_enable(device->handle);
}
+#ifdef CONFIG_PM_SLEEP
static int toshiba_bt_resume(struct device *dev)
{
return toshiba_bluetooth_enable(to_acpi_device(dev)->handle);
}
+#endif
static int toshiba_bt_rfkill_add(struct acpi_device *device)
{
diff --git a/drivers/platform/x86/xo15-ebook.c b/drivers/platform/x86/xo15-ebook.c
index 849c07c..38ba39d 100644
--- a/drivers/platform/x86/xo15-ebook.c
+++ b/drivers/platform/x86/xo15-ebook.c
@@ -77,10 +77,12 @@
}
}
+#ifdef CONFIG_PM_SLEEP
static int ebook_switch_resume(struct device *dev)
{
return ebook_send_state(to_acpi_device(dev));
}
+#endif
static SIMPLE_DEV_PM_OPS(ebook_switch_pm, NULL, ebook_switch_resume);
diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
index 8fc3808..90c5c73 100644
--- a/drivers/pwm/Kconfig
+++ b/drivers/pwm/Kconfig
@@ -1,12 +1,31 @@
menuconfig PWM
- bool "PWM Support"
+ bool "Pulse-Width Modulation (PWM) Support"
depends on !MACH_JZ4740 && !PUV3_PWM
help
- This enables PWM support through the generic PWM framework.
- You only need to enable this, if you also want to enable
- one or more of the PWM drivers below.
+ Generic Pulse-Width Modulation (PWM) support.
- If unsure, say N.
+ In Pulse-Width Modulation, a variation of the width of pulses
+ in a rectangular pulse signal is used as a means to alter the
+ average power of the signal. Applications include efficient
+ power delivery and voltage regulation. In computer systems,
+ PWMs are commonly used to control fans or the brightness of
+ display backlights.
+
+ This framework provides a generic interface to PWM devices
+ within the Linux kernel. On the driver side it provides an API
+ to register and unregister a PWM chip, an abstraction of a PWM
+ controller, that supports one or more PWM devices. Client
+ drivers can request PWM devices and use the generic framework
+ to configure as well as enable and disable them.
+
+ This generic framework replaces the legacy PWM framework which
+ allows only a single driver implementing the required API. Not
+ all legacy implementations have been ported to the framework
+ yet. The framework provides an API that is backward compatible
+ with the legacy framework so that existing client drivers
+ continue to work as expected.
+
+ If unsure, say no.
if PWM
diff --git a/drivers/pwm/core.c b/drivers/pwm/core.c
index ecb7690..c6e0507 100644
--- a/drivers/pwm/core.c
+++ b/drivers/pwm/core.c
@@ -129,8 +129,8 @@
return 0;
}
-static struct pwm_device *of_pwm_simple_xlate(struct pwm_chip *pc,
- const struct of_phandle_args *args)
+static struct pwm_device *
+of_pwm_simple_xlate(struct pwm_chip *pc, const struct of_phandle_args *args)
{
struct pwm_device *pwm;
@@ -149,7 +149,7 @@
return pwm;
}
-void of_pwmchip_add(struct pwm_chip *chip)
+static void of_pwmchip_add(struct pwm_chip *chip)
{
if (!chip->dev || !chip->dev->of_node)
return;
@@ -162,7 +162,7 @@
of_node_get(chip->dev->of_node);
}
-void of_pwmchip_remove(struct pwm_chip *chip)
+static void of_pwmchip_remove(struct pwm_chip *chip)
{
if (chip->dev && chip->dev->of_node)
of_node_put(chip->dev->of_node);
@@ -527,7 +527,7 @@
struct pwm_device *pwm_get(struct device *dev, const char *con_id)
{
struct pwm_device *pwm = ERR_PTR(-EPROBE_DEFER);
- const char *dev_id = dev ? dev_name(dev): NULL;
+ const char *dev_id = dev ? dev_name(dev) : NULL;
struct pwm_chip *chip = NULL;
unsigned int index = 0;
unsigned int best = 0;
@@ -609,7 +609,7 @@
mutex_lock(&pwm_lock);
if (!test_and_clear_bit(PWMF_REQUESTED, &pwm->flags)) {
- pr_warning("PWM device already freed\n");
+ pr_warn("PWM device already freed\n");
goto out;
}
diff --git a/drivers/pwm/pwm-samsung.c b/drivers/pwm/pwm-samsung.c
index d103865..e5187c0 100644
--- a/drivers/pwm/pwm-samsung.c
+++ b/drivers/pwm/pwm-samsung.c
@@ -225,6 +225,7 @@
/* calculate base of control bits in TCON */
s3c->tcon_base = id == 0 ? 0 : (id * 4) + 4;
+ s3c->chip.dev = &pdev->dev;
s3c->chip.ops = &s3c_pwm_ops;
s3c->chip.base = -1;
s3c->chip.npwm = 1;
diff --git a/drivers/pwm/pwm-tegra.c b/drivers/pwm/pwm-tegra.c
index 02ce18d..057465e 100644
--- a/drivers/pwm/pwm-tegra.c
+++ b/drivers/pwm/pwm-tegra.c
@@ -187,10 +187,8 @@
}
pwm->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pwm->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() region\n");
+ if (!pwm->mmio_base)
return -EADDRNOTAVAIL;
- }
platform_set_drvdata(pdev, pwm);
diff --git a/drivers/pwm/pwm-tiecap.c b/drivers/pwm/pwm-tiecap.c
index 3c2ad28..0b66d0f 100644
--- a/drivers/pwm/pwm-tiecap.c
+++ b/drivers/pwm/pwm-tiecap.c
@@ -192,10 +192,8 @@
}
pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pc->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ if (!pc->mmio_base)
return -EADDRNOTAVAIL;
- }
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-tiehrpwm.c b/drivers/pwm/pwm-tiehrpwm.c
index 010d232..c3756d1 100644
--- a/drivers/pwm/pwm-tiehrpwm.c
+++ b/drivers/pwm/pwm-tiehrpwm.c
@@ -371,10 +371,8 @@
}
pc->mmio_base = devm_request_and_ioremap(&pdev->dev, r);
- if (!pc->mmio_base) {
- dev_err(&pdev->dev, "failed to ioremap() registers\n");
+ if (!pc->mmio_base)
return -EADDRNOTAVAIL;
- }
ret = pwmchip_add(&pc->chip);
if (ret < 0) {
diff --git a/drivers/pwm/pwm-vt8500.c b/drivers/pwm/pwm-vt8500.c
index 5480214..ad14389 100644
--- a/drivers/pwm/pwm-vt8500.c
+++ b/drivers/pwm/pwm-vt8500.c
@@ -41,7 +41,7 @@
cpu_relax();
if (unlikely(!loops))
- pr_warning("Waiting for status bits 0x%x to clear timed out\n",
+ pr_warn("Waiting for status bits 0x%x to clear timed out\n",
bitmask);
}
diff --git a/drivers/rapidio/devices/tsi721.c b/drivers/rapidio/devices/tsi721.c
index 722246c..5d44252 100644
--- a/drivers/rapidio/devices/tsi721.c
+++ b/drivers/rapidio/devices/tsi721.c
@@ -435,6 +435,9 @@
" info %4.4x\n", DBELL_SID(idb.bytes),
DBELL_TID(idb.bytes), DBELL_INF(idb.bytes));
}
+
+ wr_ptr = ioread32(priv->regs +
+ TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
}
iowrite32(rd_ptr & (IDB_QSIZE - 1),
@@ -445,6 +448,10 @@
regval |= TSI721_SR_CHINT_IDBQRCV;
iowrite32(regval,
priv->regs + TSI721_SR_CHINTE(IDB_QUEUE));
+
+ wr_ptr = ioread32(priv->regs + TSI721_IDQ_WP(IDB_QUEUE)) % IDB_QSIZE;
+ if (wr_ptr != rd_ptr)
+ schedule_work(&priv->idb_work);
}
/**
@@ -2212,7 +2219,7 @@
const struct pci_device_id *id)
{
struct tsi721_device *priv;
- int i, cap;
+ int cap;
int err;
u32 regval;
@@ -2232,12 +2239,15 @@
priv->pdev = pdev;
#ifdef DEBUG
+ {
+ int i;
for (i = 0; i <= PCI_STD_RESOURCE_END; i++) {
dev_dbg(&pdev->dev, "res[%d] @ 0x%llx (0x%lx, 0x%lx)\n",
i, (unsigned long long)pci_resource_start(pdev, i),
(unsigned long)pci_resource_len(pdev, i),
pci_resource_flags(pdev, i));
}
+ }
#endif
/*
* Verify BAR configuration
diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c
index 182b553..c151fd5 100644
--- a/drivers/regulator/ab3100.c
+++ b/drivers/regulator/ab3100.c
@@ -486,6 +486,7 @@
.id = AB3100_BUCK,
.ops = ®ulator_ops_variable_sleepable,
.n_voltages = ARRAY_SIZE(ldo_e_buck_typ_voltages),
+ .volt_table = ldo_e_buck_typ_voltages,
.type = REGULATOR_VOLTAGE,
.owner = THIS_MODULE,
.enable_time = 1000,
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index e9c2085..ce0fe72 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -64,14 +64,15 @@
static int anatop_get_voltage_sel(struct regulator_dev *reg)
{
struct anatop_regulator *anatop_reg = rdev_get_drvdata(reg);
- u32 val;
+ u32 val, mask;
if (!anatop_reg->control_reg)
return -ENOTSUPP;
val = anatop_read_reg(anatop_reg->mfd, anatop_reg->control_reg);
- val = (val & ((1 << anatop_reg->vol_bit_width) - 1)) >>
+ mask = ((1 << anatop_reg->vol_bit_width) - 1) <<
anatop_reg->vol_bit_shift;
+ val = (val & mask) >> anatop_reg->vol_bit_shift;
return val - anatop_reg->min_bit_val;
}
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f092588..4838531 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -3217,7 +3217,7 @@
dev_set_drvdata(&rdev->dev, rdev);
- if (config->ena_gpio) {
+ if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) {
ret = gpio_request_one(config->ena_gpio,
GPIOF_DIR_OUT | config->ena_gpio_flags,
rdev_get_name(rdev));
diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c
index 34b67be..8b5944f 100644
--- a/drivers/regulator/gpio-regulator.c
+++ b/drivers/regulator/gpio-regulator.c
@@ -57,16 +57,17 @@
return -EINVAL;
}
-static int gpio_regulator_set_value(struct regulator_dev *dev,
- int min, int max, unsigned *selector)
+static int gpio_regulator_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV,
+ unsigned *selector)
{
struct gpio_regulator_data *data = rdev_get_drvdata(dev);
int ptr, target = 0, state, best_val = INT_MAX;
for (ptr = 0; ptr < data->nr_states; ptr++)
if (data->states[ptr].value < best_val &&
- data->states[ptr].value >= min &&
- data->states[ptr].value <= max) {
+ data->states[ptr].value >= min_uV &&
+ data->states[ptr].value <= max_uV) {
target = data->states[ptr].gpios;
best_val = data->states[ptr].value;
if (selector)
@@ -85,13 +86,6 @@
return 0;
}
-static int gpio_regulator_set_voltage(struct regulator_dev *dev,
- int min_uV, int max_uV,
- unsigned *selector)
-{
- return gpio_regulator_set_value(dev, min_uV, max_uV, selector);
-}
-
static int gpio_regulator_list_voltage(struct regulator_dev *dev,
unsigned selector)
{
@@ -106,7 +100,27 @@
static int gpio_regulator_set_current_limit(struct regulator_dev *dev,
int min_uA, int max_uA)
{
- return gpio_regulator_set_value(dev, min_uA, max_uA, NULL);
+ struct gpio_regulator_data *data = rdev_get_drvdata(dev);
+ int ptr, target = 0, state, best_val = 0;
+
+ for (ptr = 0; ptr < data->nr_states; ptr++)
+ if (data->states[ptr].value > best_val &&
+ data->states[ptr].value >= min_uA &&
+ data->states[ptr].value <= max_uA) {
+ target = data->states[ptr].gpios;
+ best_val = data->states[ptr].value;
+ }
+
+ if (best_val == 0)
+ return -EINVAL;
+
+ for (ptr = 0; ptr < data->nr_gpios; ptr++) {
+ state = (target & (1 << ptr)) >> ptr;
+ gpio_set_value(data->gpios[ptr].gpio, state);
+ }
+ data->state = target;
+
+ return 0;
}
static struct regulator_ops gpio_regulator_voltage_ops = {
diff --git a/drivers/regulator/palmas-regulator.c b/drivers/regulator/palmas-regulator.c
index 17d19fb..46c7e88 100644
--- a/drivers/regulator/palmas-regulator.c
+++ b/drivers/regulator/palmas-regulator.c
@@ -486,9 +486,12 @@
{
int ret, voltage;
- ret = ((min_uV - 900000) / 50000) + 1;
- if (ret < 0)
- return ret;
+ if (min_uV == 0)
+ return 0;
+
+ if (min_uV < 900000)
+ min_uV = 900000;
+ ret = DIV_ROUND_UP(min_uV - 900000, 50000) + 1;
/* Map back into a voltage to verify we're still in bounds */
voltage = palmas_list_voltage_ldo(rdev, ret);
@@ -586,7 +589,7 @@
addr = palmas_regs_info[id].ctrl_addr;
- ret = palmas_smps_read(palmas, addr, ®);
+ ret = palmas_ldo_read(palmas, addr, ®);
if (ret)
return ret;
@@ -596,7 +599,7 @@
if (reg_init->mode_sleep)
reg |= PALMAS_LDO1_CTRL_MODE_SLEEP;
- ret = palmas_smps_write(palmas, addr, reg);
+ ret = palmas_ldo_write(palmas, addr, reg);
if (ret)
return ret;
@@ -630,7 +633,7 @@
ret = palmas_smps_read(palmas, PALMAS_SMPS_CTRL, ®);
if (ret)
- goto err_unregister_regulator;
+ return ret;
if (reg & PALMAS_SMPS_CTRL_SMPS12_SMPS123_EN)
pmic->smps123 = 1;
@@ -676,7 +679,9 @@
case PALMAS_REG_SMPS10:
pmic->desc[id].n_voltages = PALMAS_SMPS10_NUM_VOLTAGES;
pmic->desc[id].ops = &palmas_ops_smps10;
- pmic->desc[id].vsel_reg = PALMAS_SMPS10_CTRL;
+ pmic->desc[id].vsel_reg =
+ PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
+ PALMAS_SMPS10_CTRL);
pmic->desc[id].vsel_mask = SMPS10_VSEL;
pmic->desc[id].enable_reg =
PALMAS_BASE_TO_REG(PALMAS_SMPS_BASE,
@@ -778,8 +783,10 @@
reg_init = pdata->reg_init[id];
if (reg_init) {
ret = palmas_ldo_init(palmas, id, reg_init);
- if (ret)
+ if (ret) {
+ regulator_unregister(pmic->rdev[id]);
goto err_unregister_regulator;
+ }
}
}
}
diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c
index e6da90a..19241fc 100644
--- a/drivers/regulator/tps6586x-regulator.c
+++ b/drivers/regulator/tps6586x-regulator.c
@@ -240,14 +240,16 @@
TPS6586X_LDO(LDO_9, "vinldo9", ldo, SUPPLYV6, 3, 3, ENE, 7, ENE, 7),
TPS6586X_LDO(LDO_RTC, NULL, ldo, SUPPLYV4, 3, 3, V4, 7, V4, 7),
TPS6586X_LDO(LDO_1, "vinldo01", dvm, SUPPLYV1, 0, 5, ENC, 1, END, 1),
- TPS6586X_LDO(SM_2, "sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
+ TPS6586X_LDO(SM_2, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
TPS6586X_DVM(LDO_2, "vinldo23", dvm, LDO2BV1, 0, 5, ENA, 3,
ENB, 3, VCC2, 6),
TPS6586X_DVM(LDO_4, "vinldo4", ldo4, LDO4V1, 0, 5, ENC, 3,
END, 3, VCC1, 6),
- TPS6586X_DVM(SM_0, "sm0", dvm, SM0V1, 0, 5, ENA, 1, ENB, 1, VCC1, 2),
- TPS6586X_DVM(SM_1, "sm1", dvm, SM1V1, 0, 5, ENA, 0, ENB, 0, VCC1, 0),
+ TPS6586X_DVM(SM_0, "vin-sm0", dvm, SM0V1, 0, 5, ENA, 1,
+ ENB, 1, VCC1, 2),
+ TPS6586X_DVM(SM_1, "vin-sm1", dvm, SM1V1, 0, 5, ENA, 0,
+ ENB, 0, VCC1, 0),
};
/*
diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c
index 242fe90..77a71a5 100644
--- a/drivers/regulator/twl-regulator.c
+++ b/drivers/regulator/twl-regulator.c
@@ -1037,7 +1037,7 @@
TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300);
TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300);
TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300);
-TWL4030_FIXED_LDO(VINTANA2, 0x3f, 1500, 11, 100, 0x08);
+TWL4030_FIXED_LDO(VINTANA1, 0x3f, 1500, 11, 100, 0x08);
TWL4030_FIXED_LDO(VINTDIG, 0x47, 1500, 13, 100, 0x08);
TWL4030_FIXED_LDO(VUSB1V5, 0x71, 1500, 17, 100, 0x08);
TWL4030_FIXED_LDO(VUSB1V8, 0x74, 1800, 18, 100, 0x08);
@@ -1048,7 +1048,6 @@
TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0);
TWL6030_FIXED_LDO(V1V8, 0x16, 1800, 0);
TWL6030_FIXED_LDO(V2V1, 0x1c, 2100, 0);
-TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0);
TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34);
TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10);
TWL6025_ADJUSTABLE_SMPS(VIO, 0x16);
@@ -1117,7 +1116,7 @@
TWL6025_OF_MATCH("ti,twl6025-ldo6", LDO6),
TWL6025_OF_MATCH("ti,twl6025-ldoln", LDOLN),
TWL6025_OF_MATCH("ti,twl6025-ldousb", LDOUSB),
- TWLFIXED_OF_MATCH("ti,twl4030-vintana2", VINTANA2),
+ TWLFIXED_OF_MATCH("ti,twl4030-vintana1", VINTANA1),
TWLFIXED_OF_MATCH("ti,twl4030-vintdig", VINTDIG),
TWLFIXED_OF_MATCH("ti,twl4030-vusb1v5", VUSB1V5),
TWLFIXED_OF_MATCH("ti,twl4030-vusb1v8", VUSB1V8),
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index eb415bd..9592b93 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -582,6 +582,7 @@
void rtc_update_irq(struct rtc_device *rtc,
unsigned long num, unsigned long events)
{
+ pm_stay_awake(rtc->dev.parent);
schedule_work(&rtc->irqwork);
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
@@ -844,6 +845,7 @@
mutex_lock(&rtc->ops_lock);
again:
+ pm_relax(rtc->dev.parent);
__rtc_read_time(rtc, &tm);
now = rtc_tm_to_ktime(tm);
while ((next = timerqueue_getnext(&rtc->timerqueue))) {
diff --git a/drivers/rtc/rtc-cmos.c b/drivers/rtc/rtc-cmos.c
index 132333d..4267789 100644
--- a/drivers/rtc/rtc-cmos.c
+++ b/drivers/rtc/rtc-cmos.c
@@ -568,7 +568,6 @@
hpet_mask_rtc_irq_bit(RTC_AIE);
CMOS_READ(RTC_INTR_FLAGS);
- pm_wakeup_event(cmos_rtc.dev, 0);
}
spin_unlock(&rtc_lock);
diff --git a/drivers/rtc/rtc-pcf2123.c b/drivers/rtc/rtc-pcf2123.c
index 8361187..13e4df6 100644
--- a/drivers/rtc/rtc-pcf2123.c
+++ b/drivers/rtc/rtc-pcf2123.c
@@ -43,6 +43,7 @@
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
+#include <linux/sysfs.h>
#define DRV_VERSION "0.6"
@@ -292,6 +293,7 @@
pdata->rtc = rtc;
for (i = 0; i < 16; i++) {
+ sysfs_attr_init(&pdata->regs[i].attr.attr);
sprintf(pdata->regs[i].name, "%1x", i);
pdata->regs[i].attr.attr.mode = S_IRUGO | S_IWUSR;
pdata->regs[i].attr.attr.name = pdata->regs[i].name;
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 77074cc..fd5c7af 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -122,9 +122,12 @@
tm->tm_min = bcd2bin(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
tm->tm_hour = bcd2bin(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
if (!pdata->rtc_24h) {
- tm->tm_hour %= 12;
- if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM)
+ if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM) {
+ tm->tm_hour -= 20;
+ tm->tm_hour %= 12;
tm->tm_hour += 12;
+ } else
+ tm->tm_hour %= 12;
}
tm->tm_wday = bcd2bin(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
tm->tm_mday = bcd2bin(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
diff --git a/drivers/s390/char/sclp_sdias.c b/drivers/s390/char/sclp_sdias.c
index 6a6f76b..b103293 100644
--- a/drivers/s390/char/sclp_sdias.c
+++ b/drivers/s390/char/sclp_sdias.c
@@ -242,11 +242,13 @@
switch (sdias_evbuf.event_status) {
case EVSTATE_ALL_STORED:
TRACE("all stored\n");
+ break;
case EVSTATE_PART_STORED:
TRACE("part stored: %i\n", sdias_evbuf.blk_cnt);
break;
case EVSTATE_NO_DATA:
TRACE("no data\n");
+ /* fall through */
default:
pr_err("Error from SCLP while copying hsa. "
"Event status = %x\n",
diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c
index 2374468..32c26d7 100644
--- a/drivers/sh/intc/core.c
+++ b/drivers/sh/intc/core.c
@@ -324,8 +324,16 @@
res = irq_create_identity_mapping(d->domain, irq);
if (unlikely(res)) {
- pr_err("can't get irq_desc for %d\n", irq);
- continue;
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain, irq, irq);
+ if (unlikely(res)) {
+ pr_err("domain association failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n", irq);
+ continue;
+ }
}
intc_irq_xlate_set(irq, vect->enum_id, d);
@@ -345,8 +353,19 @@
*/
res = irq_create_identity_mapping(d->domain, irq2);
if (unlikely(res)) {
- pr_err("can't get irq_desc for %d\n", irq2);
- continue;
+ if (res == -EEXIST) {
+ res = irq_domain_associate(d->domain,
+ irq, irq);
+ if (unlikely(res)) {
+ pr_err("domain association "
+ "failure\n");
+ continue;
+ }
+ } else {
+ pr_err("can't identity map IRQ %d\n",
+ irq);
+ continue;
+ }
}
vect2->enum_id = 0;
diff --git a/drivers/spi/spi-bcm63xx.c b/drivers/spi/spi-bcm63xx.c
index 6e25ef1..ea0aaa3 100644
--- a/drivers/spi/spi-bcm63xx.c
+++ b/drivers/spi/spi-bcm63xx.c
@@ -438,7 +438,7 @@
static int __devexit bcm63xx_spi_remove(struct platform_device *pdev)
{
- struct spi_master *master = platform_get_drvdata(pdev);
+ struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct bcm63xx_spi *bs = spi_master_get_devdata(master);
spi_unregister_master(master);
@@ -452,6 +452,8 @@
platform_set_drvdata(pdev, 0);
+ spi_master_put(master);
+
return 0;
}
diff --git a/drivers/spi/spi-coldfire-qspi.c b/drivers/spi/spi-coldfire-qspi.c
index b2d4b9e..764bfee 100644
--- a/drivers/spi/spi-coldfire-qspi.c
+++ b/drivers/spi/spi-coldfire-qspi.c
@@ -533,7 +533,6 @@
iounmap(mcfqspi->iobase);
release_mem_region(res->start, resource_size(res));
spi_unregister_master(master);
- spi_master_put(master);
return 0;
}
@@ -541,7 +540,7 @@
#ifdef CONFIG_PM_SLEEP
static int mcfqspi_suspend(struct device *dev)
{
- struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_suspend(master);
@@ -553,7 +552,7 @@
static int mcfqspi_resume(struct device *dev)
{
- struct spi_master *master = spi_master_get(dev_get_drvdata(dev));
+ struct spi_master *master = dev_get_drvdata(dev);
struct mcfqspi *mcfqspi = spi_master_get_devdata(master);
spi_master_resume(master);
diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c
index bc47781..b2fb141 100644
--- a/drivers/spi/spi-omap2-mcspi.c
+++ b/drivers/spi/spi-omap2-mcspi.c
@@ -1228,18 +1228,16 @@
status = spi_register_master(master);
if (status < 0)
- goto err_spi_register;
+ goto disable_pm;
return status;
-err_spi_register:
- spi_master_put(master);
disable_pm:
pm_runtime_disable(&pdev->dev);
dma_chnl_free:
kfree(mcspi->dma_channels);
free_master:
- kfree(master);
+ spi_master_put(master);
platform_set_drvdata(pdev, NULL);
return status;
}
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index aab518e..6abbe23 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -2053,7 +2053,6 @@
printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
adev->res.start, pl022->virtbase);
- pm_runtime_enable(dev);
pm_runtime_resume(dev);
pl022->clk = clk_get(&adev->dev, NULL);
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c
index 646a765..d1c8441f 100644
--- a/drivers/spi/spi-s3c64xx.c
+++ b/drivers/spi/spi-s3c64xx.c
@@ -826,7 +826,7 @@
struct spi_device *spi)
{
struct s3c64xx_spi_csinfo *cs;
- struct device_node *slave_np, *data_np;
+ struct device_node *slave_np, *data_np = NULL;
u32 fb_delay = 0;
slave_np = spi->dev.of_node;
@@ -1479,40 +1479,40 @@
s3c64xx_spi_runtime_resume, NULL)
};
-struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
+static struct s3c64xx_spi_port_config s3c2443_spi_port_config = {
.fifo_lvl_mask = { 0x7f },
.rx_lvl_offset = 13,
.tx_st_done = 21,
.high_speed = true,
};
-struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
+static struct s3c64xx_spi_port_config s3c6410_spi_port_config = {
.fifo_lvl_mask = { 0x7f, 0x7F },
.rx_lvl_offset = 13,
.tx_st_done = 21,
};
-struct s3c64xx_spi_port_config s5p64x0_spi_port_config = {
+static struct s3c64xx_spi_port_config s5p64x0_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
};
-struct s3c64xx_spi_port_config s5pc100_spi_port_config = {
+static struct s3c64xx_spi_port_config s5pc100_spi_port_config = {
.fifo_lvl_mask = { 0x7f, 0x7F },
.rx_lvl_offset = 13,
.tx_st_done = 21,
.high_speed = true,
};
-struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
+static struct s3c64xx_spi_port_config s5pv210_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
.high_speed = true,
};
-struct s3c64xx_spi_port_config exynos4_spi_port_config = {
+static struct s3c64xx_spi_port_config exynos4_spi_port_config = {
.fifo_lvl_mask = { 0x1ff, 0x7F, 0x7F },
.rx_lvl_offset = 15,
.tx_st_done = 25,
diff --git a/drivers/staging/ccg/Kconfig b/drivers/staging/ccg/Kconfig
index 1f00d70..df16665 100644
--- a/drivers/staging/ccg/Kconfig
+++ b/drivers/staging/ccg/Kconfig
@@ -17,4 +17,9 @@
Configurable Composite Gadget can be compiled "M" only
or not at all.
+ BIG FAT NOTE: DON'T RELY ON THIS USERINTERFACE HERE! AS PART
+ OF THE REWORK DONE HERE WILL BE A NEW USER INTERFACE WITHOUT ANY
+ COMPATIBILITY TO THIS SYSFS INTERFACE HERE. BE AWARE OF THIS
+ BEFORE SELECTING THIS.
+
endif # USB_GADGET
diff --git a/drivers/staging/ccg/Makefile b/drivers/staging/ccg/Makefile
index 693da63..814fa9d 100644
--- a/drivers/staging/ccg/Makefile
+++ b/drivers/staging/ccg/Makefile
@@ -1,4 +1,2 @@
g_ccg-y := ccg.o
-ccflags-y += -Idrivers/usb/gadget
-
obj-$(CONFIG_USB_G_CCG) += g_ccg.o
diff --git a/drivers/staging/ccg/ccg.c b/drivers/staging/ccg/ccg.c
index 6a7aab8..80feb95 100644
--- a/drivers/staging/ccg/ccg.c
+++ b/drivers/staging/ccg/ccg.c
@@ -32,7 +32,7 @@
#include <linux/platform_device.h>
#include <linux/usb/ch9.h>
-#include <linux/usb/composite.h>
+#include "composite.h"
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
@@ -44,19 +44,19 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "../../usb/gadget/usbstring.c"
-#include "../../usb/gadget/config.c"
-#include "../../usb/gadget/epautoconf.c"
-#include "../../usb/gadget/composite.c"
+#include "usbstring.c"
+#include "config.c"
+#include "epautoconf.c"
+#include "composite.c"
-#include "../../usb/gadget/f_mass_storage.c"
-#include "../../usb/gadget/u_serial.c"
-#include "../../usb/gadget/f_acm.c"
+#include "f_mass_storage.c"
+#include "u_serial.c"
+#include "f_acm.c"
#define USB_ETH_RNDIS y
-#include "../../usb/gadget/f_rndis.c"
-#include "../../usb/gadget/rndis.c"
-#include "../../usb/gadget/u_ether.c"
-#include "../../usb/gadget/f_fs.c"
+#include "f_rndis.c"
+#include "rndis.c"
+#include "u_ether.c"
+#include "f_fs.c"
MODULE_AUTHOR("Mike Lockwood, Andrzej Pietrasiewicz");
MODULE_DESCRIPTION("Configurable Composite USB Gadget");
@@ -1162,6 +1162,7 @@
static struct usb_composite_driver ccg_usb_driver = {
.name = "configurable_usb",
.dev = &device_desc,
+ .bind = ccg_bind,
.unbind = ccg_usb_unbind,
.needs_serial = true,
.iManufacturer = "Linux Foundation",
@@ -1275,7 +1276,7 @@
composite_driver.setup = ccg_setup;
composite_driver.disconnect = ccg_disconnect;
- err = usb_composite_probe(&ccg_usb_driver, ccg_bind);
+ err = usb_composite_probe(&ccg_usb_driver);
if (err) {
class_destroy(ccg_class);
kfree(dev);
diff --git a/drivers/staging/ccg/composite.c b/drivers/staging/ccg/composite.c
new file mode 100644
index 0000000..228b457
--- /dev/null
+++ b/drivers/staging/ccg/composite.c
@@ -0,0 +1,1688 @@
+/*
+ * composite.c - infrastructure for Composite USB Gadgets
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * 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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kallsyms.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/composite.h>
+#include <asm/unaligned.h>
+
+/*
+ * The code in this file is utility code, used to build a gadget driver
+ * from one or more "function" drivers, one or more "configuration"
+ * objects, and a "usb_composite_driver" by gluing them together along
+ * with the relevant device-wide data.
+ */
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 1024
+
+static struct usb_composite_driver *composite;
+
+/* Some systems will need runtime overrides for the product identifiers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, 0644);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, 0644);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, 0644);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, 0644);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, 0644);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, 0644);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
+
+static char composite_manufacturer[50];
+
+/*-------------------------------------------------------------------------*/
+/**
+ * next_ep_desc() - advance to the next EP descriptor
+ * @t: currect pointer within descriptor array
+ *
+ * Return: next EP descriptor or NULL
+ *
+ * Iterate over @t until either EP descriptor found or
+ * NULL (that indicates end of list) encountered
+ */
+static struct usb_descriptor_header**
+next_ep_desc(struct usb_descriptor_header **t)
+{
+ for (; *t; t++) {
+ if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+ return t;
+ }
+ return NULL;
+}
+
+/*
+ * for_each_ep_desc()- iterate over endpoint descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @ep_desc: endpoint descriptor to use as the loop cursor
+ */
+#define for_each_ep_desc(start, ep_desc) \
+ for (ep_desc = next_ep_desc(start); \
+ ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+ struct usb_function *f,
+ struct usb_ep *_ep)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(g);
+ struct usb_endpoint_descriptor *chosen_desc = NULL;
+ struct usb_descriptor_header **speed_desc = NULL;
+
+ struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
+ int want_comp_desc = 0;
+
+ struct usb_descriptor_header **d_spd; /* cursor for speed desc */
+
+ if (!g || !f || !_ep)
+ return -EIO;
+
+ /* select desired speed */
+ switch (g->speed) {
+ case USB_SPEED_SUPER:
+ if (gadget_is_superspeed(g)) {
+ speed_desc = f->ss_descriptors;
+ want_comp_desc = 1;
+ break;
+ }
+ /* else: Fall trough */
+ case USB_SPEED_HIGH:
+ if (gadget_is_dualspeed(g)) {
+ speed_desc = f->hs_descriptors;
+ break;
+ }
+ /* else: fall through */
+ default:
+ speed_desc = f->descriptors;
+ }
+ /* find descriptors */
+ for_each_ep_desc(speed_desc, d_spd) {
+ chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
+ if (chosen_desc->bEndpointAddress == _ep->address)
+ goto ep_found;
+ }
+ return -EIO;
+
+ep_found:
+ /* commit results */
+ _ep->maxpacket = usb_endpoint_maxp(chosen_desc);
+ _ep->desc = chosen_desc;
+ _ep->comp_desc = NULL;
+ _ep->maxburst = 0;
+ _ep->mult = 0;
+ if (!want_comp_desc)
+ return 0;
+
+ /*
+ * Companion descriptor should follow EP descriptor
+ * USB 3.0 spec, #9.6.7
+ */
+ comp_desc = (struct usb_ss_ep_comp_descriptor *)*(++d_spd);
+ if (!comp_desc ||
+ (comp_desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP))
+ return -EIO;
+ _ep->comp_desc = comp_desc;
+ if (g->speed == USB_SPEED_SUPER) {
+ switch (usb_endpoint_type(_ep->desc)) {
+ case USB_ENDPOINT_XFER_ISOC:
+ /* mult: bits 1:0 of bmAttributes */
+ _ep->mult = comp_desc->bmAttributes & 0x3;
+ case USB_ENDPOINT_XFER_BULK:
+ case USB_ENDPOINT_XFER_INT:
+ _ep->maxburst = comp_desc->bMaxBurst + 1;
+ break;
+ default:
+ if (comp_desc->bMaxBurst != 0)
+ ERROR(cdev, "ep0 bMaxBurst must be 0\n");
+ _ep->maxburst = 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/**
+ * usb_add_function() - add a function to a configuration
+ * @config: the configuration
+ * @function: the function being added
+ * Context: single threaded during gadget setup
+ *
+ * After initialization, each configuration must have one or more
+ * functions added to it. Adding a function involves calling its @bind()
+ * method to allocate resources such as interface and string identifiers
+ * and endpoints.
+ *
+ * This function returns the value of the function's bind(), which is
+ * zero for success else a negative errno value.
+ */
+int usb_add_function(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ int value = -EINVAL;
+
+ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
+ function->name, function,
+ config->label, config);
+
+ if (!function->set_alt || !function->disable)
+ goto done;
+
+ function->config = config;
+ list_add_tail(&function->list, &config->functions);
+
+ /* REVISIT *require* function->bind? */
+ if (function->bind) {
+ value = function->bind(config, function);
+ if (value < 0) {
+ list_del(&function->list);
+ function->config = NULL;
+ }
+ } else
+ value = 0;
+
+ /* We allow configurations that don't work at both speeds.
+ * If we run into a lowspeed Linux system, treat it the same
+ * as full speed ... it's the function drivers that will need
+ * to avoid bulk and ISO transfers.
+ */
+ if (!config->fullspeed && function->descriptors)
+ config->fullspeed = true;
+ if (!config->highspeed && function->hs_descriptors)
+ config->highspeed = true;
+ if (!config->superspeed && function->ss_descriptors)
+ config->superspeed = true;
+
+done:
+ if (value)
+ DBG(config->cdev, "adding '%s'/%p --> %d\n",
+ function->name, function, value);
+ return value;
+}
+
+/**
+ * usb_function_deactivate - prevent function and gadget enumeration
+ * @function: the function that isn't yet ready to respond
+ *
+ * Blocks response of the gadget driver to host enumeration by
+ * preventing the data line pullup from being activated. This is
+ * normally called during @bind() processing to change from the
+ * initial "ready to respond" state, or when a required resource
+ * becomes available.
+ *
+ * For example, drivers that serve as a passthrough to a userspace
+ * daemon can block enumeration unless that daemon (such as an OBEX,
+ * MTP, or print server) is ready to handle host requests.
+ *
+ * Not all systems support software control of their USB peripheral
+ * data pullups.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_deactivate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->deactivations == 0)
+ status = usb_gadget_disconnect(cdev->gadget);
+ if (status == 0)
+ cdev->deactivations++;
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return status;
+}
+
+/**
+ * usb_function_activate - allow function and gadget enumeration
+ * @function: function on which usb_function_activate() was called
+ *
+ * Reverses effect of usb_function_deactivate(). If no more functions
+ * are delaying their activation, the gadget driver will respond to
+ * host enumeration procedures.
+ *
+ * Returns zero on success, else negative errno.
+ */
+int usb_function_activate(struct usb_function *function)
+{
+ struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
+ int status = 0;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (WARN_ON(cdev->deactivations == 0))
+ status = -EINVAL;
+ else {
+ cdev->deactivations--;
+ if (cdev->deactivations == 0)
+ status = usb_gadget_connect(cdev->gadget);
+ }
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+ return status;
+}
+
+/**
+ * usb_interface_id() - allocate an unused interface ID
+ * @config: configuration associated with the interface
+ * @function: function handling the interface
+ * Context: single threaded during gadget setup
+ *
+ * usb_interface_id() is called from usb_function.bind() callbacks to
+ * allocate new interface IDs. The function driver will then store that
+ * ID in interface, association, CDC union, and other descriptors. It
+ * will also handle any control requests targeted at that interface,
+ * particularly changing its altsetting via set_alt(). There may
+ * also be class-specific or vendor-specific requests to handle.
+ *
+ * All interface identifier should be allocated using this routine, to
+ * ensure that for example different functions don't wrongly assign
+ * different meanings to the same identifier. Note that since interface
+ * identifiers are configuration-specific, functions used in more than
+ * one configuration (or more than once in a given configuration) need
+ * multiple versions of the relevant descriptors.
+ *
+ * Returns the interface ID which was allocated; or -ENODEV if no
+ * more interface IDs can be allocated.
+ */
+int usb_interface_id(struct usb_configuration *config,
+ struct usb_function *function)
+{
+ unsigned id = config->next_interface_id;
+
+ if (id < MAX_CONFIG_INTERFACES) {
+ config->interface[id] = function;
+ config->next_interface_id = id + 1;
+ return id;
+ }
+ return -ENODEV;
+}
+
+static int config_buf(struct usb_configuration *config,
+ enum usb_device_speed speed, void *buf, u8 type)
+{
+ struct usb_config_descriptor *c = buf;
+ void *next = buf + USB_DT_CONFIG_SIZE;
+ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ struct usb_function *f;
+ int status;
+
+ /* write the config descriptor */
+ c = buf;
+ c->bLength = USB_DT_CONFIG_SIZE;
+ c->bDescriptorType = type;
+ /* wTotalLength is written later */
+ c->bNumInterfaces = config->next_interface_id;
+ c->bConfigurationValue = config->bConfigurationValue;
+ c->iConfiguration = config->iConfiguration;
+ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
+ c->bMaxPower = config->bMaxPower ? : (CONFIG_USB_GADGET_VBUS_DRAW / 2);
+
+ /* There may be e.g. OTG descriptors */
+ if (config->descriptors) {
+ status = usb_descriptor_fillbuf(next, len,
+ config->descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ /* add each function's descriptors */
+ list_for_each_entry(f, &config->functions, list) {
+ struct usb_descriptor_header **descriptors;
+
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
+ descriptors = f->hs_descriptors;
+ break;
+ default:
+ descriptors = f->descriptors;
+ }
+
+ if (!descriptors)
+ continue;
+ status = usb_descriptor_fillbuf(next, len,
+ (const struct usb_descriptor_header **) descriptors);
+ if (status < 0)
+ return status;
+ len -= status;
+ next += status;
+ }
+
+ len = next - buf;
+ c->wTotalLength = cpu_to_le16(len);
+ return len;
+}
+
+static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ u8 type = w_value >> 8;
+ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
+
+ if (gadget->speed == USB_SPEED_SUPER)
+ speed = gadget->speed;
+ else if (gadget_is_dualspeed(gadget)) {
+ int hs = 0;
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (type == USB_DT_OTHER_SPEED_CONFIG)
+ hs = !hs;
+ if (hs)
+ speed = USB_SPEED_HIGH;
+
+ }
+
+ /* This is a lookup by config *INDEX* */
+ w_value &= 0xff;
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ switch (speed) {
+ case USB_SPEED_SUPER:
+ if (!c->superspeed)
+ continue;
+ break;
+ case USB_SPEED_HIGH:
+ if (!c->highspeed)
+ continue;
+ break;
+ default:
+ if (!c->fullspeed)
+ continue;
+ }
+
+ if (w_value == 0)
+ return config_buf(c, speed, cdev->req->buf, type);
+ w_value--;
+ }
+ return -EINVAL;
+}
+
+static int count_configs(struct usb_composite_dev *cdev, unsigned type)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c;
+ unsigned count = 0;
+ int hs = 0;
+ int ss = 0;
+
+ if (gadget_is_dualspeed(gadget)) {
+ if (gadget->speed == USB_SPEED_HIGH)
+ hs = 1;
+ if (gadget->speed == USB_SPEED_SUPER)
+ ss = 1;
+ if (type == USB_DT_DEVICE_QUALIFIER)
+ hs = !hs;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ /* ignore configs that won't work at this speed */
+ if (ss) {
+ if (!c->superspeed)
+ continue;
+ } else if (hs) {
+ if (!c->highspeed)
+ continue;
+ } else {
+ if (!c->fullspeed)
+ continue;
+ }
+ count++;
+ }
+ return count;
+}
+
+/**
+ * bos_desc() - prepares the BOS descriptor.
+ * @cdev: pointer to usb_composite device to generate the bos
+ * descriptor for
+ *
+ * This function generates the BOS (Binary Device Object)
+ * descriptor and its device capabilities descriptors. The BOS
+ * descriptor should be supported by a SuperSpeed device.
+ */
+static int bos_desc(struct usb_composite_dev *cdev)
+{
+ struct usb_ext_cap_descriptor *usb_ext;
+ struct usb_ss_cap_descriptor *ss_cap;
+ struct usb_dcd_config_params dcd_config_params;
+ struct usb_bos_descriptor *bos = cdev->req->buf;
+
+ bos->bLength = USB_DT_BOS_SIZE;
+ bos->bDescriptorType = USB_DT_BOS;
+
+ bos->wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE);
+ bos->bNumDeviceCaps = 0;
+
+ /*
+ * A SuperSpeed device shall include the USB2.0 extension descriptor
+ * and shall support LPM when operating in USB2.0 HS mode.
+ */
+ usb_ext = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_EXT_CAP_SIZE);
+ usb_ext->bLength = USB_DT_USB_EXT_CAP_SIZE;
+ usb_ext->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ usb_ext->bDevCapabilityType = USB_CAP_TYPE_EXT;
+ usb_ext->bmAttributes = cpu_to_le32(USB_LPM_SUPPORT);
+
+ /*
+ * The Superspeed USB Capability descriptor shall be implemented by all
+ * SuperSpeed devices.
+ */
+ ss_cap = cdev->req->buf + le16_to_cpu(bos->wTotalLength);
+ bos->bNumDeviceCaps++;
+ le16_add_cpu(&bos->wTotalLength, USB_DT_USB_SS_CAP_SIZE);
+ ss_cap->bLength = USB_DT_USB_SS_CAP_SIZE;
+ ss_cap->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
+ ss_cap->bDevCapabilityType = USB_SS_CAP_TYPE;
+ ss_cap->bmAttributes = 0; /* LTM is not supported yet */
+ ss_cap->wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION |
+ USB_FULL_SPEED_OPERATION |
+ USB_HIGH_SPEED_OPERATION |
+ USB_5GBPS_OPERATION);
+ ss_cap->bFunctionalitySupport = USB_LOW_SPEED_OPERATION;
+
+ /* Get Controller configuration */
+ if (cdev->gadget->ops->get_config_params)
+ cdev->gadget->ops->get_config_params(&dcd_config_params);
+ else {
+ dcd_config_params.bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT;
+ dcd_config_params.bU2DevExitLat =
+ cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT);
+ }
+ ss_cap->bU1devExitLat = dcd_config_params.bU1devExitLat;
+ ss_cap->bU2DevExitLat = dcd_config_params.bU2DevExitLat;
+
+ return le16_to_cpu(bos->wTotalLength);
+}
+
+static void device_qual(struct usb_composite_dev *cdev)
+{
+ struct usb_qualifier_descriptor *qual = cdev->req->buf;
+
+ qual->bLength = sizeof(*qual);
+ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
+ /* POLICY: same bcdUSB and device type info at both speeds */
+ qual->bcdUSB = cdev->desc.bcdUSB;
+ qual->bDeviceClass = cdev->desc.bDeviceClass;
+ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
+ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
+ /* ASSUME same EP0 fifo size at both speeds */
+ qual->bMaxPacketSize0 = cdev->gadget->ep0->maxpacket;
+ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
+ qual->bRESERVED = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void reset_config(struct usb_composite_dev *cdev)
+{
+ struct usb_function *f;
+
+ DBG(cdev, "reset config\n");
+
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->disable)
+ f->disable(f);
+
+ bitmap_zero(f->endpoints, 32);
+ }
+ cdev->config = NULL;
+}
+
+static int set_config(struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl, unsigned number)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct usb_configuration *c = NULL;
+ int result = -EINVAL;
+ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
+ int tmp;
+
+ if (number) {
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == number) {
+ /*
+ * We disable the FDs of the previous
+ * configuration only if the new configuration
+ * is a valid one
+ */
+ if (cdev->config)
+ reset_config(cdev);
+ result = 0;
+ break;
+ }
+ }
+ if (result < 0)
+ goto done;
+ } else { /* Zero configuration value - need to reset the config */
+ if (cdev->config)
+ reset_config(cdev);
+ result = 0;
+ }
+
+ INFO(cdev, "%s config #%d: %s\n",
+ usb_speed_string(gadget->speed),
+ number, c ? c->label : "unconfigured");
+
+ if (!c)
+ goto done;
+
+ cdev->config = c;
+
+ /* Initialize all interfaces by setting them to altsetting zero. */
+ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
+ struct usb_function *f = c->interface[tmp];
+ struct usb_descriptor_header **descriptors;
+
+ if (!f)
+ break;
+
+ /*
+ * Record which endpoints are used by the function. This is used
+ * to dispatch control requests targeted at that endpoint to the
+ * function's setup callback instead of the current
+ * configuration's setup callback.
+ */
+ switch (gadget->speed) {
+ case USB_SPEED_SUPER:
+ descriptors = f->ss_descriptors;
+ break;
+ case USB_SPEED_HIGH:
+ descriptors = f->hs_descriptors;
+ break;
+ default:
+ descriptors = f->descriptors;
+ }
+
+ for (; *descriptors; ++descriptors) {
+ struct usb_endpoint_descriptor *ep;
+ int addr;
+
+ if ((*descriptors)->bDescriptorType != USB_DT_ENDPOINT)
+ continue;
+
+ ep = (struct usb_endpoint_descriptor *)*descriptors;
+ addr = ((ep->bEndpointAddress & 0x80) >> 3)
+ | (ep->bEndpointAddress & 0x0f);
+ set_bit(addr, f->endpoints);
+ }
+
+ result = f->set_alt(f, tmp, 0);
+ if (result < 0) {
+ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
+ tmp, f->name, f, result);
+
+ reset_config(cdev);
+ goto done;
+ }
+
+ if (result == USB_GADGET_DELAYED_STATUS) {
+ DBG(cdev,
+ "%s: interface %d (%s) requested delayed status\n",
+ __func__, tmp, f->name);
+ cdev->delayed_status++;
+ DBG(cdev, "delayed_status count %d\n",
+ cdev->delayed_status);
+ }
+ }
+
+ /* when we return, be sure our power usage is valid */
+ power = c->bMaxPower ? (2 * c->bMaxPower) : CONFIG_USB_GADGET_VBUS_DRAW;
+done:
+ usb_gadget_vbus_draw(gadget, power);
+ if (result >= 0 && cdev->delayed_status)
+ result = USB_GADGET_DELAYED_STATUS;
+ return result;
+}
+
+/**
+ * usb_add_config() - add a configuration to a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration, with bConfigurationValue assigned
+ * @bind: the configuration's bind function
+ * Context: single threaded during gadget setup
+ *
+ * One of the main tasks of a composite @bind() routine is to
+ * add each of the configurations it supports, using this routine.
+ *
+ * This function returns the value of the configuration's @bind(), which
+ * is zero for success else a negative errno value. Binding configurations
+ * assigns global resources including string IDs, and per-configuration
+ * resources such as interface IDs and endpoints.
+ */
+int usb_add_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config,
+ int (*bind)(struct usb_configuration *))
+{
+ int status = -EINVAL;
+ struct usb_configuration *c;
+
+ DBG(cdev, "adding config #%u '%s'/%p\n",
+ config->bConfigurationValue,
+ config->label, config);
+
+ if (!config->bConfigurationValue || !bind)
+ goto done;
+
+ /* Prevent duplicate configuration identifiers */
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->bConfigurationValue == config->bConfigurationValue) {
+ status = -EBUSY;
+ goto done;
+ }
+ }
+
+ config->cdev = cdev;
+ list_add_tail(&config->list, &cdev->configs);
+
+ INIT_LIST_HEAD(&config->functions);
+ config->next_interface_id = 0;
+ memset(config->interface, 0, sizeof(config->interface));
+
+ status = bind(config);
+ if (status < 0) {
+ while (!list_empty(&config->functions)) {
+ struct usb_function *f;
+
+ f = list_first_entry(&config->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ DBG(cdev, "unbind function '%s'/%p\n",
+ f->name, f);
+ f->unbind(config, f);
+ /* may free memory for "f" */
+ }
+ }
+ list_del(&config->list);
+ config->cdev = NULL;
+ } else {
+ unsigned i;
+
+ DBG(cdev, "cfg %d/%p speeds:%s%s%s\n",
+ config->bConfigurationValue, config,
+ config->superspeed ? " super" : "",
+ config->highspeed ? " high" : "",
+ config->fullspeed
+ ? (gadget_is_dualspeed(cdev->gadget)
+ ? " full"
+ : " full/low")
+ : "");
+
+ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
+ struct usb_function *f = config->interface[i];
+
+ if (!f)
+ continue;
+ DBG(cdev, " interface %d = %s/%p\n",
+ i, f->name, f);
+ }
+ }
+
+ /* set_alt(), or next bind(), sets up
+ * ep->driver_data as needed.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+done:
+ if (status)
+ DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
+ config->bConfigurationValue, status);
+ return status;
+}
+
+static void remove_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ while (!list_empty(&config->functions)) {
+ struct usb_function *f;
+
+ f = list_first_entry(&config->functions,
+ struct usb_function, list);
+ list_del(&f->list);
+ if (f->unbind) {
+ DBG(cdev, "unbind function '%s'/%p\n", f->name, f);
+ f->unbind(config, f);
+ /* may free memory for "f" */
+ }
+ }
+ list_del(&config->list);
+ if (config->unbind) {
+ DBG(cdev, "unbind config '%s'/%p\n", config->label, config);
+ config->unbind(config);
+ /* may free memory for "c" */
+ }
+}
+
+/**
+ * usb_remove_config() - remove a configuration from a device.
+ * @cdev: wraps the USB gadget
+ * @config: the configuration
+ *
+ * Drivers must call usb_gadget_disconnect before calling this function
+ * to disconnect the device from the host and make sure the host will not
+ * try to enumerate the device while we are changing the config list.
+ */
+void usb_remove_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *config)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->config == config)
+ reset_config(cdev);
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+
+ remove_config(cdev, config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* We support strings in multiple languages ... string descriptor zero
+ * says which languages are supported. The typical case will be that
+ * only one language (probably English) is used, with I18N handled on
+ * the host side.
+ */
+
+static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
+{
+ const struct usb_gadget_strings *s;
+ __le16 language;
+ __le16 *tmp;
+
+ while (*sp) {
+ s = *sp;
+ language = cpu_to_le16(s->language);
+ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
+ if (*tmp == language)
+ goto repeat;
+ }
+ *tmp++ = language;
+repeat:
+ sp++;
+ }
+}
+
+static int lookup_string(
+ struct usb_gadget_strings **sp,
+ void *buf,
+ u16 language,
+ int id
+)
+{
+ struct usb_gadget_strings *s;
+ int value;
+
+ while (*sp) {
+ s = *sp++;
+ if (s->language != language)
+ continue;
+ value = usb_gadget_get_string(s, id, buf);
+ if (value > 0)
+ return value;
+ }
+ return -EINVAL;
+}
+
+static int get_string(struct usb_composite_dev *cdev,
+ void *buf, u16 language, int id)
+{
+ struct usb_configuration *c;
+ struct usb_function *f;
+ int len;
+ const char *str;
+
+ /* Yes, not only is USB's I18N support probably more than most
+ * folk will ever care about ... also, it's all supported here.
+ * (Except for UTF8 support for Unicode's "Astral Planes".)
+ */
+
+ /* 0 == report all available language codes */
+ if (id == 0) {
+ struct usb_string_descriptor *s = buf;
+ struct usb_gadget_strings **sp;
+
+ memset(s, 0, 256);
+ s->bDescriptorType = USB_DT_STRING;
+
+ sp = composite->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(c, &cdev->configs, list) {
+ sp = c->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+
+ list_for_each_entry(f, &c->functions, list) {
+ sp = f->strings;
+ if (sp)
+ collect_langs(sp, s->wData);
+ }
+ }
+
+ for (len = 0; len <= 126 && s->wData[len]; len++)
+ continue;
+ if (!len)
+ return -EINVAL;
+
+ s->bLength = 2 * (len + 1);
+ return s->bLength;
+ }
+
+ /* Otherwise, look up and return a specified string. First
+ * check if the string has not been overridden.
+ */
+ if (cdev->manufacturer_override == id)
+ str = iManufacturer ?: composite->iManufacturer ?:
+ composite_manufacturer;
+ else if (cdev->product_override == id)
+ str = iProduct ?: composite->iProduct;
+ else if (cdev->serial_override == id)
+ str = iSerialNumber ?: composite->iSerialNumber;
+ else
+ str = NULL;
+ if (str) {
+ struct usb_gadget_strings strings = {
+ .language = language,
+ .strings = &(struct usb_string) { 0xff, str }
+ };
+ return usb_gadget_get_string(&strings, 0xff, buf);
+ }
+
+ /* String IDs are device-scoped, so we look up each string
+ * table we're told about. These lookups are infrequent;
+ * simpler-is-better here.
+ */
+ if (composite->strings) {
+ len = lookup_string(composite->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(c, &cdev->configs, list) {
+ if (c->strings) {
+ len = lookup_string(c->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ list_for_each_entry(f, &c->functions, list) {
+ if (!f->strings)
+ continue;
+ len = lookup_string(f->strings, buf, language, id);
+ if (len > 0)
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/**
+ * usb_string_id() - allocate an unused string ID
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_id() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_ids_tab() or @usb_string_ids_n() routine, to ensure
+ * that for example different functions don't wrongly assign different
+ * meanings to the same identifier.
+ */
+int usb_string_id(struct usb_composite_dev *cdev)
+{
+ if (cdev->next_string_id < 254) {
+ /* string id 0 is reserved by USB spec for list of
+ * supported languages */
+ /* 255 reserved as well? -- mina86 */
+ cdev->next_string_id++;
+ return cdev->next_string_id;
+ }
+ return -ENODEV;
+}
+
+/**
+ * usb_string_ids() - allocate unused string IDs in batch
+ * @cdev: the device whose string descriptor IDs are being allocated
+ * @str: an array of usb_string objects to assign numbers to
+ * Context: single threaded during gadget setup
+ *
+ * @usb_string_ids() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then copy IDs from the string table to the appropriate descriptors
+ * and string table for other languages.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_tab(struct usb_composite_dev *cdev, struct usb_string *str)
+{
+ int next = cdev->next_string_id;
+
+ for (; str->s; ++str) {
+ if (unlikely(next >= 254))
+ return -ENODEV;
+ str->id = ++next;
+ }
+
+ cdev->next_string_id = next;
+
+ return 0;
+}
+
+/**
+ * usb_string_ids_n() - allocate unused string IDs in batch
+ * @c: the device whose string descriptor IDs are being allocated
+ * @n: number of string IDs to allocate
+ * Context: single threaded during gadget setup
+ *
+ * Returns the first requested ID. This ID and next @n-1 IDs are now
+ * valid IDs. At least provided that @n is non-zero because if it
+ * is, returns last requested ID which is now very useful information.
+ *
+ * @usb_string_ids_n() is called from bind() callbacks to allocate
+ * string IDs. Drivers for functions, configurations, or gadgets will
+ * then store that ID in the appropriate descriptors and string table.
+ *
+ * All string identifier should be allocated using this,
+ * @usb_string_id() or @usb_string_ids_n() routine, to ensure that for
+ * example different functions don't wrongly assign different meanings
+ * to the same identifier.
+ */
+int usb_string_ids_n(struct usb_composite_dev *c, unsigned n)
+{
+ unsigned next = c->next_string_id;
+ if (unlikely(n > 254 || (unsigned)next + n > 254))
+ return -ENODEV;
+ c->next_string_id += n;
+ return next + 1;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length)
+ DBG((struct usb_composite_dev *) ep->driver_data,
+ "setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver(like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config and function specific setup.
+ */
+static int
+composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ int status = 0;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u8 intf = w_index & 0xFF;
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+ struct usb_function *f = NULL;
+ u8 endp;
+
+ /* partial re-init of the response message; the function or the
+ * gadget might need to intercept e.g. a control-OUT completion
+ * when we delegate to it.
+ */
+ req->zero = 0;
+ req->complete = composite_setup_complete;
+ req->length = 0;
+ gadget->ep0->driver_data = cdev;
+
+ switch (ctrl->bRequest) {
+
+ /* we handle all standard USB descriptors */
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ cdev->desc.bNumConfigurations =
+ count_configs(cdev, USB_DT_DEVICE);
+ cdev->desc.bMaxPacketSize0 =
+ cdev->gadget->ep0->maxpacket;
+ if (gadget_is_superspeed(gadget)) {
+ if (gadget->speed >= USB_SPEED_SUPER) {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0300);
+ cdev->desc.bMaxPacketSize0 = 9;
+ } else {
+ cdev->desc.bcdUSB = cpu_to_le16(0x0210);
+ }
+ }
+
+ value = min(w_length, (u16) sizeof cdev->desc);
+ memcpy(req->buf, &cdev->desc, value);
+ break;
+ case USB_DT_DEVICE_QUALIFIER:
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ break;
+ device_qual(cdev);
+ value = min_t(int, w_length,
+ sizeof(struct usb_qualifier_descriptor));
+ break;
+ case USB_DT_OTHER_SPEED_CONFIG:
+ if (!gadget_is_dualspeed(gadget) ||
+ gadget->speed >= USB_SPEED_SUPER)
+ break;
+ /* FALLTHROUGH */
+ case USB_DT_CONFIG:
+ value = config_desc(cdev, w_value);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_STRING:
+ value = get_string(cdev, req->buf,
+ w_index, w_value & 0xff);
+ if (value >= 0)
+ value = min(w_length, (u16) value);
+ break;
+ case USB_DT_BOS:
+ if (gadget_is_superspeed(gadget)) {
+ value = bos_desc(cdev);
+ value = min(w_length, (u16) value);
+ }
+ break;
+ }
+ break;
+
+ /* any number of configs can work */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0)
+ goto unknown;
+ if (gadget_is_otg(gadget)) {
+ if (gadget->a_hnp_support)
+ DBG(cdev, "HNP available\n");
+ else if (gadget->a_alt_hnp_support)
+ DBG(cdev, "HNP on another port\n");
+ else
+ VDBG(cdev, "HNP inactive\n");
+ }
+ spin_lock(&cdev->lock);
+ value = set_config(cdev, ctrl, w_value);
+ spin_unlock(&cdev->lock);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN)
+ goto unknown;
+ if (cdev->config)
+ *(u8 *)req->buf = cdev->config->bConfigurationValue;
+ else
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /* function drivers must handle get/set altsetting; if there's
+ * no get() method, we know only altsetting zero works.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+ goto unknown;
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ if (w_value && !f->set_alt)
+ break;
+ value = f->set_alt(f, w_index, w_value);
+ if (value == USB_GADGET_DELAYED_STATUS) {
+ DBG(cdev,
+ "%s: interface %d (%s) requested delayed status\n",
+ __func__, intf, f->name);
+ cdev->delayed_status++;
+ DBG(cdev, "delayed_status count %d\n",
+ cdev->delayed_status);
+ }
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+ goto unknown;
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ /* lots of interfaces only need altsetting zero... */
+ value = f->get_alt ? f->get_alt(f, w_index) : 0;
+ if (value < 0)
+ break;
+ *((u8 *)req->buf) = value;
+ value = min(w_length, (u16) 1);
+ break;
+
+ /*
+ * USB 3.0 additions:
+ * Function driver should handle get_status request. If such cb
+ * wasn't supplied we respond with default value = 0
+ * Note: function driver should supply such cb only for the first
+ * interface of the function
+ */
+ case USB_REQ_GET_STATUS:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_IN | USB_RECIP_INTERFACE))
+ goto unknown;
+ value = 2; /* This is the length of the get_status reply */
+ put_unaligned_le16(0, req->buf);
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ status = f->get_status ? f->get_status(f) : 0;
+ if (status < 0)
+ break;
+ put_unaligned_le16(status & 0x0000ffff, req->buf);
+ break;
+ /*
+ * Function drivers should handle SetFeature/ClearFeature
+ * (FUNCTION_SUSPEND) request. function_suspend cb should be supplied
+ * only for the first interface of the function
+ */
+ case USB_REQ_CLEAR_FEATURE:
+ case USB_REQ_SET_FEATURE:
+ if (!gadget_is_superspeed(gadget))
+ goto unknown;
+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_RECIP_INTERFACE))
+ goto unknown;
+ switch (w_value) {
+ case USB_INTRF_FUNC_SUSPEND:
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ if (!f)
+ break;
+ value = 0;
+ if (f->func_suspend)
+ value = f->func_suspend(f, w_index >> 8);
+ if (value < 0) {
+ ERROR(cdev,
+ "func_suspend() returned error %d\n",
+ value);
+ value = 0;
+ }
+ break;
+ }
+ break;
+ default:
+unknown:
+ VDBG(cdev,
+ "non-core control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+
+ /* functions always handle their interfaces and endpoints...
+ * punt other recipients (other, WUSB, ...) to the current
+ * configuration code.
+ *
+ * REVISIT it could make sense to let the composite device
+ * take such requests too, if that's ever needed: to work
+ * in config 0, etc.
+ */
+ switch (ctrl->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
+ break;
+ f = cdev->config->interface[intf];
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ endp = ((w_index & 0x80) >> 3) | (w_index & 0x0f);
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (test_bit(endp, f->endpoints))
+ break;
+ }
+ if (&f->list == &cdev->config->functions)
+ f = NULL;
+ break;
+ }
+
+ if (f && f->setup)
+ value = f->setup(f, ctrl);
+ else {
+ struct usb_configuration *c;
+
+ c = cdev->config;
+ if (c && c->setup)
+ value = c->setup(c, ctrl);
+ }
+
+ goto done;
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(gadget->ep0, req);
+ }
+ } else if (value == USB_GADGET_DELAYED_STATUS && w_length != 0) {
+ WARN(cdev,
+ "%s: Delayed status not supported for w_length != 0",
+ __func__);
+ }
+
+done:
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void composite_disconnect(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ /* REVISIT: should we have config and device level
+ * disconnect callbacks?
+ */
+ spin_lock_irqsave(&cdev->lock, flags);
+ if (cdev->config)
+ reset_config(cdev);
+ if (composite->disconnect)
+ composite->disconnect(cdev);
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static ssize_t composite_show_suspended(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ return sprintf(buf, "%d\n", cdev->suspended);
+}
+
+static DEVICE_ATTR(suspended, 0444, composite_show_suspended, NULL);
+
+static void
+composite_unbind(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+
+ /* composite_disconnect() must already have been called
+ * by the underlying peripheral controller driver!
+ * so there's no i/o concurrency that could affect the
+ * state protected by cdev->lock.
+ */
+ WARN_ON(cdev->config);
+
+ while (!list_empty(&cdev->configs)) {
+ struct usb_configuration *c;
+ c = list_first_entry(&cdev->configs,
+ struct usb_configuration, list);
+ remove_config(cdev, c);
+ }
+ if (composite->unbind)
+ composite->unbind(cdev);
+
+ if (cdev->req) {
+ kfree(cdev->req->buf);
+ usb_ep_free_request(gadget->ep0, cdev->req);
+ }
+ device_remove_file(&gadget->dev, &dev_attr_suspended);
+ kfree(cdev);
+ set_gadget_data(gadget, NULL);
+ composite = NULL;
+}
+
+static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
+{
+ if (!*desc) {
+ int ret = usb_string_id(cdev);
+ if (unlikely(ret < 0))
+ WARNING(cdev, "failed to override string ID\n");
+ else
+ *desc = ret;
+ }
+
+ return *desc;
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct usb_composite_dev *cdev;
+ int status = -ENOMEM;
+
+ cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
+ if (!cdev)
+ return status;
+
+ spin_lock_init(&cdev->lock);
+ cdev->gadget = gadget;
+ set_gadget_data(gadget, cdev);
+ INIT_LIST_HEAD(&cdev->configs);
+
+ /* preallocate control response and buffer */
+ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!cdev->req)
+ goto fail;
+ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+ if (!cdev->req->buf)
+ goto fail;
+ cdev->req->complete = composite_setup_complete;
+ gadget->ep0->driver_data = cdev;
+
+ cdev->bufsiz = USB_BUFSIZ;
+ cdev->driver = composite;
+
+ /*
+ * As per USB compliance update, a device that is actively drawing
+ * more than 100mA from USB must report itself as bus-powered in
+ * the GetStatus(DEVICE) call.
+ */
+ if (CONFIG_USB_GADGET_VBUS_DRAW <= USB_SELF_POWER_VBUS_MAX_DRAW)
+ usb_gadget_set_selfpowered(gadget);
+
+ /* interface and string IDs start at zero via kzalloc.
+ * we force endpoints to start unassigned; few controller
+ * drivers will zero ep->driver_data.
+ */
+ usb_ep_autoconfig_reset(cdev->gadget);
+
+ /* composite gadget needs to assign strings for whole device (like
+ * serial number), register function drivers, potentially update
+ * power state and consumption, etc
+ */
+ status = composite->bind(cdev);
+ if (status < 0)
+ goto fail;
+
+ cdev->desc = *composite->dev;
+
+ /* standardized runtime overrides for device ID data */
+ if (idVendor)
+ cdev->desc.idVendor = cpu_to_le16(idVendor);
+ else
+ idVendor = le16_to_cpu(cdev->desc.idVendor);
+ if (idProduct)
+ cdev->desc.idProduct = cpu_to_le16(idProduct);
+ else
+ idProduct = le16_to_cpu(cdev->desc.idProduct);
+ if (bcdDevice)
+ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
+ else
+ bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
+
+ /* string overrides */
+ if (iManufacturer || !cdev->desc.iManufacturer) {
+ if (!iManufacturer && !composite->iManufacturer &&
+ !*composite_manufacturer)
+ snprintf(composite_manufacturer,
+ sizeof composite_manufacturer,
+ "%s %s with %s",
+ init_utsname()->sysname,
+ init_utsname()->release,
+ gadget->name);
+
+ cdev->manufacturer_override =
+ override_id(cdev, &cdev->desc.iManufacturer);
+ }
+
+ if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
+ cdev->product_override =
+ override_id(cdev, &cdev->desc.iProduct);
+
+ if (iSerialNumber ||
+ (!cdev->desc.iSerialNumber && composite->iSerialNumber))
+ cdev->serial_override =
+ override_id(cdev, &cdev->desc.iSerialNumber);
+
+ /* has userspace failed to provide a serial number? */
+ if (composite->needs_serial && !cdev->desc.iSerialNumber)
+ WARNING(cdev, "userspace failed to provide iSerialNumber\n");
+
+ /* finish up */
+ status = device_create_file(&gadget->dev, &dev_attr_suspended);
+ if (status)
+ goto fail;
+
+ INFO(cdev, "%s ready\n", composite->name);
+ return 0;
+
+fail:
+ composite_unbind(gadget);
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void
+composite_suspend(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+
+ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "suspend\n");
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->suspend)
+ f->suspend(f);
+ }
+ }
+ if (composite->suspend)
+ composite->suspend(cdev);
+
+ cdev->suspended = 1;
+
+ usb_gadget_vbus_draw(gadget, 2);
+}
+
+static void
+composite_resume(struct usb_gadget *gadget)
+{
+ struct usb_composite_dev *cdev = get_gadget_data(gadget);
+ struct usb_function *f;
+ u8 maxpower;
+
+ /* REVISIT: should we have config level
+ * suspend/resume callbacks?
+ */
+ DBG(cdev, "resume\n");
+ if (composite->resume)
+ composite->resume(cdev);
+ if (cdev->config) {
+ list_for_each_entry(f, &cdev->config->functions, list) {
+ if (f->resume)
+ f->resume(f);
+ }
+
+ maxpower = cdev->config->bMaxPower;
+
+ usb_gadget_vbus_draw(gadget, maxpower ?
+ (2 * maxpower) : CONFIG_USB_GADGET_VBUS_DRAW);
+ }
+
+ cdev->suspended = 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_gadget_driver composite_driver = {
+ .bind = composite_bind,
+ .unbind = composite_unbind,
+
+ .setup = composite_setup,
+ .disconnect = composite_disconnect,
+
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+};
+
+/**
+ * usb_composite_probe() - register a composite driver
+ * @driver: the driver to register
+ * @bind: the callback used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * Context: single threaded during gadget setup
+ *
+ * This function is used to register drivers using the composite driver
+ * framework. The return value is zero, or a negative errno value.
+ * Those values normally come from the driver's @bind method, which does
+ * all the work of setting up the driver to match the hardware.
+ *
+ * On successful return, the gadget is ready to respond to requests from
+ * the host, unless one of its components invokes usb_gadget_disconnect()
+ * while it was binding. That would usually be done in order to wait for
+ * some userspace participation.
+ */
+int usb_composite_probe(struct usb_composite_driver *driver)
+{
+ if (!driver || !driver->dev || composite || !driver->bind)
+ return -EINVAL;
+
+ if (!driver->name)
+ driver->name = "composite";
+ if (!driver->iProduct)
+ driver->iProduct = driver->name;
+ composite_driver.function = (char *) driver->name;
+ composite_driver.driver.name = driver->name;
+ composite_driver.max_speed = driver->max_speed;
+ composite = driver;
+
+ return usb_gadget_probe_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_unregister() - unregister a composite driver
+ * @driver: the driver to unregister
+ *
+ * This function is used to unregister drivers using the composite
+ * driver framework.
+ */
+void usb_composite_unregister(struct usb_composite_driver *driver)
+{
+ if (composite != driver)
+ return;
+ usb_gadget_unregister_driver(&composite_driver);
+}
+
+/**
+ * usb_composite_setup_continue() - Continue with the control transfer
+ * @cdev: the composite device who's control transfer was kept waiting
+ *
+ * This function must be called by the USB function driver to continue
+ * with the control transfer's data/status stage in case it had requested to
+ * delay the data/status stages. A USB function's setup handler (e.g. set_alt())
+ * can request the composite framework to delay the setup request's data/status
+ * stages by returning USB_GADGET_DELAYED_STATUS.
+ */
+void usb_composite_setup_continue(struct usb_composite_dev *cdev)
+{
+ int value;
+ struct usb_request *req = cdev->req;
+ unsigned long flags;
+
+ DBG(cdev, "%s\n", __func__);
+ spin_lock_irqsave(&cdev->lock, flags);
+
+ if (cdev->delayed_status == 0) {
+ WARN(cdev, "%s: Unexpected call\n", __func__);
+
+ } else if (--cdev->delayed_status == 0) {
+ DBG(cdev, "%s: Completing delayed status\n", __func__);
+ req->length = 0;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(cdev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ composite_setup_complete(cdev->gadget->ep0, req);
+ }
+ }
+
+ spin_unlock_irqrestore(&cdev->lock, flags);
+}
+
diff --git a/drivers/staging/ccg/composite.h b/drivers/staging/ccg/composite.h
new file mode 100644
index 0000000..19a5adf
--- /dev/null
+++ b/drivers/staging/ccg/composite.h
@@ -0,0 +1,395 @@
+/*
+ * composite.h -- framework for usb gadgets which are composite devices
+ *
+ * Copyright (C) 2006-2008 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef __LINUX_USB_COMPOSITE_H
+#define __LINUX_USB_COMPOSITE_H
+
+/*
+ * This framework is an optional layer on top of the USB Gadget interface,
+ * making it easier to build (a) Composite devices, supporting multiple
+ * functions within any single configuration, and (b) Multi-configuration
+ * devices, also supporting multiple functions but without necessarily
+ * having more than one function per configuration.
+ *
+ * Example: a device with a single configuration supporting both network
+ * link and mass storage functions is a composite device. Those functions
+ * might alternatively be packaged in individual configurations, but in
+ * the composite model the host can use both functions at the same time.
+ */
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/*
+ * USB function drivers should return USB_GADGET_DELAYED_STATUS if they
+ * wish to delay the data/status stages of the control transfer till they
+ * are ready. The control transfer will then be kept from completing till
+ * all the function drivers that requested for USB_GADGET_DELAYED_STAUS
+ * invoke usb_composite_setup_continue().
+ */
+#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
+
+struct usb_configuration;
+
+/**
+ * struct usb_function - describes one function of a configuration
+ * @name: For diagnostics, identifies the function.
+ * @strings: tables of strings, keyed by identifiers assigned during bind()
+ * and by language IDs provided in control requests
+ * @descriptors: Table of full (or low) speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at full speed (or at low speed).
+ * @hs_descriptors: Table of high speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this pointer is null,
+ * the function will not be available at high speed.
+ * @ss_descriptors: Table of super speed descriptors, using interface and
+ * string identifiers assigned during @bind(). If this
+ * pointer is null after initiation, the function will not
+ * be available at super speed.
+ * @config: assigned when @usb_add_function() is called; this is the
+ * configuration with which this function is associated.
+ * @bind: Before the gadget can register, all of its functions bind() to the
+ * available resources including string and interface identifiers used
+ * in interface or class descriptors; endpoints; I/O buffers; and so on.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this function.
+ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
+ * initialize usb_ep.driver data at this time (when it is used).
+ * Note that setting an interface to its current altsetting resets
+ * interface state, and that all interfaces have a disabled state.
+ * @get_alt: Returns the active altsetting. If this is not provided,
+ * then only altsetting zero is supported.
+ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons
+ * include host resetting or reconfiguring the gadget, and disconnection.
+ * @setup: Used for interface-specific control requests.
+ * @suspend: Notifies functions when the host stops sending USB traffic.
+ * @resume: Notifies functions when the host restarts USB traffic.
+ * @get_status: Returns function status as a reply to
+ * GetStatus() request when the recepient is Interface.
+ * @func_suspend: callback to be called when
+ * SetFeature(FUNCTION_SUSPEND) is reseived
+ *
+ * A single USB function uses one or more interfaces, and should in most
+ * cases support operation at both full and high speeds. Each function is
+ * associated by @usb_add_function() with a one configuration; that function
+ * causes @bind() to be called so resources can be allocated as part of
+ * setting up a gadget driver. Those resources include endpoints, which
+ * should be allocated using @usb_ep_autoconfig().
+ *
+ * To support dual speed operation, a function driver provides descriptors
+ * for both high and full speed operation. Except in rare cases that don't
+ * involve bulk endpoints, each speed needs different endpoint descriptors.
+ *
+ * Function drivers choose their own strategies for managing instance data.
+ * The simplest strategy just declares it "static', which means the function
+ * can only be activated once. If the function needs to be exposed in more
+ * than one configuration at a given speed, it needs to support multiple
+ * usb_function structures (one for each configuration).
+ *
+ * A more complex strategy might encapsulate a @usb_function structure inside
+ * a driver-specific instance structure to allows multiple activations. An
+ * example of multiple activations might be a CDC ACM function that supports
+ * two or more distinct instances within the same configuration, providing
+ * several independent logical data links to a USB host.
+ */
+struct usb_function {
+ const char *name;
+ struct usb_gadget_strings **strings;
+ struct usb_descriptor_header **descriptors;
+ struct usb_descriptor_header **hs_descriptors;
+ struct usb_descriptor_header **ss_descriptors;
+
+ struct usb_configuration *config;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching.
+ * Related: unbind() may kfree() but bind() won't...
+ */
+
+ /* configuration management: bind/unbind */
+ int (*bind)(struct usb_configuration *,
+ struct usb_function *);
+ void (*unbind)(struct usb_configuration *,
+ struct usb_function *);
+
+ /* runtime state management */
+ int (*set_alt)(struct usb_function *,
+ unsigned interface, unsigned alt);
+ int (*get_alt)(struct usb_function *,
+ unsigned interface);
+ void (*disable)(struct usb_function *);
+ int (*setup)(struct usb_function *,
+ const struct usb_ctrlrequest *);
+ void (*suspend)(struct usb_function *);
+ void (*resume)(struct usb_function *);
+
+ /* USB 3.0 additions */
+ int (*get_status)(struct usb_function *);
+ int (*func_suspend)(struct usb_function *,
+ u8 suspend_opt);
+ /* private: */
+ /* internals */
+ struct list_head list;
+ DECLARE_BITMAP(endpoints, 32);
+};
+
+int usb_add_function(struct usb_configuration *, struct usb_function *);
+
+int usb_function_deactivate(struct usb_function *);
+int usb_function_activate(struct usb_function *);
+
+int usb_interface_id(struct usb_configuration *, struct usb_function *);
+
+int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
+ struct usb_ep *_ep);
+
+#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
+
+/**
+ * struct usb_configuration - represents one gadget configuration
+ * @label: For diagnostics, describes the configuration.
+ * @strings: Tables of strings, keyed by identifiers assigned during @bind()
+ * and by language IDs provided in control requests.
+ * @descriptors: Table of descriptors preceding all function descriptors.
+ * Examples include OTG and vendor-specific descriptors.
+ * @unbind: Reverses @bind; called as a side effect of unregistering the
+ * driver which added this configuration.
+ * @setup: Used to delegate control requests that aren't handled by standard
+ * device infrastructure or directed at a specific interface.
+ * @bConfigurationValue: Copied into configuration descriptor.
+ * @iConfiguration: Copied into configuration descriptor.
+ * @bmAttributes: Copied into configuration descriptor.
+ * @bMaxPower: Copied into configuration descriptor.
+ * @cdev: assigned by @usb_add_config() before calling @bind(); this is
+ * the device associated with this configuration.
+ *
+ * Configurations are building blocks for gadget drivers structured around
+ * function drivers. Simple USB gadgets require only one function and one
+ * configuration, and handle dual-speed hardware by always providing the same
+ * functionality. Slightly more complex gadgets may have more than one
+ * single-function configuration at a given speed; or have configurations
+ * that only work at one speed.
+ *
+ * Composite devices are, by definition, ones with configurations which
+ * include more than one function.
+ *
+ * The lifecycle of a usb_configuration includes allocation, initialization
+ * of the fields described above, and calling @usb_add_config() to set up
+ * internal data and bind it to a specific device. The configuration's
+ * @bind() method is then used to initialize all the functions and then
+ * call @usb_add_function() for them.
+ *
+ * Those functions would normally be independent of each other, but that's
+ * not mandatory. CDC WMC devices are an example where functions often
+ * depend on other functions, with some functions subsidiary to others.
+ * Such interdependency may be managed in any way, so long as all of the
+ * descriptors complete by the time the composite driver returns from
+ * its bind() routine.
+ */
+struct usb_configuration {
+ const char *label;
+ struct usb_gadget_strings **strings;
+ const struct usb_descriptor_header **descriptors;
+
+ /* REVISIT: bind() functions can be marked __init, which
+ * makes trouble for section mismatch analysis. See if
+ * we can't restructure things to avoid mismatching...
+ */
+
+ /* configuration management: unbind/setup */
+ void (*unbind)(struct usb_configuration *);
+ int (*setup)(struct usb_configuration *,
+ const struct usb_ctrlrequest *);
+
+ /* fields in the config descriptor */
+ u8 bConfigurationValue;
+ u8 iConfiguration;
+ u8 bmAttributes;
+ u8 bMaxPower;
+
+ struct usb_composite_dev *cdev;
+
+ /* private: */
+ /* internals */
+ struct list_head list;
+ struct list_head functions;
+ u8 next_interface_id;
+ unsigned superspeed:1;
+ unsigned highspeed:1;
+ unsigned fullspeed:1;
+ struct usb_function *interface[MAX_CONFIG_INTERFACES];
+};
+
+int usb_add_config(struct usb_composite_dev *,
+ struct usb_configuration *,
+ int (*)(struct usb_configuration *));
+
+void usb_remove_config(struct usb_composite_dev *,
+ struct usb_configuration *);
+
+/**
+ * struct usb_composite_driver - groups configurations into a gadget
+ * @name: For diagnostics, identifies the driver.
+ * @iProduct: Used as iProduct override if @dev->iProduct is not set.
+ * If NULL value of @name is taken.
+ * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
+ * not set. If NULL a default "<system> <release> with <udc>" value
+ * will be used.
+ * @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
+ * not set.
+ * @dev: Template descriptor for the device, including default device
+ * identifiers.
+ * @strings: tables of strings, keyed by identifiers assigned during @bind
+ * and language IDs provided in control requests
+ * @max_speed: Highest speed the driver supports.
+ * @needs_serial: set to 1 if the gadget needs userspace to provide
+ * a serial number. If one is not provided, warning will be printed.
+ * @bind: (REQUIRED) Used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind; called as a side effect of unregistering
+ * this driver.
+ * @disconnect: optional driver disconnect method
+ * @suspend: Notifies when the host stops sending USB traffic,
+ * after function notifications
+ * @resume: Notifies configuration when the host restarts USB traffic,
+ * before function notifications
+ *
+ * Devices default to reporting self powered operation. Devices which rely
+ * on bus powered operation should report this in their @bind method.
+ *
+ * Before returning from @bind, various fields in the template descriptor
+ * may be overridden. These include the idVendor/idProduct/bcdDevice values
+ * normally to bind the appropriate host side driver, and the three strings
+ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user
+ * meaningful device identifiers. (The strings will not be defined unless
+ * they are defined in @dev and @strings.) The correct ep0 maxpacket size
+ * is also reported, as defined by the underlying controller driver.
+ */
+struct usb_composite_driver {
+ const char *name;
+ const char *iProduct;
+ const char *iManufacturer;
+ const char *iSerialNumber;
+ const struct usb_device_descriptor *dev;
+ struct usb_gadget_strings **strings;
+ enum usb_device_speed max_speed;
+ unsigned needs_serial:1;
+
+ int (*bind)(struct usb_composite_dev *cdev);
+ int (*unbind)(struct usb_composite_dev *);
+
+ void (*disconnect)(struct usb_composite_dev *);
+
+ /* global suspend hooks */
+ void (*suspend)(struct usb_composite_dev *);
+ void (*resume)(struct usb_composite_dev *);
+};
+
+extern int usb_composite_probe(struct usb_composite_driver *driver);
+extern void usb_composite_unregister(struct usb_composite_driver *driver);
+extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
+
+
+/**
+ * struct usb_composite_device - represents one composite usb gadget
+ * @gadget: read-only, abstracts the gadget's usb peripheral controller
+ * @req: used for control responses; buffer is pre-allocated
+ * @bufsiz: size of buffer pre-allocated in @req
+ * @config: the currently active configuration
+ *
+ * One of these devices is allocated and initialized before the
+ * associated device driver's bind() is called.
+ *
+ * OPEN ISSUE: it appears that some WUSB devices will need to be
+ * built by combining a normal (wired) gadget with a wireless one.
+ * This revision of the gadget framework should probably try to make
+ * sure doing that won't hurt too much.
+ *
+ * One notion for how to handle Wireless USB devices involves:
+ * (a) a second gadget here, discovery mechanism TBD, but likely
+ * needing separate "register/unregister WUSB gadget" calls;
+ * (b) updates to usb_gadget to include flags "is it wireless",
+ * "is it wired", plus (presumably in a wrapper structure)
+ * bandgroup and PHY info;
+ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
+ * wireless-specific parameters like maxburst and maxsequence;
+ * (d) configurations that are specific to wireless links;
+ * (e) function drivers that understand wireless configs and will
+ * support wireless for (additional) function instances;
+ * (f) a function to support association setup (like CBAF), not
+ * necessarily requiring a wireless adapter;
+ * (g) composite device setup that can create one or more wireless
+ * configs, including appropriate association setup support;
+ * (h) more, TBD.
+ */
+struct usb_composite_dev {
+ struct usb_gadget *gadget;
+ struct usb_request *req;
+ unsigned bufsiz;
+
+ struct usb_configuration *config;
+
+ /* private: */
+ /* internals */
+ unsigned int suspended:1;
+ struct usb_device_descriptor desc;
+ struct list_head configs;
+ struct usb_composite_driver *driver;
+ u8 next_string_id;
+ u8 manufacturer_override;
+ u8 product_override;
+ u8 serial_override;
+
+ /* the gadget driver won't enable the data pullup
+ * while the deactivation count is nonzero.
+ */
+ unsigned deactivations;
+
+ /* the composite driver won't complete the control transfer's
+ * data/status stages till delayed_status is zero.
+ */
+ int delayed_status;
+
+ /* protects deactivations and delayed_status counts*/
+ spinlock_t lock;
+};
+
+extern int usb_string_id(struct usb_composite_dev *c);
+extern int usb_string_ids_tab(struct usb_composite_dev *c,
+ struct usb_string *str);
+extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
+
+
+/* messaging utils */
+#define DBG(d, fmt, args...) \
+ dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) \
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
+
+#endif /* __LINUX_USB_COMPOSITE_H */
diff --git a/drivers/staging/ccg/config.c b/drivers/staging/ccg/config.c
new file mode 100644
index 0000000..7542a72c
--- /dev/null
+++ b/drivers/staging/ccg/config.c
@@ -0,0 +1,158 @@
+/*
+ * usb/gadget/config.c -- simplify building config descriptors
+ *
+ * Copyright (C) 2003 David Brownell
+ *
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_descriptor_fillbuf - fill buffer with descriptors
+ * @buf: Buffer to be filled
+ * @buflen: Size of buf
+ * @src: Array of descriptor pointers, terminated by null pointer.
+ *
+ * Copies descriptors into the buffer, returning the length or a
+ * negative error code if they can't all be copied. Useful when
+ * assembling descriptors for an associated set of interfaces used
+ * as part of configuring a composite device; or in other cases where
+ * sets of descriptors need to be marshaled.
+ */
+int
+usb_descriptor_fillbuf(void *buf, unsigned buflen,
+ const struct usb_descriptor_header **src)
+{
+ u8 *dest = buf;
+
+ if (!src)
+ return -EINVAL;
+
+ /* fill buffer from src[] until null descriptor ptr */
+ for (; NULL != *src; src++) {
+ unsigned len = (*src)->bLength;
+
+ if (len > buflen)
+ return -EINVAL;
+ memcpy(dest, *src, len);
+ buflen -= len;
+ dest += len;
+ }
+ return dest - (u8 *)buf;
+}
+
+
+/**
+ * usb_gadget_config_buf - builts a complete configuration descriptor
+ * @config: Header for the descriptor, including characteristics such
+ * as power requirements and number of interfaces.
+ * @desc: Null-terminated vector of pointers to the descriptors (interface,
+ * endpoint, etc) defining all functions in this device configuration.
+ * @buf: Buffer for the resulting configuration descriptor.
+ * @length: Length of buffer. If this is not big enough to hold the
+ * entire configuration descriptor, an error code will be returned.
+ *
+ * This copies descriptors into the response buffer, building a descriptor
+ * for that configuration. It returns the buffer length or a negative
+ * status code. The config.wTotalLength field is set to match the length
+ * of the result, but other descriptor fields (including power usage and
+ * interface count) must be set by the caller.
+ *
+ * Gadget drivers could use this when constructing a config descriptor
+ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the
+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed.
+ */
+int usb_gadget_config_buf(
+ const struct usb_config_descriptor *config,
+ void *buf,
+ unsigned length,
+ const struct usb_descriptor_header **desc
+)
+{
+ struct usb_config_descriptor *cp = buf;
+ int len;
+
+ /* config descriptor first */
+ if (length < USB_DT_CONFIG_SIZE || !desc)
+ return -EINVAL;
+ *cp = *config;
+
+ /* then interface/endpoint/class/vendor/... */
+ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+ length - USB_DT_CONFIG_SIZE, desc);
+ if (len < 0)
+ return len;
+ len += USB_DT_CONFIG_SIZE;
+ if (len > 0xffff)
+ return -EINVAL;
+
+ /* patch up the config descriptor */
+ cp->bLength = USB_DT_CONFIG_SIZE;
+ cp->bDescriptorType = USB_DT_CONFIG;
+ cp->wTotalLength = cpu_to_le16(len);
+ cp->bmAttributes |= USB_CONFIG_ATT_ONE;
+ return len;
+}
+
+/**
+ * usb_copy_descriptors - copy a vector of USB descriptors
+ * @src: null-terminated vector to copy
+ * Context: initialization code, which may sleep
+ *
+ * This makes a copy of a vector of USB descriptors. Its primary use
+ * is to support usb_function objects which can have multiple copies,
+ * each needing different descriptors. Functions may have static
+ * tables of descriptors, which are used as templates and customized
+ * with identifiers (for interfaces, strings, endpoints, and more)
+ * as needed by a given function instance.
+ */
+struct usb_descriptor_header **
+usb_copy_descriptors(struct usb_descriptor_header **src)
+{
+ struct usb_descriptor_header **tmp;
+ unsigned bytes;
+ unsigned n_desc;
+ void *mem;
+ struct usb_descriptor_header **ret;
+
+ /* count descriptors and their sizes; then add vector size */
+ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
+ bytes += (*tmp)->bLength;
+ bytes += (n_desc + 1) * sizeof(*tmp);
+
+ mem = kmalloc(bytes, GFP_KERNEL);
+ if (!mem)
+ return NULL;
+
+ /* fill in pointers starting at "tmp",
+ * to descriptors copied starting at "mem";
+ * and return "ret"
+ */
+ tmp = mem;
+ ret = mem;
+ mem += (n_desc + 1) * sizeof(*tmp);
+ while (*src) {
+ memcpy(mem, *src, (*src)->bLength);
+ *tmp = mem;
+ tmp++;
+ mem += (*src)->bLength;
+ src++;
+ }
+ *tmp = NULL;
+
+ return ret;
+}
+
diff --git a/drivers/staging/ccg/epautoconf.c b/drivers/staging/ccg/epautoconf.c
new file mode 100644
index 0000000..51f3d42
--- /dev/null
+++ b/drivers/staging/ccg/epautoconf.c
@@ -0,0 +1,393 @@
+/*
+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
+ *
+ * Copyright (C) 2004 David Brownell
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/device.h>
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "gadget_chips.h"
+
+
+/* we must assign addresses for configurable endpoints (like net2280) */
+static unsigned epnum;
+
+// #define MANY_ENDPOINTS
+#ifdef MANY_ENDPOINTS
+/* more than 15 configurable endpoints */
+static unsigned in_epnum;
+#endif
+
+
+/*
+ * This should work with endpoints from controller drivers sharing the
+ * same endpoint naming convention. By example:
+ *
+ * - ep1, ep2, ... address is fixed, not direction or type
+ * - ep1in, ep2out, ... address and direction are fixed, not type
+ * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
+ * - ep1in-bulk, ep2out-iso, ... all three are fixed
+ * - ep-* ... no functionality restrictions
+ *
+ * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal.
+ * Less common restrictions are implied by gadget_is_*().
+ *
+ * NOTE: each endpoint is unidirectional, as specified by its USB
+ * descriptor; and isn't specific to a configuration or altsetting.
+ */
+static int
+ep_matches (
+ struct usb_gadget *gadget,
+ struct usb_ep *ep,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+ u8 type;
+ const char *tmp;
+ u16 max;
+
+ int num_req_streams = 0;
+
+ /* endpoint already claimed? */
+ if (NULL != ep->driver_data)
+ return 0;
+
+ /* only support ep0 for portable CONTROL traffic */
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ if (USB_ENDPOINT_XFER_CONTROL == type)
+ return 0;
+
+ /* some other naming convention */
+ if ('e' != ep->name[0])
+ return 0;
+
+ /* type-restriction: "-iso", "-bulk", or "-int".
+ * direction-restriction: "in", "out".
+ */
+ if ('-' != ep->name[2]) {
+ tmp = strrchr (ep->name, '-');
+ if (tmp) {
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* bulk endpoints handle interrupt transfers,
+ * except the toggle-quirky iso-synch kind
+ */
+ if ('s' == tmp[2]) // == "-iso"
+ return 0;
+ /* for now, avoid PXA "interrupt-in";
+ * it's documented as never using DATA1.
+ */
+ if (gadget_is_pxa (gadget)
+ && 'i' == tmp [1])
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_BULK:
+ if ('b' != tmp[1]) // != "-bulk"
+ return 0;
+ break;
+ case USB_ENDPOINT_XFER_ISOC:
+ if ('s' != tmp[2]) // != "-iso"
+ return 0;
+ }
+ } else {
+ tmp = ep->name + strlen (ep->name);
+ }
+
+ /* direction-restriction: "..in-..", "out-.." */
+ tmp--;
+ if (!isdigit (*tmp)) {
+ if (desc->bEndpointAddress & USB_DIR_IN) {
+ if ('n' != *tmp)
+ return 0;
+ } else {
+ if ('t' != *tmp)
+ return 0;
+ }
+ }
+ }
+
+ /*
+ * Get the number of required streams from the EP companion
+ * descriptor and see if the EP matches it
+ */
+ if (usb_endpoint_xfer_bulk(desc)) {
+ if (ep_comp && gadget->max_speed >= USB_SPEED_SUPER) {
+ num_req_streams = ep_comp->bmAttributes & 0x1f;
+ if (num_req_streams > ep->max_streams)
+ return 0;
+ }
+
+ }
+
+ /*
+ * If the protocol driver hasn't yet decided on wMaxPacketSize
+ * and wants to know the maximum possible, provide the info.
+ */
+ if (desc->wMaxPacketSize == 0)
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
+
+ /* endpoint maxpacket size is an input parameter, except for bulk
+ * where it's an output parameter representing the full speed limit.
+ * the usb spec fixes high speed bulk maxpacket at 512 bytes.
+ */
+ max = 0x7ff & usb_endpoint_maxp(desc);
+ switch (type) {
+ case USB_ENDPOINT_XFER_INT:
+ /* INT: limit 64 bytes full speed, 1024 high/super speed */
+ if (!gadget_is_dualspeed(gadget) && max > 64)
+ return 0;
+ /* FALLTHROUGH */
+
+ case USB_ENDPOINT_XFER_ISOC:
+ /* ISO: limit 1023 bytes full speed, 1024 high/super speed */
+ if (ep->maxpacket < max)
+ return 0;
+ if (!gadget_is_dualspeed(gadget) && max > 1023)
+ return 0;
+
+ /* BOTH: "high bandwidth" works only at high speed */
+ if ((desc->wMaxPacketSize & cpu_to_le16(3<<11))) {
+ if (!gadget_is_dualspeed(gadget))
+ return 0;
+ /* configure your hardware with enough buffering!! */
+ }
+ break;
+ }
+
+ /* MATCH!! */
+
+ /* report address */
+ desc->bEndpointAddress &= USB_DIR_IN;
+ if (isdigit (ep->name [2])) {
+ u8 num = simple_strtoul (&ep->name [2], NULL, 10);
+ desc->bEndpointAddress |= num;
+#ifdef MANY_ENDPOINTS
+ } else if (desc->bEndpointAddress & USB_DIR_IN) {
+ if (++in_epnum > 15)
+ return 0;
+ desc->bEndpointAddress = USB_DIR_IN | in_epnum;
+#endif
+ } else {
+ if (++epnum > 15)
+ return 0;
+ desc->bEndpointAddress |= epnum;
+ }
+
+ /* report (variable) full speed bulk maxpacket */
+ if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
+ int size = ep->maxpacket;
+
+ /* min() doesn't work on bitfields with gcc-3.5 */
+ if (size > 64)
+ size = 64;
+ desc->wMaxPacketSize = cpu_to_le16(size);
+ }
+ ep->address = desc->bEndpointAddress;
+ return 1;
+}
+
+static struct usb_ep *
+find_ep (struct usb_gadget *gadget, const char *name)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ if (0 == strcmp (ep->name, name))
+ return ep;
+ }
+ return NULL;
+}
+
+/**
+ * usb_ep_autoconfig_ss() - choose an endpoint matching the ep
+ * descriptor and ep companion descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on
+ * success.
+ * @ep_comp: Endpoint companion descriptor, with the required
+ * number of streams. Will be modified when the chosen EP
+ * supports a different number of streams.
+ *
+ * This routine replaces the usb_ep_autoconfig when needed
+ * superspeed enhancments. If such enhancemnets are required,
+ * the FD should call usb_ep_autoconfig_ss directly and provide
+ * the additional ep_comp parameter.
+ *
+ * By choosing an endpoint to use with the specified descriptor,
+ * this routine simplifies writing gadget drivers that work with
+ * multiple USB device controllers. The endpoint would be
+ * passed later to usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration
+ * on your hardware. This code may not make the best choices
+ * about how to use the USB controller, and it can't know all
+ * the restrictions that may apply. Some combinations of driver
+ * and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed and
+ * the bmAttribute field in the ep companion descriptor is
+ * updated with the assigned number of streams if it is
+ * different from the original value. To prevent the endpoint
+ * from being returned by a later autoconfig call, claim it by
+ * assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig_ss(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc,
+ struct usb_ss_ep_comp_descriptor *ep_comp
+)
+{
+ struct usb_ep *ep;
+ u8 type;
+
+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+
+ /* First, apply chip-specific "best usage" knowledge.
+ * This might make a good usb_gadget_ops hook ...
+ */
+ if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) {
+ /* ep-e, ep-f are PIO with only 64 byte fifos */
+ ep = find_ep (gadget, "ep-e");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ ep = find_ep (gadget, "ep-f");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+
+ } else if (gadget_is_goku (gadget)) {
+ if (USB_ENDPOINT_XFER_INT == type) {
+ /* single buffering is enough */
+ ep = find_ep(gadget, "ep3-bulk");
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ } else if (USB_ENDPOINT_XFER_BULK == type
+ && (USB_DIR_IN & desc->bEndpointAddress)) {
+ /* DMA may be available */
+ ep = find_ep(gadget, "ep2-bulk");
+ if (ep && ep_matches(gadget, ep, desc,
+ ep_comp))
+ goto found_ep;
+ }
+
+#ifdef CONFIG_BLACKFIN
+ } else if (gadget_is_musbhdrc(gadget)) {
+ if ((USB_ENDPOINT_XFER_BULK == type) ||
+ (USB_ENDPOINT_XFER_ISOC == type)) {
+ if (USB_DIR_IN & desc->bEndpointAddress)
+ ep = find_ep (gadget, "ep5in");
+ else
+ ep = find_ep (gadget, "ep6out");
+ } else if (USB_ENDPOINT_XFER_INT == type) {
+ if (USB_DIR_IN & desc->bEndpointAddress)
+ ep = find_ep(gadget, "ep1in");
+ else
+ ep = find_ep(gadget, "ep2out");
+ } else
+ ep = NULL;
+ if (ep && ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+#endif
+ }
+
+ /* Second, look at endpoints until an unclaimed one looks usable */
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ if (ep_matches(gadget, ep, desc, ep_comp))
+ goto found_ep;
+ }
+
+ /* Fail */
+ return NULL;
+found_ep:
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+ return ep;
+}
+
+/**
+ * usb_ep_autoconfig() - choose an endpoint matching the
+ * descriptor
+ * @gadget: The device to which the endpoint must belong.
+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode
+ * initialized. For periodic transfers, the maximum packet
+ * size must also be initialized. This is modified on success.
+ *
+ * By choosing an endpoint to use with the specified descriptor, this
+ * routine simplifies writing gadget drivers that work with multiple
+ * USB device controllers. The endpoint would be passed later to
+ * usb_ep_enable(), along with some descriptor.
+ *
+ * That second descriptor won't always be the same as the first one.
+ * For example, isochronous endpoints can be autoconfigured for high
+ * bandwidth, and then used in several lower bandwidth altsettings.
+ * Also, high and full speed descriptors will be different.
+ *
+ * Be sure to examine and test the results of autoconfiguration on your
+ * hardware. This code may not make the best choices about how to use the
+ * USB controller, and it can't know all the restrictions that may apply.
+ * Some combinations of driver and hardware won't be able to autoconfigure.
+ *
+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint
+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value
+ * is initialized as if the endpoint were used at full speed. To prevent
+ * the endpoint from being returned by a later autoconfig call, claim it
+ * by assigning ep->driver_data to some non-null value.
+ *
+ * On failure, this returns a null endpoint descriptor.
+ */
+struct usb_ep *usb_ep_autoconfig(
+ struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *desc
+)
+{
+ return usb_ep_autoconfig_ss(gadget, desc, NULL);
+}
+
+
+/**
+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state
+ * @gadget: device for which autoconfig state will be reset
+ *
+ * Use this for devices where one configuration may need to assign
+ * endpoint resources very differently from the next one. It clears
+ * state such as ep->driver_data and the record of assigned endpoints
+ * used by usb_ep_autoconfig().
+ */
+void usb_ep_autoconfig_reset (struct usb_gadget *gadget)
+{
+ struct usb_ep *ep;
+
+ list_for_each_entry (ep, &gadget->ep_list, ep_list) {
+ ep->driver_data = NULL;
+ }
+#ifdef MANY_ENDPOINTS
+ in_epnum = 0;
+#endif
+ epnum = 0;
+}
+
diff --git a/drivers/staging/ccg/f_acm.c b/drivers/staging/ccg/f_acm.c
new file mode 100644
index 0000000..d672250a
--- /dev/null
+++ b/drivers/staging/ccg/f_acm.c
@@ -0,0 +1,814 @@
+/*
+ * f_acm.c -- USB CDC serial (ACM) function driver
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ * Copyright (C) 2009 by Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+
+/*
+ * This CDC ACM function support just wraps control functions and
+ * notifications around the generic serial-over-usb code.
+ *
+ * Because CDC ACM is standardized by the USB-IF, many host operating
+ * systems have drivers for it. Accordingly, ACM is the preferred
+ * interop solution for serial-port type connections. The control
+ * models are often not necessary, and in any case don't do much in
+ * this bare-bones implementation.
+ *
+ * Note that even MS-Windows has some support for ACM. However, that
+ * support is somewhat broken because when you use ACM in a composite
+ * device, having multiple interfaces confuses the poor OS. It doesn't
+ * seem to understand CDC Union descriptors. The new "association"
+ * descriptors (roughly equivalent to CDC Unions) may sometimes help.
+ */
+
+struct f_acm {
+ struct gserial port;
+ u8 ctrl_id, data_id;
+ u8 port_num;
+
+ u8 pending;
+
+ /* lock is mostly for pending and notify_req ... they get accessed
+ * by callbacks both from tty (open/close/break) under its spinlock,
+ * and notify_req.complete() which can't use that lock.
+ */
+ spinlock_t lock;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+
+ /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
+ u16 port_handshake_bits;
+#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
+#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
+
+ /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
+ u16 serial_state;
+#define ACM_CTRL_OVERRUN (1 << 6)
+#define ACM_CTRL_PARITY (1 << 5)
+#define ACM_CTRL_FRAMING (1 << 4)
+#define ACM_CTRL_RI (1 << 3)
+#define ACM_CTRL_BRK (1 << 2)
+#define ACM_CTRL_DSR (1 << 1)
+#define ACM_CTRL_DCD (1 << 0)
+};
+
+static inline struct f_acm *func_to_acm(struct usb_function *f)
+{
+ return container_of(f, struct f_acm, port.func);
+}
+
+static inline struct f_acm *port_to_acm(struct gserial *p)
+{
+ return container_of(p, struct f_acm, port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* notification endpoint uses smallish and infrequent fixed-size messages */
+
+#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
+
+/* interface and class descriptors: */
+
+static struct usb_interface_assoc_descriptor
+acm_iad_descriptor = {
+ .bLength = sizeof acm_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ /* .bFirstInterface = DYNAMIC, */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ACM,
+ .bFunctionProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .iFunction = DYNAMIC */
+};
+
+
+static struct usb_interface_descriptor acm_control_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_interface_descriptor acm_data_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc acm_header_desc = {
+ .bLength = sizeof(acm_header_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor
+acm_call_mgmt_descriptor = {
+ .bLength = sizeof(acm_call_mgmt_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+ .bmCapabilities = 0,
+ /* .bDataInterface = DYNAMIC */
+};
+
+static struct usb_cdc_acm_descriptor acm_descriptor = {
+ .bLength = sizeof(acm_descriptor),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+ .bmCapabilities = USB_CDC_CAP_LINE,
+};
+
+static struct usb_cdc_union_desc acm_union_desc = {
+ .bLength = sizeof(acm_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor acm_fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+};
+
+static struct usb_endpoint_descriptor acm_fs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor acm_fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *acm_fs_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_fs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_fs_in_desc,
+ (struct usb_descriptor_header *) &acm_fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor acm_hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(GS_NOTIFY_MAXPACKET),
+ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+};
+
+static struct usb_endpoint_descriptor acm_hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor acm_hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *acm_hs_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_hs_notify_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_hs_in_desc,
+ (struct usb_descriptor_header *) &acm_hs_out_desc,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor acm_ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor acm_ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor acm_ss_bulk_comp_desc = {
+ .bLength = sizeof acm_ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *acm_ss_function[] = {
+ (struct usb_descriptor_header *) &acm_iad_descriptor,
+ (struct usb_descriptor_header *) &acm_control_interface_desc,
+ (struct usb_descriptor_header *) &acm_header_desc,
+ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &acm_descriptor,
+ (struct usb_descriptor_header *) &acm_union_desc,
+ (struct usb_descriptor_header *) &acm_hs_notify_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &acm_data_interface_desc,
+ (struct usb_descriptor_header *) &acm_ss_in_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &acm_ss_out_desc,
+ (struct usb_descriptor_header *) &acm_ss_bulk_comp_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+#define ACM_CTRL_IDX 0
+#define ACM_DATA_IDX 1
+#define ACM_IAD_IDX 2
+
+/* static strings, in UTF-8 */
+static struct usb_string acm_string_defs[] = {
+ [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
+ [ACM_DATA_IDX].s = "CDC ACM Data",
+ [ACM_IAD_IDX ].s = "CDC Serial",
+ { /* ZEROES END LIST */ },
+};
+
+static struct usb_gadget_strings acm_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = acm_string_defs,
+};
+
+static struct usb_gadget_strings *acm_strings[] = {
+ &acm_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM control ... data handling is delegated to tty library code.
+ * The main task of this function is to activate and deactivate
+ * that code based on device state; track parameters like line
+ * speed, handshake state, and so on; and issue notifications.
+ */
+
+static void acm_complete_set_line_coding(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct f_acm *acm = ep->driver_data;
+ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+
+ if (req->status != 0) {
+ DBG(cdev, "acm ttyGS%d completion, err %d\n",
+ acm->port_num, req->status);
+ return;
+ }
+
+ /* normal completion */
+ if (req->actual != sizeof(acm->port_line_coding)) {
+ DBG(cdev, "acm ttyGS%d short resp, len %d\n",
+ acm->port_num, req->actual);
+ usb_ep_set_halt(ep);
+ } else {
+ struct usb_cdc_line_coding *value = req->buf;
+
+ /* REVISIT: we currently just remember this data.
+ * If we change that, (a) validate it first, then
+ * (b) update whatever hardware needs updating,
+ * (c) worry about locking. This is information on
+ * the order of 9600-8-N-1 ... most of which means
+ * nothing unless we control a real RS232 line.
+ */
+ acm->port_line_coding = *value;
+ }
+}
+
+static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ *
+ * Note CDC spec table 4 lists the ACM request profile. It requires
+ * encapsulated command support ... we don't handle any, and respond
+ * to them by stalling. Options include get/set/clear comm features
+ * (not that useful) and SEND_BREAK.
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* SET_LINE_CODING ... just read and save what the host sends */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_LINE_CODING:
+ if (w_length != sizeof(struct usb_cdc_line_coding)
+ || w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = w_length;
+ cdev->gadget->ep0->driver_data = acm;
+ req->complete = acm_complete_set_line_coding;
+ break;
+
+ /* GET_LINE_CODING ... return what host sent, or initial value */
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_GET_LINE_CODING:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = min_t(unsigned, w_length,
+ sizeof(struct usb_cdc_line_coding));
+ memcpy(req->buf, &acm->port_line_coding, value);
+ break;
+
+ /* SET_CONTROL_LINE_STATE ... save what the host sent */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+ if (w_index != acm->ctrl_id)
+ goto invalid;
+
+ value = 0;
+
+ /* FIXME we should not allow data to flow until the
+ * host sets the ACM_CTRL_DTR bit; and when it clears
+ * that bit, we should return to that no-flow state.
+ */
+ acm->port_handshake_bits = w_value;
+ break;
+
+ default:
+invalid:
+ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
+ acm->port_num, ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = 0;
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "acm response on ttyGS%d, err %d\n",
+ acm->port_num, value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0, so this is an activation or a reset */
+
+ if (intf == acm->ctrl_id) {
+ if (acm->notify->driver_data) {
+ VDBG(cdev, "reset acm control interface %d\n", intf);
+ usb_ep_disable(acm->notify);
+ } else {
+ VDBG(cdev, "init acm ctrl interface %d\n", intf);
+ if (config_ep_by_speed(cdev->gadget, f, acm->notify))
+ return -EINVAL;
+ }
+ usb_ep_enable(acm->notify);
+ acm->notify->driver_data = acm;
+
+ } else if (intf == acm->data_id) {
+ if (acm->port.in->driver_data) {
+ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ }
+ if (!acm->port.in->desc || !acm->port.out->desc) {
+ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
+ if (config_ep_by_speed(cdev->gadget, f,
+ acm->port.in) ||
+ config_ep_by_speed(cdev->gadget, f,
+ acm->port.out)) {
+ acm->port.in->desc = NULL;
+ acm->port.out->desc = NULL;
+ return -EINVAL;
+ }
+ }
+ gserial_connect(&acm->port, acm->port_num);
+
+ } else
+ return -EINVAL;
+
+ return 0;
+}
+
+static void acm_disable(struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
+ gserial_disconnect(&acm->port);
+ usb_ep_disable(acm->notify);
+ acm->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * acm_cdc_notify - issue CDC notification to host
+ * @acm: wraps host to be notified
+ * @type: notification type
+ * @value: Refer to cdc specs, wValue field.
+ * @data: data to be sent
+ * @length: size of data
+ * Context: irqs blocked, acm->lock held, acm_notify_req non-null
+ *
+ * Returns zero on success or a negative errno.
+ *
+ * See section 6.3.5 of the CDC 1.1 specification for information
+ * about the only notification we issue: SerialState change.
+ */
+static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
+ void *data, unsigned length)
+{
+ struct usb_ep *ep = acm->notify;
+ struct usb_request *req;
+ struct usb_cdc_notification *notify;
+ const unsigned len = sizeof(*notify) + length;
+ void *buf;
+ int status;
+
+ req = acm->notify_req;
+ acm->notify_req = NULL;
+ acm->pending = false;
+
+ req->length = len;
+ notify = req->buf;
+ buf = notify + 1;
+
+ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
+ | USB_RECIP_INTERFACE;
+ notify->bNotificationType = type;
+ notify->wValue = cpu_to_le16(value);
+ notify->wIndex = cpu_to_le16(acm->ctrl_id);
+ notify->wLength = cpu_to_le16(length);
+ memcpy(buf, data, length);
+
+ /* ep_queue() can complete immediately if it fills the fifo... */
+ spin_unlock(&acm->lock);
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ spin_lock(&acm->lock);
+
+ if (status < 0) {
+ ERROR(acm->port.func.config->cdev,
+ "acm ttyGS%d can't notify serial state, %d\n",
+ acm->port_num, status);
+ acm->notify_req = req;
+ }
+
+ return status;
+}
+
+static int acm_notify_serial_state(struct f_acm *acm)
+{
+ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
+ int status;
+
+ spin_lock(&acm->lock);
+ if (acm->notify_req) {
+ DBG(cdev, "acm ttyGS%d serial state %04x\n",
+ acm->port_num, acm->serial_state);
+ status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
+ 0, &acm->serial_state, sizeof(acm->serial_state));
+ } else {
+ acm->pending = true;
+ status = 0;
+ }
+ spin_unlock(&acm->lock);
+ return status;
+}
+
+static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_acm *acm = req->context;
+ u8 doit = false;
+
+ /* on this call path we do NOT hold the port spinlock,
+ * which is why ACM needs its own spinlock
+ */
+ spin_lock(&acm->lock);
+ if (req->status != -ESHUTDOWN)
+ doit = acm->pending;
+ acm->notify_req = req;
+ spin_unlock(&acm->lock);
+
+ if (doit)
+ acm_notify_serial_state(acm);
+}
+
+/* connect == the TTY link is open */
+
+static void acm_connect(struct gserial *port)
+{
+ struct f_acm *acm = port_to_acm(port);
+
+ acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
+ acm_notify_serial_state(acm);
+}
+
+static void acm_disconnect(struct gserial *port)
+{
+ struct f_acm *acm = port_to_acm(port);
+
+ acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
+ acm_notify_serial_state(acm);
+}
+
+static int acm_send_break(struct gserial *port, int duration)
+{
+ struct f_acm *acm = port_to_acm(port);
+ u16 state;
+
+ state = acm->serial_state;
+ state &= ~ACM_CTRL_BRK;
+ if (duration)
+ state |= ACM_CTRL_BRK;
+
+ acm->serial_state = state;
+ return acm_notify_serial_state(acm);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ACM function driver setup/binding */
+static int
+acm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_acm *acm = func_to_acm(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs, and patch descriptors */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->ctrl_id = status;
+ acm_iad_descriptor.bFirstInterface = status;
+
+ acm_control_interface_desc.bInterfaceNumber = status;
+ acm_union_desc .bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ acm->data_id = status;
+
+ acm_data_interface_desc.bInterfaceNumber = status;
+ acm_union_desc.bSlaveInterface0 = status;
+ acm_call_mgmt_descriptor.bDataInterface = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
+ if (!ep)
+ goto fail;
+ acm->port.in = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
+ if (!ep)
+ goto fail;
+ acm->port.out = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
+ if (!ep)
+ goto fail;
+ acm->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* allocate notification */
+ acm->notify_req = gs_alloc_req(ep,
+ sizeof(struct usb_cdc_notification) + 2,
+ GFP_KERNEL);
+ if (!acm->notify_req)
+ goto fail;
+
+ acm->notify_req->complete = acm_cdc_notify_complete;
+ acm->notify_req->context = acm;
+
+ /* copy descriptors */
+ f->descriptors = usb_copy_descriptors(acm_fs_function);
+ if (!f->descriptors)
+ goto fail;
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ acm_hs_in_desc.bEndpointAddress =
+ acm_fs_in_desc.bEndpointAddress;
+ acm_hs_out_desc.bEndpointAddress =
+ acm_fs_out_desc.bEndpointAddress;
+ acm_hs_notify_desc.bEndpointAddress =
+ acm_fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors */
+ f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
+ }
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ acm_ss_in_desc.bEndpointAddress =
+ acm_fs_in_desc.bEndpointAddress;
+ acm_ss_out_desc.bEndpointAddress =
+ acm_fs_out_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(acm_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
+ }
+
+ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ acm->port_num,
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ acm->port.in->name, acm->port.out->name,
+ acm->notify->name);
+ return 0;
+
+fail:
+ if (acm->notify_req)
+ gs_free_req(acm->notify, acm->notify_req);
+
+ /* we might as well release our claims on endpoints */
+ if (acm->notify)
+ acm->notify->driver_data = NULL;
+ if (acm->port.out)
+ acm->port.out->driver_data = NULL;
+ if (acm->port.in)
+ acm->port.in->driver_data = NULL;
+
+ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
+
+ return status;
+}
+
+static void
+acm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_acm *acm = func_to_acm(f);
+
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
+ usb_free_descriptors(f->descriptors);
+ gs_free_req(acm->notify, acm->notify_req);
+ kfree(acm);
+}
+
+/* Some controllers can't support CDC ACM ... */
+static inline bool can_support_cdc(struct usb_configuration *c)
+{
+ /* everything else is *probably* fine ... */
+ return true;
+}
+
+/**
+ * acm_bind_config - add a CDC ACM function to a configuration
+ * @c: the configuration to support the CDC ACM instance
+ * @port_num: /dev/ttyGS* port this interface will use
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gserial_setup() with enough ports to
+ * handle all the ones it binds. Caller is also responsible
+ * for calling @gserial_cleanup() before module unload.
+ */
+int acm_bind_config(struct usb_configuration *c, u8 port_num)
+{
+ struct f_acm *acm;
+ int status;
+
+ if (!can_support_cdc(c))
+ return -EINVAL;
+
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string IDs, and patch descriptors */
+ if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_CTRL_IDX].id = status;
+
+ acm_control_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_DATA_IDX].id = status;
+
+ acm_data_interface_desc.iInterface = status;
+
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ acm_string_defs[ACM_IAD_IDX].id = status;
+
+ acm_iad_descriptor.iFunction = status;
+ }
+
+ /* allocate and initialize one new instance */
+ acm = kzalloc(sizeof *acm, GFP_KERNEL);
+ if (!acm)
+ return -ENOMEM;
+
+ spin_lock_init(&acm->lock);
+
+ acm->port_num = port_num;
+
+ acm->port.connect = acm_connect;
+ acm->port.disconnect = acm_disconnect;
+ acm->port.send_break = acm_send_break;
+
+ acm->port.func.name = "acm";
+ acm->port.func.strings = acm_strings;
+ /* descriptors are per-instance copies */
+ acm->port.func.bind = acm_bind;
+ acm->port.func.unbind = acm_unbind;
+ acm->port.func.set_alt = acm_set_alt;
+ acm->port.func.setup = acm_setup;
+ acm->port.func.disable = acm_disable;
+
+ status = usb_add_function(c, &acm->port.func);
+ if (status)
+ kfree(acm);
+ return status;
+}
diff --git a/drivers/staging/ccg/f_fs.c b/drivers/staging/ccg/f_fs.c
new file mode 100644
index 0000000..8adc79d
--- /dev/null
+++ b/drivers/staging/ccg/f_fs.c
@@ -0,0 +1,2455 @@
+/*
+ * f_fs.c -- user mode file system API for USB composite function controllers
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ *
+ * Based on inode.c (GadgetFS) which was:
+ * Copyright (C) 2003-2004 David Brownell
+ * Copyright (C) 2003 Agilent Technologies
+ *
+ * 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.
+ */
+
+
+/* #define DEBUG */
+/* #define VERBOSE_DEBUG */
+
+#include <linux/blkdev.h>
+#include <linux/pagemap.h>
+#include <linux/export.h>
+#include <linux/hid.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/composite.h>
+#include <linux/usb/functionfs.h>
+
+
+#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
+
+
+/* Debugging ****************************************************************/
+
+#ifdef VERBOSE_DEBUG
+# define pr_vdebug pr_debug
+# define ffs_dump_mem(prefix, ptr, len) \
+ print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
+#else
+# define pr_vdebug(...) do { } while (0)
+# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER() pr_vdebug("%s()\n", __func__)
+
+
+/* The data structure and setup file ****************************************/
+
+enum ffs_state {
+ /*
+ * Waiting for descriptors and strings.
+ *
+ * In this state no open(2), read(2) or write(2) on epfiles
+ * may succeed (which should not be the problem as there
+ * should be no such files opened in the first place).
+ */
+ FFS_READ_DESCRIPTORS,
+ FFS_READ_STRINGS,
+
+ /*
+ * We've got descriptors and strings. We are or have called
+ * functionfs_ready_callback(). functionfs_bind() may have
+ * been called but we don't know.
+ *
+ * This is the only state in which operations on epfiles may
+ * succeed.
+ */
+ FFS_ACTIVE,
+
+ /*
+ * All endpoints have been closed. This state is also set if
+ * we encounter an unrecoverable error. The only
+ * unrecoverable error is situation when after reading strings
+ * from user space we fail to initialise epfiles or
+ * functionfs_ready_callback() returns with error (<0).
+ *
+ * In this state no open(2), read(2) or write(2) (both on ep0
+ * as well as epfile) may succeed (at this point epfiles are
+ * unlinked and all closed so this is not a problem; ep0 is
+ * also closed but ep0 file exists and so open(2) on ep0 must
+ * fail).
+ */
+ FFS_CLOSING
+};
+
+
+enum ffs_setup_state {
+ /* There is no setup request pending. */
+ FFS_NO_SETUP,
+ /*
+ * User has read events and there was a setup request event
+ * there. The next read/write on ep0 will handle the
+ * request.
+ */
+ FFS_SETUP_PENDING,
+ /*
+ * There was event pending but before user space handled it
+ * some other event was introduced which canceled existing
+ * setup. If this state is set read/write on ep0 return
+ * -EIDRM. This state is only set when adding event.
+ */
+ FFS_SETUP_CANCELED
+};
+
+
+
+struct ffs_epfile;
+struct ffs_function;
+
+struct ffs_data {
+ struct usb_gadget *gadget;
+
+ /*
+ * Protect access read/write operations, only one read/write
+ * at a time. As a consequence protects ep0req and company.
+ * While setup request is being processed (queued) this is
+ * held.
+ */
+ struct mutex mutex;
+
+ /*
+ * Protect access to endpoint related structures (basically
+ * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+ * endpoint zero.
+ */
+ spinlock_t eps_lock;
+
+ /*
+ * XXX REVISIT do we need our own request? Since we are not
+ * handling setup requests immediately user space may be so
+ * slow that another setup will be sent to the gadget but this
+ * time not to us but another function and then there could be
+ * a race. Is that the case? Or maybe we can use cdev->req
+ * after all, maybe we just need some spinlock for that?
+ */
+ struct usb_request *ep0req; /* P: mutex */
+ struct completion ep0req_completion; /* P: mutex */
+ int ep0req_status; /* P: mutex */
+
+ /* reference counter */
+ atomic_t ref;
+ /* how many files are opened (EP0 and others) */
+ atomic_t opened;
+
+ /* EP0 state */
+ enum ffs_state state;
+
+ /*
+ * Possible transitions:
+ * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
+ * happens only in ep0 read which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
+ * happens only in ep0 i/o which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
+ * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
+ */
+ enum ffs_setup_state setup_state;
+
+#define FFS_SETUP_STATE(ffs) \
+ ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
+ FFS_SETUP_CANCELED, FFS_NO_SETUP))
+
+ /* Events & such. */
+ struct {
+ u8 types[4];
+ unsigned short count;
+ /* XXX REVISIT need to update it in some places, or do we? */
+ unsigned short can_stall;
+ struct usb_ctrlrequest setup;
+
+ wait_queue_head_t waitq;
+ } ev; /* the whole structure, P: ev.waitq.lock */
+
+ /* Flags */
+ unsigned long flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND 1
+
+ /* Active function */
+ struct ffs_function *func;
+
+ /*
+ * Device name, write once when file system is mounted.
+ * Intended for user to read if she wants.
+ */
+ const char *dev_name;
+ /* Private data for our user (ie. gadget). Managed by user. */
+ void *private_data;
+
+ /* filled by __ffs_data_got_descs() */
+ /*
+ * Real descriptors are 16 bytes after raw_descs (so you need
+ * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
+ * first full speed descriptor). raw_descs_length and
+ * raw_fs_descs_length do not have those 16 bytes added.
+ */
+ const void *raw_descs;
+ unsigned raw_descs_length;
+ unsigned raw_fs_descs_length;
+ unsigned fs_descs_count;
+ unsigned hs_descs_count;
+
+ unsigned short strings_count;
+ unsigned short interfaces_count;
+ unsigned short eps_count;
+ unsigned short _pad1;
+
+ /* filled by __ffs_data_got_strings() */
+ /* ids in stringtabs are set in functionfs_bind() */
+ const void *raw_strings;
+ struct usb_gadget_strings **stringtabs;
+
+ /*
+ * File system's super block, write once when file system is
+ * mounted.
+ */
+ struct super_block *sb;
+
+ /* File permissions, written once when fs is mounted */
+ struct ffs_file_perms {
+ umode_t mode;
+ uid_t uid;
+ gid_t gid;
+ } file_perms;
+
+ /*
+ * The endpoint files, filled by ffs_epfiles_create(),
+ * destroyed by ffs_epfiles_destroy().
+ */
+ struct ffs_epfile *epfiles;
+};
+
+/* Reference counter handling */
+static void ffs_data_get(struct ffs_data *ffs);
+static void ffs_data_put(struct ffs_data *ffs);
+/* Creates new ffs_data object. */
+static struct ffs_data *__must_check ffs_data_new(void) __attribute__((malloc));
+
+/* Opened counter handling. */
+static void ffs_data_opened(struct ffs_data *ffs);
+static void ffs_data_closed(struct ffs_data *ffs);
+
+/* Called with ffs->mutex held; take over ownership of data. */
+static int __must_check
+__ffs_data_got_descs(struct ffs_data *ffs, char *data, size_t len);
+static int __must_check
+__ffs_data_got_strings(struct ffs_data *ffs, char *data, size_t len);
+
+
+/* The function structure ***************************************************/
+
+struct ffs_ep;
+
+struct ffs_function {
+ struct usb_configuration *conf;
+ struct usb_gadget *gadget;
+ struct ffs_data *ffs;
+
+ struct ffs_ep *eps;
+ u8 eps_revmap[16];
+ short *interfaces_nums;
+
+ struct usb_function function;
+};
+
+
+static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
+{
+ return container_of(f, struct ffs_function, function);
+}
+
+static void ffs_func_free(struct ffs_function *func);
+
+static void ffs_func_eps_disable(struct ffs_function *func);
+static int __must_check ffs_func_eps_enable(struct ffs_function *func);
+
+static int ffs_func_bind(struct usb_configuration *,
+ struct usb_function *);
+static void ffs_func_unbind(struct usb_configuration *,
+ struct usb_function *);
+static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
+static void ffs_func_disable(struct usb_function *);
+static int ffs_func_setup(struct usb_function *,
+ const struct usb_ctrlrequest *);
+static void ffs_func_suspend(struct usb_function *);
+static void ffs_func_resume(struct usb_function *);
+
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num);
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf);
+
+
+/* The endpoints structures *************************************************/
+
+struct ffs_ep {
+ struct usb_ep *ep; /* P: ffs->eps_lock */
+ struct usb_request *req; /* P: epfile->mutex */
+
+ /* [0]: full speed, [1]: high speed */
+ struct usb_endpoint_descriptor *descs[2];
+
+ u8 num;
+
+ int status; /* P: epfile->mutex */
+};
+
+struct ffs_epfile {
+ /* Protects ep->ep and ep->req. */
+ struct mutex mutex;
+ wait_queue_head_t wait;
+
+ struct ffs_data *ffs;
+ struct ffs_ep *ep; /* P: ffs->eps_lock */
+
+ struct dentry *dentry;
+
+ char name[5];
+
+ unsigned char in; /* P: ffs->eps_lock */
+ unsigned char isoc; /* P: ffs->eps_lock */
+
+ unsigned char _pad;
+};
+
+static int __must_check ffs_epfiles_create(struct ffs_data *ffs);
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
+
+static struct inode *__must_check
+ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
+ const struct file_operations *fops,
+ struct dentry **dentry_p);
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+ __attribute__((warn_unused_result, nonnull));
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+ __attribute__((warn_unused_result, nonnull));
+
+
+/* Control file aka ep0 *****************************************************/
+
+static void ffs_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct ffs_data *ffs = req->context;
+
+ complete_all(&ffs->ep0req_completion);
+}
+
+static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+{
+ struct usb_request *req = ffs->ep0req;
+ int ret;
+
+ req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ req->buf = data;
+ req->length = len;
+
+ /*
+ * UDC layer requires to provide a buffer even for ZLP, but should
+ * not use it at all. Let's provide some poisoned pointer to catch
+ * possible bug in the driver.
+ */
+ if (req->buf == NULL)
+ req->buf = (void *)0xDEADBABE;
+
+ INIT_COMPLETION(ffs->ep0req_completion);
+
+ ret = usb_ep_queue(ffs->gadget->ep0, req, GFP_ATOMIC);
+ if (unlikely(ret < 0))
+ return ret;
+
+ ret = wait_for_completion_interruptible(&ffs->ep0req_completion);
+ if (unlikely(ret)) {
+ usb_ep_dequeue(ffs->gadget->ep0, req);
+ return -EINTR;
+ }
+
+ ffs->setup_state = FFS_NO_SETUP;
+ return ffs->ep0req_status;
+}
+
+static int __ffs_ep0_stall(struct ffs_data *ffs)
+{
+ if (ffs->ev.can_stall) {
+ pr_vdebug("ep0 stall\n");
+ usb_ep_set_halt(ffs->gadget->ep0);
+ ffs->setup_state = FFS_NO_SETUP;
+ return -EL2HLT;
+ } else {
+ pr_debug("bogus ep0 stall!\n");
+ return -ESRCH;
+ }
+}
+
+static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ ssize_t ret;
+ char *data;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+ /* Check state */
+ switch (ffs->state) {
+ case FFS_READ_DESCRIPTORS:
+ case FFS_READ_STRINGS:
+ /* Copy data */
+ if (unlikely(len < 16)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ data = ffs_prepare_buffer(buf, len);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ /* Handle data */
+ if (ffs->state == FFS_READ_DESCRIPTORS) {
+ pr_info("read descriptors\n");
+ ret = __ffs_data_got_descs(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ffs->state = FFS_READ_STRINGS;
+ ret = len;
+ } else {
+ pr_info("read strings\n");
+ ret = __ffs_data_got_strings(ffs, data, len);
+ if (unlikely(ret < 0))
+ break;
+
+ ret = ffs_epfiles_create(ffs);
+ if (unlikely(ret)) {
+ ffs->state = FFS_CLOSING;
+ break;
+ }
+
+ ffs->state = FFS_ACTIVE;
+ mutex_unlock(&ffs->mutex);
+
+ ret = functionfs_ready_callback(ffs);
+ if (unlikely(ret < 0)) {
+ ffs->state = FFS_CLOSING;
+ return ret;
+ }
+
+ set_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags);
+ return len;
+ }
+ break;
+
+ case FFS_ACTIVE:
+ data = NULL;
+ /*
+ * We're called from user space, we can use _irq
+ * rather then _irqsave
+ */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ goto done_spin;
+
+ case FFS_NO_SETUP:
+ ret = -ESRCH;
+ goto done_spin;
+
+ case FFS_SETUP_PENDING:
+ break;
+ }
+
+ /* FFS_SETUP_PENDING */
+ if (!(ffs->ev.setup.bRequestType & USB_DIR_IN)) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ break;
+ }
+
+ /* FFS_SETUP_PENDING and not stall */
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ data = ffs_prepare_buffer(buf, len);
+ if (IS_ERR(data)) {
+ ret = PTR_ERR(data);
+ break;
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /*
+ * We are guaranteed to be still in FFS_ACTIVE state
+ * but the state of setup could have changed from
+ * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+ * to check for that. If that happened we copied data
+ * from user space in vain but it's unlikely.
+ *
+ * For sure we are not in FFS_NO_SETUP since this is
+ * the only place FFS_SETUP_PENDING -> FFS_NO_SETUP
+ * transition can be performed and it's protected by
+ * mutex.
+ */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ ret = -EIDRM;
+done_spin:
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ } else {
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ }
+ kfree(data);
+ break;
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+ mutex_unlock(&ffs->mutex);
+ return ret;
+}
+
+static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
+ size_t n)
+{
+ /*
+ * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
+ * to release them.
+ */
+ struct usb_functionfs_event events[n];
+ unsigned i = 0;
+
+ memset(events, 0, sizeof events);
+
+ do {
+ events[i].type = ffs->ev.types[i];
+ if (events[i].type == FUNCTIONFS_SETUP) {
+ events[i].u.setup = ffs->ev.setup;
+ ffs->setup_state = FFS_SETUP_PENDING;
+ }
+ } while (++i < n);
+
+ if (n < ffs->ev.count) {
+ ffs->ev.count -= n;
+ memmove(ffs->ev.types, ffs->ev.types + n,
+ ffs->ev.count * sizeof *ffs->ev.types);
+ } else {
+ ffs->ev.count = 0;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ mutex_unlock(&ffs->mutex);
+
+ return unlikely(__copy_to_user(buf, events, sizeof events))
+ ? -EFAULT : sizeof events;
+}
+
+static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ptr)
+{
+ struct ffs_data *ffs = file->private_data;
+ char *data = NULL;
+ size_t n;
+ int ret;
+
+ ENTER();
+
+ /* Fast check if setup was canceled */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+ return -EIDRM;
+
+ /* Acquire mutex */
+ ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret < 0))
+ return ret;
+
+ /* Check state */
+ if (ffs->state != FFS_ACTIVE) {
+ ret = -EBADFD;
+ goto done_mutex;
+ }
+
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ switch (FFS_SETUP_STATE(ffs)) {
+ case FFS_SETUP_CANCELED:
+ ret = -EIDRM;
+ break;
+
+ case FFS_NO_SETUP:
+ n = len / sizeof(struct usb_functionfs_event);
+ if (unlikely(!n)) {
+ ret = -EINVAL;
+ break;
+ }
+
+ if ((file->f_flags & O_NONBLOCK) && !ffs->ev.count) {
+ ret = -EAGAIN;
+ break;
+ }
+
+ if (wait_event_interruptible_exclusive_locked_irq(ffs->ev.waitq,
+ ffs->ev.count)) {
+ ret = -EINTR;
+ break;
+ }
+
+ return __ffs_ep0_read_events(ffs, buf,
+ min(n, (size_t)ffs->ev.count));
+
+ case FFS_SETUP_PENDING:
+ if (ffs->ev.setup.bRequestType & USB_DIR_IN) {
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+ ret = __ffs_ep0_stall(ffs);
+ goto done_mutex;
+ }
+
+ len = min(len, (size_t)le16_to_cpu(ffs->ev.setup.wLength));
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+
+ if (likely(len)) {
+ data = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!data)) {
+ ret = -ENOMEM;
+ goto done_mutex;
+ }
+ }
+
+ spin_lock_irq(&ffs->ev.waitq.lock);
+
+ /* See ffs_ep0_write() */
+ if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+ ret = -EIDRM;
+ break;
+ }
+
+ /* unlocks spinlock */
+ ret = __ffs_ep0_queue_wait(ffs, data, len);
+ if (likely(ret > 0) && unlikely(__copy_to_user(buf, data, len)))
+ ret = -EFAULT;
+ goto done_mutex;
+
+ default:
+ ret = -EBADFD;
+ break;
+ }
+
+ spin_unlock_irq(&ffs->ev.waitq.lock);
+done_mutex:
+ mutex_unlock(&ffs->mutex);
+ kfree(data);
+ return ret;
+}
+
+static int ffs_ep0_open(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = inode->i_private;
+
+ ENTER();
+
+ if (unlikely(ffs->state == FFS_CLOSING))
+ return -EBUSY;
+
+ file->private_data = ffs;
+ ffs_data_opened(ffs);
+
+ return 0;
+}
+
+static int ffs_ep0_release(struct inode *inode, struct file *file)
+{
+ struct ffs_data *ffs = file->private_data;
+
+ ENTER();
+
+ ffs_data_closed(ffs);
+
+ return 0;
+}
+
+static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
+{
+ struct ffs_data *ffs = file->private_data;
+ struct usb_gadget *gadget = ffs->gadget;
+ long ret;
+
+ ENTER();
+
+ if (code == FUNCTIONFS_INTERFACE_REVMAP) {
+ struct ffs_function *func = ffs->func;
+ ret = func ? ffs_func_revmap_intf(func, value) : -ENODEV;
+ } else if (gadget && gadget->ops->ioctl) {
+ ret = gadget->ops->ioctl(gadget, code, value);
+ } else {
+ ret = -ENOTTY;
+ }
+
+ return ret;
+}
+
+static const struct file_operations ffs_ep0_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_ep0_open,
+ .write = ffs_ep0_write,
+ .read = ffs_ep0_read,
+ .release = ffs_ep0_release,
+ .unlocked_ioctl = ffs_ep0_ioctl,
+};
+
+
+/* "Normal" endpoints operations ********************************************/
+
+static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
+{
+ ENTER();
+ if (likely(req->context)) {
+ struct ffs_ep *ep = _ep->driver_data;
+ ep->status = req->status ? req->status : req->actual;
+ complete(req->context);
+ }
+}
+
+static ssize_t ffs_epfile_io(struct file *file,
+ char __user *buf, size_t len, int read)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ struct ffs_ep *ep;
+ char *data = NULL;
+ ssize_t ret;
+ int halt;
+
+ goto first_try;
+ do {
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ mutex_unlock(&epfile->mutex);
+
+first_try:
+ /* Are we still active? */
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+ ret = -ENODEV;
+ goto error;
+ }
+
+ /* Wait for endpoint to be enabled */
+ ep = epfile->ep;
+ if (!ep) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
+ goto error;
+ }
+
+ if (wait_event_interruptible(epfile->wait,
+ (ep = epfile->ep))) {
+ ret = -EINTR;
+ goto error;
+ }
+ }
+
+ /* Do we halt? */
+ halt = !read == !epfile->in;
+ if (halt && epfile->isoc) {
+ ret = -EINVAL;
+ goto error;
+ }
+
+ /* Allocate & copy */
+ if (!halt && !data) {
+ data = kzalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ if (!read &&
+ unlikely(__copy_from_user(data, buf, len))) {
+ ret = -EFAULT;
+ goto error;
+ }
+ }
+
+ /* We will be using request */
+ ret = ffs_mutex_lock(&epfile->mutex,
+ file->f_flags & O_NONBLOCK);
+ if (unlikely(ret))
+ goto error;
+
+ /*
+ * We're called from user space, we can use _irq rather then
+ * _irqsave
+ */
+ spin_lock_irq(&epfile->ffs->eps_lock);
+
+ /*
+ * While we were acquiring mutex endpoint got disabled
+ * or changed?
+ */
+ } while (unlikely(epfile->ep != ep));
+
+ /* Halt */
+ if (unlikely(halt)) {
+ if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
+ usb_ep_set_halt(ep->ep);
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ ret = -EBADMSG;
+ } else {
+ /* Fire the request */
+ DECLARE_COMPLETION_ONSTACK(done);
+
+ struct usb_request *req = ep->req;
+ req->context = &done;
+ req->complete = ffs_epfile_io_complete;
+ req->buf = data;
+ req->length = len;
+
+ ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ if (unlikely(ret < 0)) {
+ /* nop */
+ } else if (unlikely(wait_for_completion_interruptible(&done))) {
+ ret = -EINTR;
+ usb_ep_dequeue(ep->ep, req);
+ } else {
+ ret = ep->status;
+ if (read && ret > 0 &&
+ unlikely(copy_to_user(buf, data, ret)))
+ ret = -EFAULT;
+ }
+ }
+
+ mutex_unlock(&epfile->mutex);
+error:
+ kfree(data);
+ return ret;
+}
+
+static ssize_t
+ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
+ loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, (char __user *)buf, len, 0);
+}
+
+static ssize_t
+ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
+{
+ ENTER();
+
+ return ffs_epfile_io(file, buf, len, 1);
+}
+
+static int
+ffs_epfile_open(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ file->private_data = epfile;
+ ffs_data_opened(epfile->ffs);
+
+ return 0;
+}
+
+static int
+ffs_epfile_release(struct inode *inode, struct file *file)
+{
+ struct ffs_epfile *epfile = inode->i_private;
+
+ ENTER();
+
+ ffs_data_closed(epfile->ffs);
+
+ return 0;
+}
+
+static long ffs_epfile_ioctl(struct file *file, unsigned code,
+ unsigned long value)
+{
+ struct ffs_epfile *epfile = file->private_data;
+ int ret;
+
+ ENTER();
+
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE))
+ return -ENODEV;
+
+ spin_lock_irq(&epfile->ffs->eps_lock);
+ if (likely(epfile->ep)) {
+ switch (code) {
+ case FUNCTIONFS_FIFO_STATUS:
+ ret = usb_ep_fifo_status(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_FIFO_FLUSH:
+ usb_ep_fifo_flush(epfile->ep->ep);
+ ret = 0;
+ break;
+ case FUNCTIONFS_CLEAR_HALT:
+ ret = usb_ep_clear_halt(epfile->ep->ep);
+ break;
+ case FUNCTIONFS_ENDPOINT_REVMAP:
+ ret = epfile->ep->num;
+ break;
+ default:
+ ret = -ENOTTY;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+
+ return ret;
+}
+
+static const struct file_operations ffs_epfile_operations = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+
+ .open = ffs_epfile_open,
+ .write = ffs_epfile_write,
+ .read = ffs_epfile_read,
+ .release = ffs_epfile_release,
+ .unlocked_ioctl = ffs_epfile_ioctl,
+};
+
+
+/* File system and super block operations ***********************************/
+
+/*
+ * Mounting the file system creates a controller file, used first for
+ * function configuration then later for event monitoring.
+ */
+
+static struct inode *__must_check
+ffs_sb_make_inode(struct super_block *sb, void *data,
+ const struct file_operations *fops,
+ const struct inode_operations *iops,
+ struct ffs_file_perms *perms)
+{
+ struct inode *inode;
+
+ ENTER();
+
+ inode = new_inode(sb);
+
+ if (likely(inode)) {
+ struct timespec current_time = CURRENT_TIME;
+
+ inode->i_ino = get_next_ino();
+ inode->i_mode = perms->mode;
+ inode->i_uid = perms->uid;
+ inode->i_gid = perms->gid;
+ inode->i_atime = current_time;
+ inode->i_mtime = current_time;
+ inode->i_ctime = current_time;
+ inode->i_private = data;
+ if (fops)
+ inode->i_fop = fops;
+ if (iops)
+ inode->i_op = iops;
+ }
+
+ return inode;
+}
+
+/* Create "regular" file */
+static struct inode *ffs_sb_create_file(struct super_block *sb,
+ const char *name, void *data,
+ const struct file_operations *fops,
+ struct dentry **dentry_p)
+{
+ struct ffs_data *ffs = sb->s_fs_info;
+ struct dentry *dentry;
+ struct inode *inode;
+
+ ENTER();
+
+ dentry = d_alloc_name(sb->s_root, name);
+ if (unlikely(!dentry))
+ return NULL;
+
+ inode = ffs_sb_make_inode(sb, data, fops, NULL, &ffs->file_perms);
+ if (unlikely(!inode)) {
+ dput(dentry);
+ return NULL;
+ }
+
+ d_add(dentry, inode);
+ if (dentry_p)
+ *dentry_p = dentry;
+
+ return inode;
+}
+
+/* Super block */
+static const struct super_operations ffs_sb_operations = {
+ .statfs = simple_statfs,
+ .drop_inode = generic_delete_inode,
+};
+
+struct ffs_sb_fill_data {
+ struct ffs_file_perms perms;
+ umode_t root_mode;
+ const char *dev_name;
+ union {
+ /* set by ffs_fs_mount(), read by ffs_sb_fill() */
+ void *private_data;
+ /* set by ffs_sb_fill(), read by ffs_fs_mount */
+ struct ffs_data *ffs_data;
+ };
+};
+
+static int ffs_sb_fill(struct super_block *sb, void *_data, int silent)
+{
+ struct ffs_sb_fill_data *data = _data;
+ struct inode *inode;
+ struct ffs_data *ffs;
+
+ ENTER();
+
+ /* Initialise data */
+ ffs = ffs_data_new();
+ if (unlikely(!ffs))
+ goto Enomem;
+
+ ffs->sb = sb;
+ ffs->dev_name = kstrdup(data->dev_name, GFP_KERNEL);
+ if (unlikely(!ffs->dev_name))
+ goto Enomem;
+ ffs->file_perms = data->perms;
+ ffs->private_data = data->private_data;
+
+ /* used by the caller of this function */
+ data->ffs_data = ffs;
+
+ sb->s_fs_info = ffs;
+ sb->s_blocksize = PAGE_CACHE_SIZE;
+ sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+ sb->s_magic = FUNCTIONFS_MAGIC;
+ sb->s_op = &ffs_sb_operations;
+ sb->s_time_gran = 1;
+
+ /* Root inode */
+ data->perms.mode = data->root_mode;
+ inode = ffs_sb_make_inode(sb, NULL,
+ &simple_dir_operations,
+ &simple_dir_inode_operations,
+ &data->perms);
+ sb->s_root = d_make_root(inode);
+ if (unlikely(!sb->s_root))
+ goto Enomem;
+
+ /* EP0 file */
+ if (unlikely(!ffs_sb_create_file(sb, "ep0", ffs,
+ &ffs_ep0_operations, NULL)))
+ goto Enomem;
+
+ return 0;
+
+Enomem:
+ return -ENOMEM;
+}
+
+static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
+{
+ ENTER();
+
+ if (!opts || !*opts)
+ return 0;
+
+ for (;;) {
+ char *end, *eq, *comma;
+ unsigned long value;
+
+ /* Option limit */
+ comma = strchr(opts, ',');
+ if (comma)
+ *comma = 0;
+
+ /* Value limit */
+ eq = strchr(opts, '=');
+ if (unlikely(!eq)) {
+ pr_err("'=' missing in %s\n", opts);
+ return -EINVAL;
+ }
+ *eq = 0;
+
+ /* Parse value */
+ value = simple_strtoul(eq + 1, &end, 0);
+ if (unlikely(*end != ',' && *end != 0)) {
+ pr_err("%s: invalid value: %s\n", opts, eq + 1);
+ return -EINVAL;
+ }
+
+ /* Interpret option */
+ switch (eq - opts) {
+ case 5:
+ if (!memcmp(opts, "rmode", 5))
+ data->root_mode = (value & 0555) | S_IFDIR;
+ else if (!memcmp(opts, "fmode", 5))
+ data->perms.mode = (value & 0666) | S_IFREG;
+ else
+ goto invalid;
+ break;
+
+ case 4:
+ if (!memcmp(opts, "mode", 4)) {
+ data->root_mode = (value & 0555) | S_IFDIR;
+ data->perms.mode = (value & 0666) | S_IFREG;
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 3:
+ if (!memcmp(opts, "uid", 3))
+ data->perms.uid = value;
+ else if (!memcmp(opts, "gid", 3))
+ data->perms.gid = value;
+ else
+ goto invalid;
+ break;
+
+ default:
+invalid:
+ pr_err("%s: invalid option\n", opts);
+ return -EINVAL;
+ }
+
+ /* Next iteration */
+ if (!comma)
+ break;
+ opts = comma + 1;
+ }
+
+ return 0;
+}
+
+/* "mount -t functionfs dev_name /dev/function" ends up here */
+
+static struct dentry *
+ffs_fs_mount(struct file_system_type *t, int flags,
+ const char *dev_name, void *opts)
+{
+ struct ffs_sb_fill_data data = {
+ .perms = {
+ .mode = S_IFREG | 0600,
+ .uid = 0,
+ .gid = 0
+ },
+ .root_mode = S_IFDIR | 0500,
+ };
+ struct dentry *rv;
+ int ret;
+ void *ffs_dev;
+
+ ENTER();
+
+ ret = ffs_fs_parse_opts(&data, opts);
+ if (unlikely(ret < 0))
+ return ERR_PTR(ret);
+
+ ffs_dev = functionfs_acquire_dev_callback(dev_name);
+ if (IS_ERR(ffs_dev))
+ return ffs_dev;
+
+ data.dev_name = dev_name;
+ data.private_data = ffs_dev;
+ rv = mount_nodev(t, flags, &data, ffs_sb_fill);
+
+ /* data.ffs_data is set by ffs_sb_fill */
+ if (IS_ERR(rv))
+ functionfs_release_dev_callback(data.ffs_data);
+
+ return rv;
+}
+
+static void
+ffs_fs_kill_sb(struct super_block *sb)
+{
+ ENTER();
+
+ kill_litter_super(sb);
+ if (sb->s_fs_info) {
+ functionfs_release_dev_callback(sb->s_fs_info);
+ ffs_data_put(sb->s_fs_info);
+ }
+}
+
+static struct file_system_type ffs_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "functionfs",
+ .mount = ffs_fs_mount,
+ .kill_sb = ffs_fs_kill_sb,
+};
+
+
+/* Driver's main init/cleanup functions *************************************/
+
+static int functionfs_init(void)
+{
+ int ret;
+
+ ENTER();
+
+ ret = register_filesystem(&ffs_fs_type);
+ if (likely(!ret))
+ pr_info("file system registered\n");
+ else
+ pr_err("failed registering file system (%d)\n", ret);
+
+ return ret;
+}
+
+static void functionfs_cleanup(void)
+{
+ ENTER();
+
+ pr_info("unloading\n");
+ unregister_filesystem(&ffs_fs_type);
+}
+
+
+/* ffs_data and ffs_function construction and destruction code **************/
+
+static void ffs_data_clear(struct ffs_data *ffs);
+static void ffs_data_reset(struct ffs_data *ffs);
+
+static void ffs_data_get(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+}
+
+static void ffs_data_opened(struct ffs_data *ffs)
+{
+ ENTER();
+
+ atomic_inc(&ffs->ref);
+ atomic_inc(&ffs->opened);
+}
+
+static void ffs_data_put(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (unlikely(atomic_dec_and_test(&ffs->ref))) {
+ pr_info("%s(): freeing\n", __func__);
+ ffs_data_clear(ffs);
+ BUG_ON(waitqueue_active(&ffs->ev.waitq) ||
+ waitqueue_active(&ffs->ep0req_completion.wait));
+ kfree(ffs->dev_name);
+ kfree(ffs);
+ }
+}
+
+static void ffs_data_closed(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (atomic_dec_and_test(&ffs->opened)) {
+ ffs->state = FFS_CLOSING;
+ ffs_data_reset(ffs);
+ }
+
+ ffs_data_put(ffs);
+}
+
+static struct ffs_data *ffs_data_new(void)
+{
+ struct ffs_data *ffs = kzalloc(sizeof *ffs, GFP_KERNEL);
+ if (unlikely(!ffs))
+ return 0;
+
+ ENTER();
+
+ atomic_set(&ffs->ref, 1);
+ atomic_set(&ffs->opened, 0);
+ ffs->state = FFS_READ_DESCRIPTORS;
+ mutex_init(&ffs->mutex);
+ spin_lock_init(&ffs->eps_lock);
+ init_waitqueue_head(&ffs->ev.waitq);
+ init_completion(&ffs->ep0req_completion);
+
+ /* XXX REVISIT need to update it in some places, or do we? */
+ ffs->ev.can_stall = 1;
+
+ return ffs;
+}
+
+static void ffs_data_clear(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
+ functionfs_closed_callback(ffs);
+
+ BUG_ON(ffs->gadget);
+
+ if (ffs->epfiles)
+ ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
+
+ kfree(ffs->raw_descs);
+ kfree(ffs->raw_strings);
+ kfree(ffs->stringtabs);
+}
+
+static void ffs_data_reset(struct ffs_data *ffs)
+{
+ ENTER();
+
+ ffs_data_clear(ffs);
+
+ ffs->epfiles = NULL;
+ ffs->raw_descs = NULL;
+ ffs->raw_strings = NULL;
+ ffs->stringtabs = NULL;
+
+ ffs->raw_descs_length = 0;
+ ffs->raw_fs_descs_length = 0;
+ ffs->fs_descs_count = 0;
+ ffs->hs_descs_count = 0;
+
+ ffs->strings_count = 0;
+ ffs->interfaces_count = 0;
+ ffs->eps_count = 0;
+
+ ffs->ev.count = 0;
+
+ ffs->state = FFS_READ_DESCRIPTORS;
+ ffs->setup_state = FFS_NO_SETUP;
+ ffs->flags = 0;
+}
+
+
+static int functionfs_bind(struct ffs_data *ffs, struct usb_composite_dev *cdev)
+{
+ struct usb_gadget_strings **lang;
+ int first_id;
+
+ ENTER();
+
+ if (WARN_ON(ffs->state != FFS_ACTIVE
+ || test_and_set_bit(FFS_FL_BOUND, &ffs->flags)))
+ return -EBADFD;
+
+ first_id = usb_string_ids_n(cdev, ffs->strings_count);
+ if (unlikely(first_id < 0))
+ return first_id;
+
+ ffs->ep0req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
+ if (unlikely(!ffs->ep0req))
+ return -ENOMEM;
+ ffs->ep0req->complete = ffs_ep0_complete;
+ ffs->ep0req->context = ffs;
+
+ lang = ffs->stringtabs;
+ for (lang = ffs->stringtabs; *lang; ++lang) {
+ struct usb_string *str = (*lang)->strings;
+ int id = first_id;
+ for (; str->s; ++id, ++str)
+ str->id = id;
+ }
+
+ ffs->gadget = cdev->gadget;
+ ffs_data_get(ffs);
+ return 0;
+}
+
+static void functionfs_unbind(struct ffs_data *ffs)
+{
+ ENTER();
+
+ if (!WARN_ON(!ffs->gadget)) {
+ usb_ep_free_request(ffs->gadget->ep0, ffs->ep0req);
+ ffs->ep0req = NULL;
+ ffs->gadget = NULL;
+ ffs_data_put(ffs);
+ clear_bit(FFS_FL_BOUND, &ffs->flags);
+ }
+}
+
+static int ffs_epfiles_create(struct ffs_data *ffs)
+{
+ struct ffs_epfile *epfile, *epfiles;
+ unsigned i, count;
+
+ ENTER();
+
+ count = ffs->eps_count;
+ epfiles = kcalloc(count, sizeof(*epfiles), GFP_KERNEL);
+ if (!epfiles)
+ return -ENOMEM;
+
+ epfile = epfiles;
+ for (i = 1; i <= count; ++i, ++epfile) {
+ epfile->ffs = ffs;
+ mutex_init(&epfile->mutex);
+ init_waitqueue_head(&epfile->wait);
+ sprintf(epfiles->name, "ep%u", i);
+ if (!unlikely(ffs_sb_create_file(ffs->sb, epfiles->name, epfile,
+ &ffs_epfile_operations,
+ &epfile->dentry))) {
+ ffs_epfiles_destroy(epfiles, i - 1);
+ return -ENOMEM;
+ }
+ }
+
+ ffs->epfiles = epfiles;
+ return 0;
+}
+
+static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
+{
+ struct ffs_epfile *epfile = epfiles;
+
+ ENTER();
+
+ for (; count; --count, ++epfile) {
+ BUG_ON(mutex_is_locked(&epfile->mutex) ||
+ waitqueue_active(&epfile->wait));
+ if (epfile->dentry) {
+ d_delete(epfile->dentry);
+ dput(epfile->dentry);
+ epfile->dentry = NULL;
+ }
+ }
+
+ kfree(epfiles);
+}
+
+static int functionfs_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct ffs_data *ffs)
+{
+ struct ffs_function *func;
+ int ret;
+
+ ENTER();
+
+ func = kzalloc(sizeof *func, GFP_KERNEL);
+ if (unlikely(!func))
+ return -ENOMEM;
+
+ func->function.name = "Function FS Gadget";
+ func->function.strings = ffs->stringtabs;
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+
+ func->conf = c;
+ func->gadget = cdev->gadget;
+ func->ffs = ffs;
+ ffs_data_get(ffs);
+
+ ret = usb_add_function(c, &func->function);
+ if (unlikely(ret))
+ ffs_func_free(func);
+
+ return ret;
+}
+
+static void ffs_func_free(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ ENTER();
+
+ /* cleanup after autoconfig */
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ if (ep->ep && ep->req)
+ usb_ep_free_request(ep->ep, ep->req);
+ ep->req = NULL;
+ ++ep;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ ffs_data_put(func->ffs);
+
+ kfree(func->eps);
+ /*
+ * eps and interfaces_nums are allocated in the same chunk so
+ * only one free is required. Descriptors are also allocated
+ * in the same chunk.
+ */
+
+ kfree(func);
+}
+
+static void ffs_func_eps_disable(struct ffs_function *func)
+{
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = func->ffs->epfiles;
+ unsigned count = func->ffs->eps_count;
+ unsigned long flags;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ /* pending requests get nuked */
+ if (likely(ep->ep))
+ usb_ep_disable(ep->ep);
+ epfile->ep = NULL;
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+}
+
+static int ffs_func_eps_enable(struct ffs_function *func)
+{
+ struct ffs_data *ffs = func->ffs;
+ struct ffs_ep *ep = func->eps;
+ struct ffs_epfile *epfile = ffs->epfiles;
+ unsigned count = ffs->eps_count;
+ unsigned long flags;
+ int ret = 0;
+
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ struct usb_endpoint_descriptor *ds;
+ ds = ep->descs[ep->descs[1] ? 1 : 0];
+
+ ep->ep->driver_data = ep;
+ ep->ep->desc = ds;
+ ret = usb_ep_enable(ep->ep);
+ if (likely(!ret)) {
+ epfile->ep = ep;
+ epfile->in = usb_endpoint_dir_in(ds);
+ epfile->isoc = usb_endpoint_xfer_isoc(ds);
+ } else {
+ break;
+ }
+
+ wake_up(&epfile->wait);
+
+ ++ep;
+ ++epfile;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+
+ return ret;
+}
+
+
+/* Parsing and building descriptors and strings *****************************/
+
+/*
+ * This validates if data pointed by data is a valid USB descriptor as
+ * well as record how many interfaces, endpoints and strings are
+ * required by given configuration. Returns address after the
+ * descriptor or NULL if data is invalid.
+ */
+
+enum ffs_entity_type {
+ FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
+};
+
+typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
+ u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv);
+
+static int __must_check ffs_do_desc(char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ struct usb_descriptor_header *_ds = (void *)data;
+ u8 length;
+ int ret;
+
+ ENTER();
+
+ /* At least two bytes are required: length and type */
+ if (len < 2) {
+ pr_vdebug("descriptor too short\n");
+ return -EINVAL;
+ }
+
+ /* If we have at least as many bytes as the descriptor takes? */
+ length = _ds->bLength;
+ if (len < length) {
+ pr_vdebug("descriptor longer then available data\n");
+ return -EINVAL;
+ }
+
+#define __entity_check_INTERFACE(val) 1
+#define __entity_check_STRING(val) (val)
+#define __entity_check_ENDPOINT(val) ((val) & USB_ENDPOINT_NUMBER_MASK)
+#define __entity(type, val) do { \
+ pr_vdebug("entity " #type "(%02x)\n", (val)); \
+ if (unlikely(!__entity_check_ ##type(val))) { \
+ pr_vdebug("invalid entity's value\n"); \
+ return -EINVAL; \
+ } \
+ ret = entity(FFS_ ##type, &val, _ds, priv); \
+ if (unlikely(ret < 0)) { \
+ pr_debug("entity " #type "(%02x); ret = %d\n", \
+ (val), ret); \
+ return ret; \
+ } \
+ } while (0)
+
+ /* Parse descriptor depending on type. */
+ switch (_ds->bDescriptorType) {
+ case USB_DT_DEVICE:
+ case USB_DT_CONFIG:
+ case USB_DT_STRING:
+ case USB_DT_DEVICE_QUALIFIER:
+ /* function can't have any of those */
+ pr_vdebug("descriptor reserved for gadget: %d\n",
+ _ds->bDescriptorType);
+ return -EINVAL;
+
+ case USB_DT_INTERFACE: {
+ struct usb_interface_descriptor *ds = (void *)_ds;
+ pr_vdebug("interface descriptor\n");
+ if (length != sizeof *ds)
+ goto inv_length;
+
+ __entity(INTERFACE, ds->bInterfaceNumber);
+ if (ds->iInterface)
+ __entity(STRING, ds->iInterface);
+ }
+ break;
+
+ case USB_DT_ENDPOINT: {
+ struct usb_endpoint_descriptor *ds = (void *)_ds;
+ pr_vdebug("endpoint descriptor\n");
+ if (length != USB_DT_ENDPOINT_SIZE &&
+ length != USB_DT_ENDPOINT_AUDIO_SIZE)
+ goto inv_length;
+ __entity(ENDPOINT, ds->bEndpointAddress);
+ }
+ break;
+
+ case HID_DT_HID:
+ pr_vdebug("hid descriptor\n");
+ if (length != sizeof(struct hid_descriptor))
+ goto inv_length;
+ break;
+
+ case USB_DT_OTG:
+ if (length != sizeof(struct usb_otg_descriptor))
+ goto inv_length;
+ break;
+
+ case USB_DT_INTERFACE_ASSOCIATION: {
+ struct usb_interface_assoc_descriptor *ds = (void *)_ds;
+ pr_vdebug("interface association descriptor\n");
+ if (length != sizeof *ds)
+ goto inv_length;
+ if (ds->iFunction)
+ __entity(STRING, ds->iFunction);
+ }
+ break;
+
+ case USB_DT_OTHER_SPEED_CONFIG:
+ case USB_DT_INTERFACE_POWER:
+ case USB_DT_DEBUG:
+ case USB_DT_SECURITY:
+ case USB_DT_CS_RADIO_CONTROL:
+ /* TODO */
+ pr_vdebug("unimplemented descriptor: %d\n", _ds->bDescriptorType);
+ return -EINVAL;
+
+ default:
+ /* We should never be here */
+ pr_vdebug("unknown descriptor: %d\n", _ds->bDescriptorType);
+ return -EINVAL;
+
+inv_length:
+ pr_vdebug("invalid length: %d (descriptor %d)\n",
+ _ds->bLength, _ds->bDescriptorType);
+ return -EINVAL;
+ }
+
+#undef __entity
+#undef __entity_check_DESCRIPTOR
+#undef __entity_check_INTERFACE
+#undef __entity_check_STRING
+#undef __entity_check_ENDPOINT
+
+ return length;
+}
+
+static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
+ ffs_entity_callback entity, void *priv)
+{
+ const unsigned _len = len;
+ unsigned long num = 0;
+
+ ENTER();
+
+ for (;;) {
+ int ret;
+
+ if (num == count)
+ data = NULL;
+
+ /* Record "descriptor" entity */
+ ret = entity(FFS_DESCRIPTOR, (u8 *)num, (void *)data, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("entity DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
+ return ret;
+ }
+
+ if (!data)
+ return _len - len;
+
+ ret = ffs_do_desc(data, len, entity, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("%s returns %d\n", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ ++num;
+ }
+}
+
+static int __ffs_data_do_entity(enum ffs_entity_type type,
+ u8 *valuep, struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_data *ffs = priv;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_DESCRIPTOR:
+ break;
+
+ case FFS_INTERFACE:
+ /*
+ * Interfaces are indexed from zero so if we
+ * encountered interface "n" then there are at least
+ * "n+1" interfaces.
+ */
+ if (*valuep >= ffs->interfaces_count)
+ ffs->interfaces_count = *valuep + 1;
+ break;
+
+ case FFS_STRING:
+ /*
+ * Strings are indexed from 1 (0 is magic ;) reserved
+ * for languages list or some such)
+ */
+ if (*valuep > ffs->strings_count)
+ ffs->strings_count = *valuep;
+ break;
+
+ case FFS_ENDPOINT:
+ /* Endpoints are indexed from 1 as well. */
+ if ((*valuep & USB_ENDPOINT_NUMBER_MASK) > ffs->eps_count)
+ ffs->eps_count = (*valuep & USB_ENDPOINT_NUMBER_MASK);
+ break;
+ }
+
+ return 0;
+}
+
+static int __ffs_data_got_descs(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ unsigned fs_count, hs_count;
+ int fs_len, ret = -EINVAL;
+ char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ fs_count = get_unaligned_le32(data + 8);
+ hs_count = get_unaligned_le32(data + 12);
+
+ if (!fs_count && !hs_count)
+ goto einval;
+
+ data += 16;
+ len -= 16;
+
+ if (likely(fs_count)) {
+ fs_len = ffs_do_descs(fs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(fs_len < 0)) {
+ ret = fs_len;
+ goto error;
+ }
+
+ data += fs_len;
+ len -= fs_len;
+ } else {
+ fs_len = 0;
+ }
+
+ if (likely(hs_count)) {
+ ret = ffs_do_descs(hs_count, data, len,
+ __ffs_data_do_entity, ffs);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (unlikely(len != ret))
+ goto einval;
+
+ ffs->raw_fs_descs_length = fs_len;
+ ffs->raw_descs_length = fs_len + ret;
+ ffs->raw_descs = _data;
+ ffs->fs_descs_count = fs_count;
+ ffs->hs_descs_count = hs_count;
+
+ return 0;
+
+einval:
+ ret = -EINVAL;
+error:
+ kfree(_data);
+ return ret;
+}
+
+static int __ffs_data_got_strings(struct ffs_data *ffs,
+ char *const _data, size_t len)
+{
+ u32 str_count, needed_count, lang_count;
+ struct usb_gadget_strings **stringtabs, *t;
+ struct usb_string *strings, *s;
+ const char *data = _data;
+
+ ENTER();
+
+ if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_STRINGS_MAGIC ||
+ get_unaligned_le32(data + 4) != len))
+ goto error;
+ str_count = get_unaligned_le32(data + 8);
+ lang_count = get_unaligned_le32(data + 12);
+
+ /* if one is zero the other must be zero */
+ if (unlikely(!str_count != !lang_count))
+ goto error;
+
+ /* Do we have at least as many strings as descriptors need? */
+ needed_count = ffs->strings_count;
+ if (unlikely(str_count < needed_count))
+ goto error;
+
+ /*
+ * If we don't need any strings just return and free all
+ * memory.
+ */
+ if (!needed_count) {
+ kfree(_data);
+ return 0;
+ }
+
+ /* Allocate everything in one chunk so there's less maintenance. */
+ {
+ struct {
+ struct usb_gadget_strings *stringtabs[lang_count + 1];
+ struct usb_gadget_strings stringtab[lang_count];
+ struct usb_string strings[lang_count*(needed_count+1)];
+ } *d;
+ unsigned i = 0;
+
+ d = kmalloc(sizeof *d, GFP_KERNEL);
+ if (unlikely(!d)) {
+ kfree(_data);
+ return -ENOMEM;
+ }
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ i = lang_count;
+ do {
+ *stringtabs++ = t++;
+ } while (--i);
+ *stringtabs = NULL;
+
+ stringtabs = d->stringtabs;
+ t = d->stringtab;
+ s = d->strings;
+ strings = s;
+ }
+
+ /* For each language */
+ data += 16;
+ len -= 16;
+
+ do { /* lang_count > 0 so we can use do-while */
+ unsigned needed = needed_count;
+
+ if (unlikely(len < 3))
+ goto error_free;
+ t->language = get_unaligned_le16(data);
+ t->strings = s;
+ ++t;
+
+ data += 2;
+ len -= 2;
+
+ /* For each string */
+ do { /* str_count > 0 so we can use do-while */
+ size_t length = strnlen(data, len);
+
+ if (unlikely(length == len))
+ goto error_free;
+
+ /*
+ * User may provide more strings then we need,
+ * if that's the case we simply ignore the
+ * rest
+ */
+ if (likely(needed)) {
+ /*
+ * s->id will be set while adding
+ * function to configuration so for
+ * now just leave garbage here.
+ */
+ s->s = data;
+ --needed;
+ ++s;
+ }
+
+ data += length + 1;
+ len -= length + 1;
+ } while (--str_count);
+
+ s->id = 0; /* terminator */
+ s->s = NULL;
+ ++s;
+
+ } while (--lang_count);
+
+ /* Some garbage left? */
+ if (unlikely(len))
+ goto error_free;
+
+ /* Done! */
+ ffs->stringtabs = stringtabs;
+ ffs->raw_strings = _data;
+
+ return 0;
+
+error_free:
+ kfree(stringtabs);
+error:
+ kfree(_data);
+ return -EINVAL;
+}
+
+
+/* Events handling and management *******************************************/
+
+static void __ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ enum usb_functionfs_event_type rem_type1, rem_type2 = type;
+ int neg = 0;
+
+ /*
+ * Abort any unhandled setup
+ *
+ * We do not need to worry about some cmpxchg() changing value
+ * of ffs->setup_state without holding the lock because when
+ * state is FFS_SETUP_PENDING cmpxchg() in several places in
+ * the source does nothing.
+ */
+ if (ffs->setup_state == FFS_SETUP_PENDING)
+ ffs->setup_state = FFS_SETUP_CANCELED;
+
+ switch (type) {
+ case FUNCTIONFS_RESUME:
+ rem_type2 = FUNCTIONFS_SUSPEND;
+ /* FALL THROUGH */
+ case FUNCTIONFS_SUSPEND:
+ case FUNCTIONFS_SETUP:
+ rem_type1 = type;
+ /* Discard all similar events */
+ break;
+
+ case FUNCTIONFS_BIND:
+ case FUNCTIONFS_UNBIND:
+ case FUNCTIONFS_DISABLE:
+ case FUNCTIONFS_ENABLE:
+ /* Discard everything other then power management. */
+ rem_type1 = FUNCTIONFS_SUSPEND;
+ rem_type2 = FUNCTIONFS_RESUME;
+ neg = 1;
+ break;
+
+ default:
+ BUG();
+ }
+
+ {
+ u8 *ev = ffs->ev.types, *out = ev;
+ unsigned n = ffs->ev.count;
+ for (; n; --n, ++ev)
+ if ((*ev == rem_type1 || *ev == rem_type2) == neg)
+ *out++ = *ev;
+ else
+ pr_vdebug("purging event %d\n", *ev);
+ ffs->ev.count = out - ffs->ev.types;
+ }
+
+ pr_vdebug("adding event %d\n", type);
+ ffs->ev.types[ffs->ev.count++] = type;
+ wake_up_locked(&ffs->ev.waitq);
+}
+
+static void ffs_event_add(struct ffs_data *ffs,
+ enum usb_functionfs_event_type type)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ __ffs_event_add(ffs, type);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+}
+
+
+/* Bind/unbind USB function hooks *******************************************/
+
+static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct usb_endpoint_descriptor *ds = (void *)desc;
+ struct ffs_function *func = priv;
+ struct ffs_ep *ffs_ep;
+
+ /*
+ * If hs_descriptors is not NULL then we are reading hs
+ * descriptors now
+ */
+ const int isHS = func->function.hs_descriptors != NULL;
+ unsigned idx;
+
+ if (type != FFS_DESCRIPTOR)
+ return 0;
+
+ if (isHS)
+ func->function.hs_descriptors[(long)valuep] = desc;
+ else
+ func->function.descriptors[(long)valuep] = desc;
+
+ if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
+ ffs_ep = func->eps + idx;
+
+ if (unlikely(ffs_ep->descs[isHS])) {
+ pr_vdebug("two %sspeed descriptors for EP %d\n",
+ isHS ? "high" : "full",
+ ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+ return -EINVAL;
+ }
+ ffs_ep->descs[isHS] = ds;
+
+ ffs_dump_mem(": Original ep desc", ds, ds->bLength);
+ if (ffs_ep->ep) {
+ ds->bEndpointAddress = ffs_ep->descs[0]->bEndpointAddress;
+ if (!ds->wMaxPacketSize)
+ ds->wMaxPacketSize = ffs_ep->descs[0]->wMaxPacketSize;
+ } else {
+ struct usb_request *req;
+ struct usb_ep *ep;
+
+ pr_vdebug("autoconfig\n");
+ ep = usb_ep_autoconfig(func->gadget, ds);
+ if (unlikely(!ep))
+ return -ENOTSUPP;
+ ep->driver_data = func->eps + idx;
+
+ req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (unlikely(!req))
+ return -ENOMEM;
+
+ ffs_ep->ep = ep;
+ ffs_ep->req = req;
+ func->eps_revmap[ds->bEndpointAddress &
+ USB_ENDPOINT_NUMBER_MASK] = idx + 1;
+ }
+ ffs_dump_mem(": Rewritten ep desc", ds, ds->bLength);
+
+ return 0;
+}
+
+static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
+ struct usb_descriptor_header *desc,
+ void *priv)
+{
+ struct ffs_function *func = priv;
+ unsigned idx;
+ u8 newValue;
+
+ switch (type) {
+ default:
+ case FFS_DESCRIPTOR:
+ /* Handled in previous pass by __ffs_func_bind_do_descs() */
+ return 0;
+
+ case FFS_INTERFACE:
+ idx = *valuep;
+ if (func->interfaces_nums[idx] < 0) {
+ int id = usb_interface_id(func->conf, &func->function);
+ if (unlikely(id < 0))
+ return id;
+ func->interfaces_nums[idx] = id;
+ }
+ newValue = func->interfaces_nums[idx];
+ break;
+
+ case FFS_STRING:
+ /* String' IDs are allocated when fsf_data is bound to cdev */
+ newValue = func->ffs->stringtabs[0]->strings[*valuep - 1].id;
+ break;
+
+ case FFS_ENDPOINT:
+ /*
+ * USB_DT_ENDPOINT are handled in
+ * __ffs_func_bind_do_descs().
+ */
+ if (desc->bDescriptorType == USB_DT_ENDPOINT)
+ return 0;
+
+ idx = (*valuep & USB_ENDPOINT_NUMBER_MASK) - 1;
+ if (unlikely(!func->eps[idx].ep))
+ return -EINVAL;
+
+ {
+ struct usb_endpoint_descriptor **descs;
+ descs = func->eps[idx].descs;
+ newValue = descs[descs[0] ? 0 : 1]->bEndpointAddress;
+ }
+ break;
+ }
+
+ pr_vdebug("%02x -> %02x\n", *valuep, newValue);
+ *valuep = newValue;
+ return 0;
+}
+
+static int ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ const int full = !!func->ffs->fs_descs_count;
+ const int high = gadget_is_dualspeed(func->gadget) &&
+ func->ffs->hs_descs_count;
+
+ int ret;
+
+ /* Make it a single chunk, less management later on */
+ struct {
+ struct ffs_ep eps[ffs->eps_count];
+ struct usb_descriptor_header
+ *fs_descs[full ? ffs->fs_descs_count + 1 : 0];
+ struct usb_descriptor_header
+ *hs_descs[high ? ffs->hs_descs_count + 1 : 0];
+ short inums[ffs->interfaces_count];
+ char raw_descs[high ? ffs->raw_descs_length
+ : ffs->raw_fs_descs_length];
+ } *data;
+
+ ENTER();
+
+ /* Only high speed but not supported by gadget? */
+ if (unlikely(!(full | high)))
+ return -ENOTSUPP;
+
+ /* Allocate */
+ data = kmalloc(sizeof *data, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
+
+ /* Zero */
+ memset(data->eps, 0, sizeof data->eps);
+ memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
+ memset(data->inums, 0xff, sizeof data->inums);
+ for (ret = ffs->eps_count; ret; --ret)
+ data->eps[ret].num = -1;
+
+ /* Save pointers */
+ func->eps = data->eps;
+ func->interfaces_nums = data->inums;
+
+ /*
+ * Go through all the endpoint descriptors and allocate
+ * endpoints first, so that later we can rewrite the endpoint
+ * numbers without worrying that it may be described later on.
+ */
+ if (likely(full)) {
+ func->function.descriptors = data->fs_descs;
+ ret = ffs_do_descs(ffs->fs_descs_count,
+ data->raw_descs,
+ sizeof data->raw_descs,
+ __ffs_func_bind_do_descs, func);
+ if (unlikely(ret < 0))
+ goto error;
+ } else {
+ ret = 0;
+ }
+
+ if (likely(high)) {
+ func->function.hs_descriptors = data->hs_descs;
+ ret = ffs_do_descs(ffs->hs_descs_count,
+ data->raw_descs + ret,
+ (sizeof data->raw_descs) - ret,
+ __ffs_func_bind_do_descs, func);
+ }
+
+ /*
+ * Now handle interface numbers allocation and interface and
+ * endpoint numbers rewriting. We can do that in one go
+ * now.
+ */
+ ret = ffs_do_descs(ffs->fs_descs_count +
+ (high ? ffs->hs_descs_count : 0),
+ data->raw_descs, sizeof data->raw_descs,
+ __ffs_func_bind_do_nums, func);
+ if (unlikely(ret < 0))
+ goto error;
+
+ /* And we're done */
+ ffs_event_add(ffs, FUNCTIONFS_BIND);
+ return 0;
+
+error:
+ /* XXX Do we need to release all claimed endpoints here? */
+ return ret;
+}
+
+
+/* Other USB function hooks *************************************************/
+
+static void ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+
+ ENTER();
+
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+
+ ffs_func_free(func);
+}
+
+static int ffs_func_set_alt(struct usb_function *f,
+ unsigned interface, unsigned alt)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ int ret = 0, intf;
+
+ if (alt != (unsigned)-1) {
+ intf = ffs_func_revmap_intf(func, interface);
+ if (unlikely(intf < 0))
+ return intf;
+ }
+
+ if (ffs->func)
+ ffs_func_eps_disable(ffs->func);
+
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ if (alt == (unsigned)-1) {
+ ffs->func = NULL;
+ ffs_event_add(ffs, FUNCTIONFS_DISABLE);
+ return 0;
+ }
+
+ ffs->func = func;
+ ret = ffs_func_eps_enable(func);
+ if (likely(ret >= 0))
+ ffs_event_add(ffs, FUNCTIONFS_ENABLE);
+ return ret;
+}
+
+static void ffs_func_disable(struct usb_function *f)
+{
+ ffs_func_set_alt(f, 0, (unsigned)-1);
+}
+
+static int ffs_func_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *creq)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ unsigned long flags;
+ int ret;
+
+ ENTER();
+
+ pr_vdebug("creq->bRequestType = %02x\n", creq->bRequestType);
+ pr_vdebug("creq->bRequest = %02x\n", creq->bRequest);
+ pr_vdebug("creq->wValue = %04x\n", le16_to_cpu(creq->wValue));
+ pr_vdebug("creq->wIndex = %04x\n", le16_to_cpu(creq->wIndex));
+ pr_vdebug("creq->wLength = %04x\n", le16_to_cpu(creq->wLength));
+
+ /*
+ * Most requests directed to interface go through here
+ * (notable exceptions are set/get interface) so we need to
+ * handle them. All other either handled by composite or
+ * passed to usb_configuration->setup() (if one is set). No
+ * matter, we will handle requests directed to endpoint here
+ * as well (as it's straightforward) but what to do with any
+ * other request?
+ */
+ if (ffs->state != FFS_ACTIVE)
+ return -ENODEV;
+
+ switch (creq->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_INTERFACE:
+ ret = ffs_func_revmap_intf(func, le16_to_cpu(creq->wIndex));
+ if (unlikely(ret < 0))
+ return ret;
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ ret = ffs_func_revmap_ep(func, le16_to_cpu(creq->wIndex));
+ if (unlikely(ret < 0))
+ return ret;
+ break;
+
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ spin_lock_irqsave(&ffs->ev.waitq.lock, flags);
+ ffs->ev.setup = *creq;
+ ffs->ev.setup.wIndex = cpu_to_le16(ret);
+ __ffs_event_add(ffs, FUNCTIONFS_SETUP);
+ spin_unlock_irqrestore(&ffs->ev.waitq.lock, flags);
+
+ return 0;
+}
+
+static void ffs_func_suspend(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_SUSPEND);
+}
+
+static void ffs_func_resume(struct usb_function *f)
+{
+ ENTER();
+ ffs_event_add(ffs_func_from_usb(f)->ffs, FUNCTIONFS_RESUME);
+}
+
+
+/* Endpoint and interface numbers reverse mapping ***************************/
+
+static int ffs_func_revmap_ep(struct ffs_function *func, u8 num)
+{
+ num = func->eps_revmap[num & USB_ENDPOINT_NUMBER_MASK];
+ return num ? num : -EDOM;
+}
+
+static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
+{
+ short *nums = func->interfaces_nums;
+ unsigned count = func->ffs->interfaces_count;
+
+ for (; count; --count, ++nums) {
+ if (*nums >= 0 && *nums == intf)
+ return nums - func->interfaces_nums;
+ }
+
+ return -EDOM;
+}
+
+
+/* Misc helper functions ****************************************************/
+
+static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
+{
+ return nonblock
+ ? likely(mutex_trylock(mutex)) ? 0 : -EAGAIN
+ : mutex_lock_interruptible(mutex);
+}
+
+static char *ffs_prepare_buffer(const char * __user buf, size_t len)
+{
+ char *data;
+
+ if (unlikely(!len))
+ return NULL;
+
+ data = kmalloc(len, GFP_KERNEL);
+ if (unlikely(!data))
+ return ERR_PTR(-ENOMEM);
+
+ if (unlikely(__copy_from_user(data, buf, len))) {
+ kfree(data);
+ return ERR_PTR(-EFAULT);
+ }
+
+ pr_vdebug("Buffer from user space:\n");
+ ffs_dump_mem("", data, len);
+
+ return data;
+}
diff --git a/drivers/staging/ccg/f_mass_storage.c b/drivers/staging/ccg/f_mass_storage.c
new file mode 100644
index 0000000..4f1142e
--- /dev/null
+++ b/drivers/staging/ccg/f_mass_storage.c
@@ -0,0 +1,3135 @@
+/*
+ * f_mass_storage.c -- Mass Storage USB Composite Function
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz <mina86@mina86.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. 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.
+ * 3. The names of the above-listed copyright holders may not be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * ALTERNATIVELY, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL") as published by the Free Software
+ * Foundation, either version 2 of that License or (at your option) any
+ * later version.
+ *
+ * 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.
+ */
+
+/*
+ * The Mass Storage Function acts as a USB Mass Storage device,
+ * appearing to the host as a disk drive or as a CD-ROM drive. In
+ * addition to providing an example of a genuinely useful composite
+ * function for a USB device, it also illustrates a technique of
+ * double-buffering for increased throughput.
+ *
+ * For more information about MSF and in particular its module
+ * parameters and sysfs interface read the
+ * <Documentation/usb/mass-storage.txt> file.
+ */
+
+/*
+ * MSF is configured by specifying a fsg_config structure. It has the
+ * following fields:
+ *
+ * nluns Number of LUNs function have (anywhere from 1
+ * to FSG_MAX_LUNS which is 8).
+ * luns An array of LUN configuration values. This
+ * should be filled for each LUN that
+ * function will include (ie. for "nluns"
+ * LUNs). Each element of the array has
+ * the following fields:
+ * ->filename The path to the backing file for the LUN.
+ * Required if LUN is not marked as
+ * removable.
+ * ->ro Flag specifying access to the LUN shall be
+ * read-only. This is implied if CD-ROM
+ * emulation is enabled as well as when
+ * it was impossible to open "filename"
+ * in R/W mode.
+ * ->removable Flag specifying that LUN shall be indicated as
+ * being removable.
+ * ->cdrom Flag specifying that LUN shall be reported as
+ * being a CD-ROM.
+ * ->nofua Flag specifying that FUA flag in SCSI WRITE(10,12)
+ * commands for this LUN shall be ignored.
+ *
+ * vendor_name
+ * product_name
+ * release Information used as a reply to INQUIRY
+ * request. To use default set to NULL,
+ * NULL, 0xffff respectively. The first
+ * field should be 8 and the second 16
+ * characters or less.
+ *
+ * can_stall Set to permit function to halt bulk endpoints.
+ * Disabled on some USB devices known not
+ * to work correctly. You should set it
+ * to true.
+ *
+ * If "removable" is not set for a LUN then a backing file must be
+ * specified. If it is set, then NULL filename means the LUN's medium
+ * is not loaded (an empty string as "filename" in the fsg_config
+ * structure causes error). The CD-ROM emulation includes a single
+ * data track and no audio tracks; hence there need be only one
+ * backing file per LUN.
+ *
+ * This function is heavily based on "File-backed Storage Gadget" by
+ * Alan Stern which in turn is heavily based on "Gadget Zero" by David
+ * Brownell. The driver's SCSI command interface was based on the
+ * "Information technology - Small Computer System Interface - 2"
+ * document from X3T9.2 Project 375D, Revision 10L, 7-SEP-93,
+ * available at <http://www.t10.org/ftp/t10/drafts/s2/s2-r10l.pdf>.
+ * The single exception is opcode 0x23 (READ FORMAT CAPACITIES), which
+ * was based on the "Universal Serial Bus Mass Storage Class UFI
+ * Command Specification" document, Revision 1.0, December 14, 1998,
+ * available at
+ * <http://www.usb.org/developers/devclass_docs/usbmass-ufi10.pdf>.
+ */
+
+/*
+ * Driver Design
+ *
+ * The MSF is fairly straightforward. There is a main kernel
+ * thread that handles most of the work. Interrupt routines field
+ * callbacks from the controller driver: bulk- and interrupt-request
+ * completion notifications, endpoint-0 events, and disconnect events.
+ * Completion events are passed to the main thread by wakeup calls. Many
+ * ep0 requests are handled at interrupt time, but SetInterface,
+ * SetConfiguration, and device reset requests are forwarded to the
+ * thread in the form of "exceptions" using SIGUSR1 signals (since they
+ * should interrupt any ongoing file I/O operations).
+ *
+ * The thread's main routine implements the standard command/data/status
+ * parts of a SCSI interaction. It and its subroutines are full of tests
+ * for pending signals/exceptions -- all this polling is necessary since
+ * the kernel has no setjmp/longjmp equivalents. (Maybe this is an
+ * indication that the driver really wants to be running in userspace.)
+ * An important point is that so long as the thread is alive it keeps an
+ * open reference to the backing file. This will prevent unmounting
+ * the backing file's underlying filesystem and could cause problems
+ * during system shutdown, for example. To prevent such problems, the
+ * thread catches INT, TERM, and KILL signals and converts them into
+ * an EXIT exception.
+ *
+ * In normal operation the main thread is started during the gadget's
+ * fsg_bind() callback and stopped during fsg_unbind(). But it can
+ * also exit when it receives a signal, and there's no point leaving
+ * the gadget running when the thread is dead. As of this moment, MSF
+ * provides no way to deregister the gadget when thread dies -- maybe
+ * a callback functions is needed.
+ *
+ * To provide maximum throughput, the driver uses a circular pipeline of
+ * buffer heads (struct fsg_buffhd). In principle the pipeline can be
+ * arbitrarily long; in practice the benefits don't justify having more
+ * than 2 stages (i.e., double buffering). But it helps to think of the
+ * pipeline as being a long one. Each buffer head contains a bulk-in and
+ * a bulk-out request pointer (since the buffer can be used for both
+ * output and input -- directions always are given from the host's
+ * point of view) as well as a pointer to the buffer and various state
+ * variables.
+ *
+ * Use of the pipeline follows a simple protocol. There is a variable
+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use.
+ * At any time that buffer head may still be in use from an earlier
+ * request, so each buffer head has a state variable indicating whether
+ * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the
+ * buffer head to be EMPTY, filling the buffer either by file I/O or by
+ * USB I/O (during which the buffer head is BUSY), and marking the buffer
+ * head FULL when the I/O is complete. Then the buffer will be emptied
+ * (again possibly by USB I/O, during which it is marked BUSY) and
+ * finally marked EMPTY again (possibly by a completion routine).
+ *
+ * A module parameter tells the driver to avoid stalling the bulk
+ * endpoints wherever the transport specification allows. This is
+ * necessary for some UDCs like the SuperH, which cannot reliably clear a
+ * halt on a bulk endpoint. However, under certain circumstances the
+ * Bulk-only specification requires a stall. In such cases the driver
+ * will halt the endpoint and set a flag indicating that it should clear
+ * the halt in software during the next device reset. Hopefully this
+ * will permit everything to work correctly. Furthermore, although the
+ * specification allows the bulk-out endpoint to halt when the host sends
+ * too much data, implementing this would cause an unavoidable race.
+ * The driver will always use the "no-stall" approach for OUT transfers.
+ *
+ * One subtle point concerns sending status-stage responses for ep0
+ * requests. Some of these requests, such as device reset, can involve
+ * interrupting an ongoing file I/O operation, which might take an
+ * arbitrarily long time. During that delay the host might give up on
+ * the original ep0 request and issue a new one. When that happens the
+ * driver should not notify the host about completion of the original
+ * request, as the host will no longer be waiting for it. So the driver
+ * assigns to each ep0 request a unique tag, and it keeps track of the
+ * tag value of the request associated with a long-running exception
+ * (device-reset, interface-change, or configuration-change). When the
+ * exception handler is finished, the status-stage response is submitted
+ * only if the current ep0 request tag is equal to the exception request
+ * tag. Thus only the most recently received ep0 request will get a
+ * status-stage response.
+ *
+ * Warning: This driver source file is too long. It ought to be split up
+ * into a header file plus about 3 separate .c files, to handle the details
+ * of the Gadget, USB Mass Storage, and SCSI protocols.
+ */
+
+
+/* #define VERBOSE_DEBUG */
+/* #define DUMP_MSGS */
+
+#include <linux/blkdev.h>
+#include <linux/completion.h>
+#include <linux/dcache.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fcntl.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/kref.h>
+#include <linux/kthread.h>
+#include <linux/limits.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/freezer.h>
+#include <linux/utsname.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+#include "gadget_chips.h"
+
+
+/*------------------------------------------------------------------------*/
+
+#define FSG_DRIVER_DESC "Mass Storage Function"
+#define FSG_DRIVER_VERSION "2009/09/11"
+
+static const char fsg_string_interface[] = "Mass Storage";
+
+#define FSG_NO_DEVICE_STRINGS 1
+#define FSG_NO_OTG 1
+#define FSG_NO_INTR_EP 1
+
+#include "storage_common.c"
+
+
+/*-------------------------------------------------------------------------*/
+
+struct fsg_dev;
+struct fsg_common;
+
+/* FSF callback functions */
+struct fsg_operations {
+ /*
+ * Callback function to call when thread exits. If no
+ * callback is set or it returns value lower then zero MSF
+ * will force eject all LUNs it operates on (including those
+ * marked as non-removable or with prevent_medium_removal flag
+ * set).
+ */
+ int (*thread_exits)(struct fsg_common *common);
+
+ /*
+ * Called prior to ejection. Negative return means error,
+ * zero means to continue with ejection, positive means not to
+ * eject.
+ */
+ int (*pre_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+ /*
+ * Called after ejection. Negative return means error, zero
+ * or positive is just a success.
+ */
+ int (*post_eject)(struct fsg_common *common,
+ struct fsg_lun *lun, int num);
+};
+
+/* Data shared by all the FSG instances. */
+struct fsg_common {
+ struct usb_gadget *gadget;
+ struct usb_composite_dev *cdev;
+ struct fsg_dev *fsg, *new_fsg;
+ wait_queue_head_t fsg_wait;
+
+ /* filesem protects: backing files in use */
+ struct rw_semaphore filesem;
+
+ /* lock protects: state, all the req_busy's */
+ spinlock_t lock;
+
+ struct usb_ep *ep0; /* Copy of gadget->ep0 */
+ struct usb_request *ep0req; /* Copy of cdev->req */
+ unsigned int ep0_req_tag;
+
+ struct fsg_buffhd *next_buffhd_to_fill;
+ struct fsg_buffhd *next_buffhd_to_drain;
+ struct fsg_buffhd *buffhds;
+
+ int cmnd_size;
+ u8 cmnd[MAX_COMMAND_SIZE];
+
+ unsigned int nluns;
+ unsigned int lun;
+ struct fsg_lun *luns;
+ struct fsg_lun *curlun;
+
+ unsigned int bulk_out_maxpacket;
+ enum fsg_state state; /* For exception handling */
+ unsigned int exception_req_tag;
+
+ enum data_direction data_dir;
+ u32 data_size;
+ u32 data_size_from_cmnd;
+ u32 tag;
+ u32 residue;
+ u32 usb_amount_left;
+
+ unsigned int can_stall:1;
+ unsigned int free_storage_on_release:1;
+ unsigned int phase_error:1;
+ unsigned int short_packet_received:1;
+ unsigned int bad_lun_okay:1;
+ unsigned int running:1;
+
+ int thread_wakeup_needed;
+ struct completion thread_notifier;
+ struct task_struct *thread_task;
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ /*
+ * Vendor (8 chars), product (16 chars), release (4
+ * hexadecimal digits) and NUL byte
+ */
+ char inquiry_string[8 + 16 + 4 + 1];
+
+ struct kref ref;
+};
+
+struct fsg_config {
+ unsigned nluns;
+ struct fsg_lun_config {
+ const char *filename;
+ char ro;
+ char removable;
+ char cdrom;
+ char nofua;
+ } luns[FSG_MAX_LUNS];
+
+ /* Callback functions. */
+ const struct fsg_operations *ops;
+ /* Gadget's private data. */
+ void *private_data;
+
+ const char *vendor_name; /* 8 characters or less */
+ const char *product_name; /* 16 characters or less */
+ u16 release;
+
+ char can_stall;
+};
+
+struct fsg_dev {
+ struct usb_function function;
+ struct usb_gadget *gadget; /* Copy of cdev->gadget */
+ struct fsg_common *common;
+
+ u16 interface_number;
+
+ unsigned int bulk_in_enabled:1;
+ unsigned int bulk_out_enabled:1;
+
+ unsigned long atomic_bitflags;
+#define IGNORE_BULK_OUT 0
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
+};
+
+static inline int __fsg_is_set(struct fsg_common *common,
+ const char *func, unsigned line)
+{
+ if (common->fsg)
+ return 1;
+ ERROR(common, "common->fsg is NULL in %s at %u\n", func, line);
+ WARN_ON(1);
+ return 0;
+}
+
+#define fsg_is_set(common) likely(__fsg_is_set(common, __func__, __LINE__))
+
+static inline struct fsg_dev *fsg_from_func(struct usb_function *f)
+{
+ return container_of(f, struct fsg_dev, function);
+}
+
+typedef void (*fsg_routine_t)(struct fsg_dev *);
+
+static int exception_in_progress(struct fsg_common *common)
+{
+ return common->state > FSG_STATE_IDLE;
+}
+
+/* Make bulk-out requests be divisible by the maxpacket size */
+static void set_bulk_out_req_length(struct fsg_common *common,
+ struct fsg_buffhd *bh, unsigned int length)
+{
+ unsigned int rem;
+
+ bh->bulk_out_intended_length = length;
+ rem = length % common->bulk_out_maxpacket;
+ if (rem > 0)
+ length += common->bulk_out_maxpacket - rem;
+ bh->outreq->length = length;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep)
+{
+ const char *name;
+
+ if (ep == fsg->bulk_in)
+ name = "bulk-in";
+ else if (ep == fsg->bulk_out)
+ name = "bulk-out";
+ else
+ name = ep->name;
+ DBG(fsg, "%s set halt\n", name);
+ return usb_ep_set_halt(ep);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* These routines may be called in process context or in_irq */
+
+/* Caller must hold fsg->lock */
+static void wakeup_thread(struct fsg_common *common)
+{
+ /* Tell the main thread that something has happened */
+ common->thread_wakeup_needed = 1;
+ if (common->thread_task)
+ wake_up_process(common->thread_task);
+}
+
+static void raise_exception(struct fsg_common *common, enum fsg_state new_state)
+{
+ unsigned long flags;
+
+ /*
+ * Do nothing if a higher-priority exception is already in progress.
+ * If a lower-or-equal priority exception is in progress, preempt it
+ * and notify the main thread by sending it a signal.
+ */
+ spin_lock_irqsave(&common->lock, flags);
+ if (common->state <= new_state) {
+ common->exception_req_tag = common->ep0_req_tag;
+ common->state = new_state;
+ if (common->thread_task)
+ send_sig_info(SIGUSR1, SEND_SIG_FORCED,
+ common->thread_task);
+ }
+ spin_unlock_irqrestore(&common->lock, flags);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int ep0_queue(struct fsg_common *common)
+{
+ int rc;
+
+ rc = usb_ep_queue(common->ep0, common->ep0req, GFP_ATOMIC);
+ common->ep0->driver_data = common;
+ if (rc != 0 && rc != -ESHUTDOWN) {
+ /* We can't do much more than wait for a reset */
+ WARNING(common, "error in submission: %s --> %d\n",
+ common->ep0->name, rc);
+ }
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* Completion handlers. These always run in_irq. */
+
+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ if (req->status || req->actual != req->length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, req->length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->inreq_busy = 0;
+ bh->state = BUF_STATE_EMPTY;
+ wakeup_thread(common);
+ spin_unlock(&common->lock);
+}
+
+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct fsg_common *common = ep->driver_data;
+ struct fsg_buffhd *bh = req->context;
+
+ dump_msg(common, "bulk-out", req->buf, req->actual);
+ if (req->status || req->actual != bh->bulk_out_intended_length)
+ DBG(common, "%s --> %d, %u/%u\n", __func__,
+ req->status, req->actual, bh->bulk_out_intended_length);
+ if (req->status == -ECONNRESET) /* Request was cancelled */
+ usb_ep_fifo_flush(ep);
+
+ /* Hold the lock while we update the request and buffer states */
+ smp_wmb();
+ spin_lock(&common->lock);
+ bh->outreq_busy = 0;
+ bh->state = BUF_STATE_FULL;
+ wakeup_thread(common);
+ spin_unlock(&common->lock);
+}
+
+static int fsg_setup(struct usb_function *f,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_request *req = fsg->common->ep0req;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ if (!fsg_is_set(fsg->common))
+ return -EOPNOTSUPP;
+
+ ++fsg->common->ep0_req_tag; /* Record arrival of a new request */
+ req->context = NULL;
+ req->length = 0;
+ dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl));
+
+ switch (ctrl->bRequest) {
+
+ case US_BULK_RESET_REQUEST:
+ if (ctrl->bRequestType !=
+ (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0 ||
+ w_length != 0)
+ return -EDOM;
+
+ /*
+ * Raise an exception to stop the current operation
+ * and reinitialize our state.
+ */
+ DBG(fsg, "bulk reset request\n");
+ raise_exception(fsg->common, FSG_STATE_RESET);
+ return DELAYED_STATUS;
+
+ case US_BULK_GET_MAX_LUN:
+ if (ctrl->bRequestType !=
+ (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE))
+ break;
+ if (w_index != fsg->interface_number || w_value != 0 ||
+ w_length != 1)
+ return -EDOM;
+ VDBG(fsg, "get max LUN\n");
+ *(u8 *)req->buf = fsg->common->nluns - 1;
+
+ /* Respond with data/status */
+ req->length = min((u16)1, w_length);
+ return ep0_queue(fsg->common);
+ }
+
+ VDBG(fsg,
+ "unknown class-specific control req %02x.%02x v%04x i%04x l%u\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ le16_to_cpu(ctrl->wValue), w_index, w_length);
+ return -EOPNOTSUPP;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/* All the following routines run in process context */
+
+/* Use this for bulk or interrupt transfers, not ep0 */
+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+ struct usb_request *req, int *pbusy,
+ enum fsg_buffer_state *state)
+{
+ int rc;
+
+ if (ep == fsg->bulk_in)
+ dump_msg(fsg, "bulk-in", req->buf, req->length);
+
+ spin_lock_irq(&fsg->common->lock);
+ *pbusy = 1;
+ *state = BUF_STATE_BUSY;
+ spin_unlock_irq(&fsg->common->lock);
+ rc = usb_ep_queue(ep, req, GFP_KERNEL);
+ if (rc != 0) {
+ *pbusy = 0;
+ *state = BUF_STATE_EMPTY;
+
+ /* We can't do much more than wait for a reset */
+
+ /*
+ * Note: currently the net2280 driver fails zero-length
+ * submissions if DMA is enabled.
+ */
+ if (rc != -ESHUTDOWN &&
+ !(rc == -EOPNOTSUPP && req->length == 0))
+ WARNING(fsg, "error in submission: %s --> %d\n",
+ ep->name, rc);
+ }
+}
+
+static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_in,
+ bh->inreq, &bh->inreq_busy, &bh->state);
+ return true;
+}
+
+static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ if (!fsg_is_set(common))
+ return false;
+ start_transfer(common->fsg, common->fsg->bulk_out,
+ bh->outreq, &bh->outreq_busy, &bh->state);
+ return true;
+}
+
+static int sleep_thread(struct fsg_common *common)
+{
+ int rc = 0;
+
+ /* Wait until a signal arrives or we are woken up */
+ for (;;) {
+ try_to_freeze();
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (signal_pending(current)) {
+ rc = -EINTR;
+ break;
+ }
+ if (common->thread_wakeup_needed)
+ break;
+ schedule();
+ }
+ __set_current_state(TASK_RUNNING);
+ common->thread_wakeup_needed = 0;
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_read(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int rc;
+ u32 amount_left;
+ loff_t file_offset, file_offset_tmp;
+ unsigned int amount;
+ ssize_t nread;
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
+ if (common->cmnd[0] == READ_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = don't read from the
+ * cache), but we don't implement them.
+ */
+ if ((common->cmnd[1] & ~0x18) != 0) {
+ 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;
+ }
+ file_offset = ((loff_t) lba) << curlun->blkbits;
+
+ /* Carry out the file reads */
+ amount_left = common->data_size_from_cmnd;
+ if (unlikely(amount_left == 0))
+ return -EIO; /* No default reply */
+
+ for (;;) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount.
+ * But don't read more than the buffer size.
+ * And don't try to read past the end of the file.
+ */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t)amount,
+ curlun->file_length - file_offset);
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /*
+ * If we were asked to read past the end of file,
+ * end with an empty buffer.
+ */
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ bh->inreq->length = 0;
+ bh->state = BUF_STATE_FULL;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long)file_offset, (int)nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file read: %d\n", (int)nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file read: %d/%u\n",
+ (int)nread, amount);
+ nread = round_down(nread, curlun->blksize);
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ common->residue -= nread;
+
+ /*
+ * Except at the end of the transfer, nread will be
+ * equal to the buffer size, which is divisible by the
+ * bulk-in maxpacket size.
+ */
+ bh->inreq->length = nread;
+ bh->state = BUF_STATE_FULL;
+
+ /* If an error occurred, report it and its position */
+ if (nread < amount) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ if (amount_left == 0)
+ break; /* No more left to read */
+
+ /* Send this buffer and go read some more */
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_write(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ struct fsg_buffhd *bh;
+ int get_some_more;
+ u32 amount_left_to_req, amount_left_to_write;
+ loff_t usb_offset, file_offset, file_offset_tmp;
+ unsigned int amount;
+ ssize_t nwritten;
+ int rc;
+
+ if (curlun->ro) {
+ curlun->sense_data = SS_WRITE_PROTECTED;
+ return -EINVAL;
+ }
+ spin_lock(&curlun->filp->f_lock);
+ curlun->filp->f_flags &= ~O_SYNC; /* Default is not to wait */
+ spin_unlock(&curlun->filp->f_lock);
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big
+ */
+ if (common->cmnd[0] == WRITE_6)
+ lba = get_unaligned_be24(&common->cmnd[1]);
+ else {
+ lba = get_unaligned_be32(&common->cmnd[2]);
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) and FUA (Force Unit Access = write directly to the
+ * medium). We don't implement DPO; we implement FUA by
+ * performing synchronous output.
+ */
+ if (common->cmnd[1] & ~0x18) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ if (!curlun->nofua && (common->cmnd[1] & 0x08)) { /* FUA */
+ spin_lock(&curlun->filp->f_lock);
+ curlun->filp->f_flags |= O_SYNC;
+ spin_unlock(&curlun->filp->f_lock);
+ }
+ }
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /* Carry out the file writes */
+ get_some_more = 1;
+ file_offset = usb_offset = ((loff_t) lba) << curlun->blkbits;
+ amount_left_to_req = common->data_size_from_cmnd;
+ amount_left_to_write = common->data_size_from_cmnd;
+
+ while (amount_left_to_write > 0) {
+
+ /* Queue a request for more data from the host */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY && get_some_more) {
+
+ /*
+ * Figure out how much we want to get:
+ * Try to get the remaining amount,
+ * but not more than the buffer size.
+ */
+ amount = min(amount_left_to_req, FSG_BUFLEN);
+
+ /* Beyond the end of the backing file? */
+ if (usb_offset >= curlun->file_length) {
+ get_some_more = 0;
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ usb_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ continue;
+ }
+
+ /* Get the next buffer */
+ usb_offset += amount;
+ common->usb_amount_left -= amount;
+ amount_left_to_req -= amount;
+ if (amount_left_to_req == 0)
+ get_some_more = 0;
+
+ /*
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(common, bh, amount);
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ continue;
+ }
+
+ /* Write the received data to the backing file */
+ bh = common->next_buffhd_to_drain;
+ if (bh->state == BUF_STATE_EMPTY && !get_some_more)
+ break; /* We stopped early */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ common->next_buffhd_to_drain = bh->next;
+ bh->state = BUF_STATE_EMPTY;
+
+ /* Did something go wrong with the transfer? */
+ if (bh->outreq->status != 0) {
+ curlun->sense_data = SS_COMMUNICATION_FAILURE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ amount = bh->outreq->actual;
+ if (curlun->file_length - file_offset < amount) {
+ LERROR(curlun,
+ "write %u @ %llu beyond end %llu\n",
+ amount, (unsigned long long)file_offset,
+ (unsigned long long)curlun->file_length);
+ amount = curlun->file_length - file_offset;
+ }
+
+ /* Don't accept excess data. The spec doesn't say
+ * what to do in this case. We'll ignore the error.
+ */
+ amount = min(amount, bh->bulk_out_intended_length);
+
+ /* Don't write a partial block */
+ amount = round_down(amount, curlun->blksize);
+ if (amount == 0)
+ goto empty_write;
+
+ /* Perform the write */
+ file_offset_tmp = file_offset;
+ nwritten = vfs_write(curlun->filp,
+ (char __user *)bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file write %u @ %llu -> %d\n", amount,
+ (unsigned long long)file_offset, (int)nwritten);
+ if (signal_pending(current))
+ return -EINTR; /* Interrupted! */
+
+ if (nwritten < 0) {
+ LDBG(curlun, "error in file write: %d\n",
+ (int)nwritten);
+ nwritten = 0;
+ } else if (nwritten < amount) {
+ LDBG(curlun, "partial file write: %d/%u\n",
+ (int)nwritten, amount);
+ nwritten = round_down(nwritten, curlun->blksize);
+ }
+ file_offset += nwritten;
+ amount_left_to_write -= nwritten;
+ common->residue -= nwritten;
+
+ /* If an error occurred, report it and its position */
+ if (nwritten < amount) {
+ curlun->sense_data = SS_WRITE_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ empty_write:
+ /* Did the host decide to stop early? */
+ if (bh->outreq->actual < bh->bulk_out_intended_length) {
+ common->short_packet_received = 1;
+ break;
+ }
+ continue;
+ }
+
+ /* Wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ return -EIO; /* No default reply */
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_synchronize_cache(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int rc;
+
+ /* We ignore the requested LBA and write out all file's
+ * dirty data buffers. */
+ rc = fsg_lun_fsync_sub(curlun);
+ if (rc)
+ curlun->sense_data = SS_WRITE_ERROR;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void invalidate_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+ struct inode *inode = filp->f_path.dentry->d_inode;
+ unsigned long rc;
+
+ rc = invalidate_mapping_pages(inode->i_mapping, 0, -1);
+ VLDBG(curlun, "invalidate_mapping_pages -> %ld\n", rc);
+}
+
+static int do_verify(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba;
+ u32 verification_length;
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ loff_t file_offset, file_offset_tmp;
+ u32 amount_left;
+ unsigned int amount;
+ ssize_t nread;
+
+ /*
+ * Get the starting Logical Block Address and check that it's
+ * not too big.
+ */
+ lba = get_unaligned_be32(&common->cmnd[2]);
+ if (lba >= curlun->num_sectors) {
+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ return -EINVAL;
+ }
+
+ /*
+ * We allow DPO (Disable Page Out = don't save data in the
+ * cache) but we don't implement it.
+ */
+ if (common->cmnd[1] & ~0x10) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ verification_length = get_unaligned_be16(&common->cmnd[7]);
+ if (unlikely(verification_length == 0))
+ return -EIO; /* No default reply */
+
+ /* Prepare to carry out the file verify */
+ amount_left = verification_length << curlun->blkbits;
+ file_offset = ((loff_t) lba) << curlun->blkbits;
+
+ /* Write out all the dirty buffers before invalidating them */
+ fsg_lun_fsync_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ invalidate_sub(curlun);
+ if (signal_pending(current))
+ return -EINTR;
+
+ /* Just try to read the requested blocks */
+ while (amount_left > 0) {
+ /*
+ * Figure out how much we need to read:
+ * Try to read the remaining amount, but not more than
+ * the buffer size.
+ * And don't try to read past the end of the file.
+ */
+ amount = min(amount_left, FSG_BUFLEN);
+ amount = min((loff_t)amount,
+ curlun->file_length - file_offset);
+ if (amount == 0) {
+ curlun->sense_data =
+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+
+ /* Perform the read */
+ file_offset_tmp = file_offset;
+ nread = vfs_read(curlun->filp,
+ (char __user *) bh->buf,
+ amount, &file_offset_tmp);
+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount,
+ (unsigned long long) file_offset,
+ (int) nread);
+ if (signal_pending(current))
+ return -EINTR;
+
+ if (nread < 0) {
+ LDBG(curlun, "error in file verify: %d\n", (int)nread);
+ nread = 0;
+ } else if (nread < amount) {
+ LDBG(curlun, "partial file verify: %d/%u\n",
+ (int)nread, amount);
+ nread = round_down(nread, curlun->blksize);
+ }
+ if (nread == 0) {
+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
+ curlun->sense_data_info =
+ file_offset >> curlun->blkbits;
+ curlun->info_valid = 1;
+ break;
+ }
+ file_offset += nread;
+ amount_left -= nread;
+ }
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ memset(buf, 0, 36);
+ buf[0] = 0x7f; /* Unsupported, no device-type */
+ buf[4] = 31; /* Additional length */
+ return 36;
+ }
+
+ buf[0] = curlun->cdrom ? TYPE_ROM : TYPE_DISK;
+ buf[1] = curlun->removable ? 0x80 : 0;
+ buf[2] = 2; /* ANSI SCSI level 2 */
+ buf[3] = 2; /* SCSI-2 INQUIRY data format */
+ buf[4] = 31; /* Additional length */
+ buf[5] = 0; /* No special options */
+ buf[6] = 0;
+ buf[7] = 0;
+ memcpy(buf + 8, common->inquiry_string, sizeof common->inquiry_string);
+ return 36;
+}
+
+static int do_request_sense(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+ u32 sd, sdinfo;
+ int valid;
+
+ /*
+ * From the SCSI-2 spec., section 7.9 (Unit attention condition):
+ *
+ * If a REQUEST SENSE command is received from an initiator
+ * with a pending unit attention condition (before the target
+ * generates the contingent allegiance condition), then the
+ * target shall either:
+ * a) report any pending sense data and preserve the unit
+ * attention condition on the logical unit, or,
+ * b) report the unit attention condition, may discard any
+ * pending sense data, and clear the unit attention
+ * condition on the logical unit for that initiator.
+ *
+ * FSG normally uses option a); enable this code to use option b).
+ */
+#if 0
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ }
+#endif
+
+ if (!curlun) { /* Unsupported LUNs are okay */
+ common->bad_lun_okay = 1;
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+ sdinfo = 0;
+ valid = 0;
+ } else {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ valid = curlun->info_valid << 7;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+
+ memset(buf, 0, 18);
+ buf[0] = valid | 0x70; /* Valid, current error */
+ buf[2] = SK(sd);
+ put_unaligned_be32(sdinfo, &buf[3]); /* Sense information */
+ buf[7] = 18 - 8; /* Additional sense length */
+ buf[12] = ASC(sd);
+ buf[13] = ASCQ(sd);
+ return 18;
+}
+
+static int do_read_capacity(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ int pmi = common->cmnd[8];
+ u8 *buf = (u8 *)bh->buf;
+
+ /* Check the PMI and LBA fields */
+ if (pmi > 1 || (pmi == 0 && lba != 0)) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ put_unaligned_be32(curlun->num_sectors - 1, &buf[0]);
+ /* Max logical block */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+ return 8;
+}
+
+static int do_read_header(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int msf = common->cmnd[1] & 0x02;
+ u32 lba = get_unaligned_be32(&common->cmnd[2]);
+ u8 *buf = (u8 *)bh->buf;
+
+ if (common->cmnd[1] & ~0x02) { /* 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_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int msf = common->cmnd[1] & 0x02;
+ int start_track = common->cmnd[6];
+ u8 *buf = (u8 *)bh->buf;
+
+ if ((common->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_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int mscmnd = common->cmnd[0];
+ u8 *buf = (u8 *) bh->buf;
+ u8 *buf0 = buf;
+ int pc, page_code;
+ int changeable_values, all_pages;
+ int valid_page = 0;
+ int len, limit;
+
+ if ((common->cmnd[1] & ~0x08) != 0) { /* Mask away DBD */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ pc = common->cmnd[2] >> 6;
+ page_code = common->cmnd[2] & 0x3f;
+ if (pc == 3) {
+ curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED;
+ return -EINVAL;
+ }
+ changeable_values = (pc == 1);
+ all_pages = (page_code == 0x3f);
+
+ /*
+ * Write the mode parameter header. Fixed values are: default
+ * medium type, no cache control (DPOFUA), and no block descriptors.
+ * The only variable value is the WriteProtect bit. We will fill in
+ * the mode data length later.
+ */
+ memset(buf, 0, 8);
+ if (mscmnd == MODE_SENSE) {
+ buf[2] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 4;
+ limit = 255;
+ } else { /* MODE_SENSE_10 */
+ buf[3] = (curlun->ro ? 0x80 : 0x00); /* WP, DPOFUA */
+ buf += 8;
+ limit = 65535; /* Should really be FSG_BUFLEN */
+ }
+
+ /* No block descriptors */
+
+ /*
+ * The mode pages, in numerical order. The only page we support
+ * is the Caching page.
+ */
+ if (page_code == 0x08 || all_pages) {
+ valid_page = 1;
+ buf[0] = 0x08; /* Page code */
+ buf[1] = 10; /* Page length */
+ memset(buf+2, 0, 10); /* None of the fields are changeable */
+
+ if (!changeable_values) {
+ buf[2] = 0x04; /* Write cache enable, */
+ /* Read cache not disabled */
+ /* No cache retention priorities */
+ put_unaligned_be16(0xffff, &buf[4]);
+ /* Don't disable prefetch */
+ /* Minimum prefetch = 0 */
+ put_unaligned_be16(0xffff, &buf[8]);
+ /* Maximum prefetch */
+ put_unaligned_be16(0xffff, &buf[10]);
+ /* Maximum prefetch ceiling */
+ }
+ buf += 12;
+ }
+
+ /*
+ * Check that a valid page was requested and the mode data length
+ * isn't too long.
+ */
+ len = buf - buf0;
+ if (!valid_page || len > limit) {
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ /* Store the mode data length */
+ if (mscmnd == MODE_SENSE)
+ buf0[0] = len - 1;
+ else
+ put_unaligned_be16(len - 2, buf0);
+ return len;
+}
+
+static int do_start_stop(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int loej, start;
+
+ if (!curlun) {
+ return -EINVAL;
+ } else if (!curlun->removable) {
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ } else if ((common->cmnd[1] & ~0x01) != 0 || /* Mask away Immed */
+ (common->cmnd[4] & ~0x03) != 0) { /* Mask LoEj, Start */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ loej = common->cmnd[4] & 0x02;
+ start = common->cmnd[4] & 0x01;
+
+ /*
+ * Our emulation doesn't support mounting; the medium is
+ * available for use as soon as it is loaded.
+ */
+ if (start) {
+ if (!fsg_lun_is_open(curlun)) {
+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return -EINVAL;
+ }
+ return 0;
+ }
+
+ /* Are we allowed to unload the media? */
+ if (curlun->prevent_medium_removal) {
+ LDBG(curlun, "unload attempt prevented\n");
+ curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED;
+ return -EINVAL;
+ }
+
+ if (!loej)
+ return 0;
+
+ /* Simulate an unload/eject */
+ if (common->ops && common->ops->pre_eject) {
+ int r = common->ops->pre_eject(common, curlun,
+ curlun - common->luns);
+ if (unlikely(r < 0))
+ return r;
+ else if (r)
+ return 0;
+ }
+
+ up_read(&common->filesem);
+ down_write(&common->filesem);
+ fsg_lun_close(curlun);
+ up_write(&common->filesem);
+ down_read(&common->filesem);
+
+ return common->ops && common->ops->post_eject
+ ? min(0, common->ops->post_eject(common, curlun,
+ curlun - common->luns))
+ : 0;
+}
+
+static int do_prevent_allow(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ int prevent;
+
+ if (!common->curlun) {
+ return -EINVAL;
+ } else if (!common->curlun->removable) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+ }
+
+ prevent = common->cmnd[4] & 0x01;
+ if ((common->cmnd[4] & ~0x01) != 0) { /* Mask away Prevent */
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+
+ if (curlun->prevent_medium_removal && !prevent)
+ fsg_lun_fsync_sub(curlun);
+ curlun->prevent_medium_removal = prevent;
+ return 0;
+}
+
+static int do_read_format_capacities(struct fsg_common *common,
+ struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+ u8 *buf = (u8 *) bh->buf;
+
+ buf[0] = buf[1] = buf[2] = 0;
+ buf[3] = 8; /* Only the Current/Maximum Capacity Descriptor */
+ buf += 4;
+
+ put_unaligned_be32(curlun->num_sectors, &buf[0]);
+ /* Number of blocks */
+ put_unaligned_be32(curlun->blksize, &buf[4]);/* Block length */
+ buf[4] = 0x02; /* Current capacity */
+ return 12;
+}
+
+static int do_mode_select(struct fsg_common *common, struct fsg_buffhd *bh)
+{
+ struct fsg_lun *curlun = common->curlun;
+
+ /* We don't support MODE SELECT */
+ if (curlun)
+ curlun->sense_data = SS_INVALID_COMMAND;
+ return -EINVAL;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ rc = fsg_set_halt(fsg, fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint halt\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_halt(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+{
+ int rc;
+
+ DBG(fsg, "bulk-in set wedge\n");
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ if (rc == -EAGAIN)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+
+ /* Wait for a short time and then try again */
+ if (msleep_interruptible(100) != 0)
+ return -EINTR;
+ rc = usb_ep_set_wedge(fsg->bulk_in);
+ }
+ return rc;
+}
+
+static int throw_away_data(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ u32 amount;
+ int rc;
+
+ for (bh = common->next_buffhd_to_drain;
+ bh->state != BUF_STATE_EMPTY || common->usb_amount_left > 0;
+ bh = common->next_buffhd_to_drain) {
+
+ /* Throw away the data in a filled buffer */
+ if (bh->state == BUF_STATE_FULL) {
+ smp_rmb();
+ bh->state = BUF_STATE_EMPTY;
+ common->next_buffhd_to_drain = bh->next;
+
+ /* A short packet or an error ends everything */
+ if (bh->outreq->actual < bh->bulk_out_intended_length ||
+ bh->outreq->status != 0) {
+ raise_exception(common,
+ FSG_STATE_ABORT_BULK_OUT);
+ return -EINTR;
+ }
+ continue;
+ }
+
+ /* Try to submit another request if we need one */
+ bh = common->next_buffhd_to_fill;
+ if (bh->state == BUF_STATE_EMPTY
+ && common->usb_amount_left > 0) {
+ amount = min(common->usb_amount_left, FSG_BUFLEN);
+
+ /*
+ * Except at the end of the transfer, amount will be
+ * equal to the buffer size, which is divisible by
+ * the bulk-out maxpacket size.
+ */
+ set_bulk_out_req_length(common, bh, amount);
+ if (!start_out_transfer(common, bh))
+ /* Dunno what to do if common->fsg is NULL */
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ common->usb_amount_left -= amount;
+ continue;
+ }
+
+ /* Otherwise wait for something to happen */
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ return 0;
+}
+
+static int finish_reply(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh = common->next_buffhd_to_fill;
+ int rc = 0;
+
+ switch (common->data_dir) {
+ case DATA_DIR_NONE:
+ break; /* Nothing to send */
+
+ /*
+ * If we don't know whether the host wants to read or write,
+ * this must be CB or CBI with an unknown command. We mustn't
+ * try to send or receive any data. So stall both bulk pipes
+ * if we can and wait for a reset.
+ */
+ case DATA_DIR_UNKNOWN:
+ if (!common->can_stall) {
+ /* Nothing */
+ } else if (fsg_is_set(common)) {
+ fsg_set_halt(common->fsg, common->fsg->bulk_out);
+ rc = halt_bulk_in_endpoint(common->fsg);
+ } else {
+ /* Don't know what to do if common->fsg is NULL */
+ rc = -EIO;
+ }
+ break;
+
+ /* All but the last buffer of data must have already been sent */
+ case DATA_DIR_TO_HOST:
+ if (common->data_size == 0) {
+ /* Nothing to send */
+
+ /* Don't know what to do if common->fsg is NULL */
+ } else if (!fsg_is_set(common)) {
+ rc = -EIO;
+
+ /* If there's no residue, simply send the last buffer */
+ } else if (common->residue == 0) {
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ return -EIO;
+ common->next_buffhd_to_fill = bh->next;
+
+ /*
+ * For Bulk-only, mark the end of the data with a short
+ * packet. If we are allowed to stall, halt the bulk-in
+ * endpoint. (Note: This violates the Bulk-Only Transport
+ * specification, which requires us to pad the data if we
+ * don't halt the endpoint. Presumably nobody will mind.)
+ */
+ } else {
+ bh->inreq->zero = 1;
+ if (!start_in_transfer(common, bh))
+ rc = -EIO;
+ common->next_buffhd_to_fill = bh->next;
+ if (common->can_stall)
+ rc = halt_bulk_in_endpoint(common->fsg);
+ }
+ break;
+
+ /*
+ * We have processed all we want from the data the host has sent.
+ * There may still be outstanding bulk-out requests.
+ */
+ case DATA_DIR_FROM_HOST:
+ if (common->residue == 0) {
+ /* Nothing to receive */
+
+ /* Did the host stop sending unexpectedly early? */
+ } else if (common->short_packet_received) {
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+
+ /*
+ * We haven't processed all the incoming data. Even though
+ * we may be allowed to stall, doing so would cause a race.
+ * The controller may already have ACK'ed all the remaining
+ * bulk-out packets, in which case the host wouldn't see a
+ * STALL. Not realizing the endpoint was halted, it wouldn't
+ * clear the halt -- leading to problems later on.
+ */
+#if 0
+ } else if (common->can_stall) {
+ if (fsg_is_set(common))
+ fsg_set_halt(common->fsg,
+ common->fsg->bulk_out);
+ raise_exception(common, FSG_STATE_ABORT_BULK_OUT);
+ rc = -EINTR;
+#endif
+
+ /*
+ * We can't stall. Read in the excess data and throw it
+ * all away.
+ */
+ } else {
+ rc = throw_away_data(common);
+ }
+ break;
+ }
+ return rc;
+}
+
+static int send_status(struct fsg_common *common)
+{
+ struct fsg_lun *curlun = common->curlun;
+ struct fsg_buffhd *bh;
+ struct bulk_cs_wrap *csw;
+ int rc;
+ u8 status = US_BULK_STAT_OK;
+ u32 sd, sdinfo = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ if (curlun) {
+ sd = curlun->sense_data;
+ sdinfo = curlun->sense_data_info;
+ } else if (common->bad_lun_okay)
+ sd = SS_NO_SENSE;
+ else
+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
+
+ if (common->phase_error) {
+ DBG(common, "sending phase-error status\n");
+ status = US_BULK_STAT_PHASE;
+ sd = SS_INVALID_COMMAND;
+ } else if (sd != SS_NO_SENSE) {
+ DBG(common, "sending command-failure status\n");
+ status = US_BULK_STAT_FAIL;
+ VDBG(common, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;"
+ " info x%x\n",
+ SK(sd), ASC(sd), ASCQ(sd), sdinfo);
+ }
+
+ /* Store and send the Bulk-only CSW */
+ csw = (void *)bh->buf;
+
+ csw->Signature = cpu_to_le32(US_BULK_CS_SIGN);
+ csw->Tag = common->tag;
+ csw->Residue = cpu_to_le32(common->residue);
+ csw->Status = status;
+
+ bh->inreq->length = US_BULK_CS_WRAP_LEN;
+ bh->inreq->zero = 0;
+ if (!start_in_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ common->next_buffhd_to_fill = bh->next;
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Check whether the command is properly formed and whether its data size
+ * and direction agree with the values we already have.
+ */
+static int check_command(struct fsg_common *common, int cmnd_size,
+ enum data_direction data_dir, unsigned int mask,
+ int needs_medium, const char *name)
+{
+ int i;
+ int lun = common->cmnd[1] >> 5;
+ static const char dirletter[4] = {'u', 'o', 'i', 'n'};
+ char hdlen[20];
+ struct fsg_lun *curlun;
+
+ hdlen[0] = 0;
+ if (common->data_dir != DATA_DIR_UNKNOWN)
+ sprintf(hdlen, ", H%c=%u", dirletter[(int) common->data_dir],
+ common->data_size);
+ VDBG(common, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n",
+ name, cmnd_size, dirletter[(int) data_dir],
+ common->data_size_from_cmnd, common->cmnd_size, hdlen);
+
+ /*
+ * We can't reply at all until we know the correct data direction
+ * and size.
+ */
+ if (common->data_size_from_cmnd == 0)
+ data_dir = DATA_DIR_NONE;
+ if (common->data_size < common->data_size_from_cmnd) {
+ /*
+ * Host data size < Device data size is a phase error.
+ * Carry out the command, but only transfer as much as
+ * we are allowed.
+ */
+ common->data_size_from_cmnd = common->data_size;
+ common->phase_error = 1;
+ }
+ common->residue = common->data_size;
+ common->usb_amount_left = common->data_size;
+
+ /* Conflicting data directions is a phase error */
+ if (common->data_dir != data_dir && common->data_size_from_cmnd > 0) {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+
+ /* Verify the length of the command itself */
+ if (cmnd_size != common->cmnd_size) {
+
+ /*
+ * Special case workaround: There are plenty of buggy SCSI
+ * implementations. Many have issues with cbw->Length
+ * field passing a wrong command size. For those cases we
+ * always try to work around the problem by using the length
+ * sent by the host side provided it is at least as large
+ * as the correct command length.
+ * Examples of such cases would be MS-Windows, which issues
+ * REQUEST SENSE with cbw->Length == 12 where it should
+ * be 6, and xbox360 issuing INQUIRY, TEST UNIT READY and
+ * REQUEST SENSE with cbw->Length == 10 where it should
+ * be 6 as well.
+ */
+ if (cmnd_size <= common->cmnd_size) {
+ DBG(common, "%s is buggy! Expected length %d "
+ "but we got %d\n", name,
+ cmnd_size, common->cmnd_size);
+ cmnd_size = common->cmnd_size;
+ } else {
+ common->phase_error = 1;
+ return -EINVAL;
+ }
+ }
+
+ /* Check that the LUN values are consistent */
+ if (common->lun != lun)
+ DBG(common, "using LUN %d from CBW, not LUN %d from CDB\n",
+ common->lun, lun);
+
+ /* Check the LUN */
+ curlun = common->curlun;
+ if (curlun) {
+ if (common->cmnd[0] != REQUEST_SENSE) {
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ } else {
+ common->bad_lun_okay = 0;
+
+ /*
+ * INQUIRY and REQUEST SENSE commands are explicitly allowed
+ * to use unsupported LUNs; all others may not.
+ */
+ if (common->cmnd[0] != INQUIRY &&
+ common->cmnd[0] != REQUEST_SENSE) {
+ DBG(common, "unsupported LUN %d\n", common->lun);
+ return -EINVAL;
+ }
+ }
+
+ /*
+ * If a unit attention condition exists, only INQUIRY and
+ * REQUEST SENSE commands are allowed; anything else must fail.
+ */
+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE &&
+ common->cmnd[0] != INQUIRY &&
+ common->cmnd[0] != REQUEST_SENSE) {
+ curlun->sense_data = curlun->unit_attention_data;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ return -EINVAL;
+ }
+
+ /* Check that only command bytes listed in the mask are non-zero */
+ common->cmnd[1] &= 0x1f; /* Mask away the LUN */
+ for (i = 1; i < cmnd_size; ++i) {
+ if (common->cmnd[i] && !(mask & (1 << i))) {
+ if (curlun)
+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
+ return -EINVAL;
+ }
+ }
+
+ /* If the medium isn't mounted and the command needs to access
+ * it, return an error. */
+ if (curlun && !fsg_lun_is_open(curlun) && needs_medium) {
+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* wrapper of check_command for data size in blocks handling */
+static int check_command_size_in_blocks(struct fsg_common *common,
+ int cmnd_size, enum data_direction data_dir,
+ unsigned int mask, int needs_medium, const char *name)
+{
+ if (common->curlun)
+ common->data_size_from_cmnd <<= common->curlun->blkbits;
+ return check_command(common, cmnd_size, data_dir,
+ mask, needs_medium, name);
+}
+
+static int do_scsi_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc;
+ int reply = -EINVAL;
+ int i;
+ static char unknown[16];
+
+ dump_cdb(common);
+
+ /* Wait for the next buffer to become available for data or status */
+ bh = common->next_buffhd_to_fill;
+ common->next_buffhd_to_drain = bh;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ common->phase_error = 0;
+ common->short_packet_received = 0;
+
+ down_read(&common->filesem); /* We're using the backing file */
+ switch (common->cmnd[0]) {
+
+ case INQUIRY:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "INQUIRY");
+ if (reply == 0)
+ reply = do_inquiry(common, bh);
+ break;
+
+ case MODE_SELECT:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_FROM_HOST,
+ (1<<1) | (1<<4), 0,
+ "MODE SELECT(6)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case MODE_SELECT_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_FROM_HOST,
+ (1<<1) | (3<<7), 0,
+ "MODE SELECT(10)");
+ if (reply == 0)
+ reply = do_mode_select(common, bh);
+ break;
+
+ case MODE_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (1<<4), 0,
+ "MODE SENSE(6)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case MODE_SENSE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (1<<1) | (1<<2) | (3<<7), 0,
+ "MODE SENSE(10)");
+ if (reply == 0)
+ reply = do_mode_sense(common, bh);
+ break;
+
+ case ALLOW_MEDIUM_REMOVAL:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<4), 0,
+ "PREVENT-ALLOW MEDIUM REMOVAL");
+ if (reply == 0)
+ reply = do_prevent_allow(common);
+ break;
+
+ case READ_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0) ? 256 : i;
+ reply = check_command_size_in_blocks(common, 6,
+ DATA_DIR_TO_HOST,
+ (7<<1) | (1<<4), 1,
+ "READ(6)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command_size_in_blocks(common, 10,
+ DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "READ(10)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]);
+ reply = check_command_size_in_blocks(common, 12,
+ DATA_DIR_TO_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "READ(12)");
+ if (reply == 0)
+ reply = do_read(common);
+ break;
+
+ case READ_CAPACITY:
+ common->data_size_from_cmnd = 8;
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (0xf<<2) | (1<<8), 1,
+ "READ CAPACITY");
+ if (reply == 0)
+ reply = do_read_capacity(common, bh);
+ break;
+
+ case READ_HEADER:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7) | (0x1f<<1), 1,
+ "READ HEADER");
+ if (reply == 0)
+ reply = do_read_header(common, bh);
+ break;
+
+ case READ_TOC:
+ if (!common->curlun || !common->curlun->cdrom)
+ goto unknown_cmnd;
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (7<<6) | (1<<1), 1,
+ "READ TOC");
+ if (reply == 0)
+ reply = do_read_toc(common, bh);
+ break;
+
+ case READ_FORMAT_CAPACITIES:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command(common, 10, DATA_DIR_TO_HOST,
+ (3<<7), 1,
+ "READ FORMAT CAPACITIES");
+ if (reply == 0)
+ reply = do_read_format_capacities(common, bh);
+ break;
+
+ case REQUEST_SENSE:
+ common->data_size_from_cmnd = common->cmnd[4];
+ reply = check_command(common, 6, DATA_DIR_TO_HOST,
+ (1<<4), 0,
+ "REQUEST SENSE");
+ if (reply == 0)
+ reply = do_request_sense(common, bh);
+ break;
+
+ case START_STOP:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ (1<<1) | (1<<4), 0,
+ "START-STOP UNIT");
+ if (reply == 0)
+ reply = do_start_stop(common);
+ break;
+
+ case SYNCHRONIZE_CACHE:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (0xf<<2) | (3<<7), 1,
+ "SYNCHRONIZE CACHE");
+ if (reply == 0)
+ reply = do_synchronize_cache(common);
+ break;
+
+ case TEST_UNIT_READY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 6, DATA_DIR_NONE,
+ 0, 1,
+ "TEST UNIT READY");
+ break;
+
+ /*
+ * Although optional, this command is used by MS-Windows. We
+ * support a minimal version: BytChk must be 0.
+ */
+ case VERIFY:
+ common->data_size_from_cmnd = 0;
+ reply = check_command(common, 10, DATA_DIR_NONE,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "VERIFY");
+ if (reply == 0)
+ reply = do_verify(common);
+ break;
+
+ case WRITE_6:
+ i = common->cmnd[4];
+ common->data_size_from_cmnd = (i == 0) ? 256 : i;
+ reply = check_command_size_in_blocks(common, 6,
+ DATA_DIR_FROM_HOST,
+ (7<<1) | (1<<4), 1,
+ "WRITE(6)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case WRITE_10:
+ common->data_size_from_cmnd =
+ get_unaligned_be16(&common->cmnd[7]);
+ reply = check_command_size_in_blocks(common, 10,
+ DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (3<<7), 1,
+ "WRITE(10)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ case WRITE_12:
+ common->data_size_from_cmnd =
+ get_unaligned_be32(&common->cmnd[6]);
+ reply = check_command_size_in_blocks(common, 12,
+ DATA_DIR_FROM_HOST,
+ (1<<1) | (0xf<<2) | (0xf<<6), 1,
+ "WRITE(12)");
+ if (reply == 0)
+ reply = do_write(common);
+ break;
+
+ /*
+ * Some mandatory commands that we recognize but don't implement.
+ * They don't mean much in this setting. It's left as an exercise
+ * for anyone interested to implement RESERVE and RELEASE in terms
+ * of Posix locks.
+ */
+ case FORMAT_UNIT:
+ case RELEASE:
+ case RESERVE:
+ case SEND_DIAGNOSTIC:
+ /* Fall through */
+
+ default:
+unknown_cmnd:
+ common->data_size_from_cmnd = 0;
+ sprintf(unknown, "Unknown x%02x", common->cmnd[0]);
+ reply = check_command(common, common->cmnd_size,
+ DATA_DIR_UNKNOWN, ~0, 0, unknown);
+ if (reply == 0) {
+ common->curlun->sense_data = SS_INVALID_COMMAND;
+ reply = -EINVAL;
+ }
+ break;
+ }
+ up_read(&common->filesem);
+
+ if (reply == -EINTR || signal_pending(current))
+ return -EINTR;
+
+ /* Set up the single reply buffer for finish_reply() */
+ if (reply == -EINVAL)
+ reply = 0; /* Error reply length */
+ if (reply >= 0 && common->data_dir == DATA_DIR_TO_HOST) {
+ reply = min((u32)reply, common->data_size_from_cmnd);
+ bh->inreq->length = reply;
+ bh->state = BUF_STATE_FULL;
+ common->residue -= reply;
+ } /* Otherwise it's already set */
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+{
+ struct usb_request *req = bh->outreq;
+ struct bulk_cb_wrap *cbw = req->buf;
+ struct fsg_common *common = fsg->common;
+
+ /* Was this a real packet? Should it be ignored? */
+ if (req->status || test_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags))
+ return -EINVAL;
+
+ /* Is the CBW valid? */
+ if (req->actual != US_BULK_CB_WRAP_LEN ||
+ cbw->Signature != cpu_to_le32(
+ US_BULK_CB_SIGN)) {
+ DBG(fsg, "invalid CBW: len %u sig 0x%x\n",
+ req->actual,
+ le32_to_cpu(cbw->Signature));
+
+ /*
+ * The Bulk-only spec says we MUST stall the IN endpoint
+ * (6.6.1), so it's unavoidable. It also says we must
+ * retain this state until the next reset, but there's
+ * no way to tell the controller driver it should ignore
+ * Clear-Feature(HALT) requests.
+ *
+ * We aren't required to halt the OUT endpoint; instead
+ * we can simply accept and discard any data received
+ * until the next reset.
+ */
+ wedge_bulk_in_endpoint(fsg);
+ set_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+ return -EINVAL;
+ }
+
+ /* Is the CBW meaningful? */
+ if (cbw->Lun >= FSG_MAX_LUNS || cbw->Flags & ~US_BULK_FLAG_IN ||
+ cbw->Length <= 0 || cbw->Length > MAX_COMMAND_SIZE) {
+ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, "
+ "cmdlen %u\n",
+ cbw->Lun, cbw->Flags, cbw->Length);
+
+ /*
+ * We can do anything we want here, so let's stall the
+ * bulk pipes if we are allowed to.
+ */
+ if (common->can_stall) {
+ fsg_set_halt(fsg, fsg->bulk_out);
+ halt_bulk_in_endpoint(fsg);
+ }
+ return -EINVAL;
+ }
+
+ /* Save the command for later */
+ common->cmnd_size = cbw->Length;
+ memcpy(common->cmnd, cbw->CDB, common->cmnd_size);
+ if (cbw->Flags & US_BULK_FLAG_IN)
+ common->data_dir = DATA_DIR_TO_HOST;
+ else
+ common->data_dir = DATA_DIR_FROM_HOST;
+ common->data_size = le32_to_cpu(cbw->DataTransferLength);
+ if (common->data_size == 0)
+ common->data_dir = DATA_DIR_NONE;
+ common->lun = cbw->Lun;
+ if (common->lun >= 0 && common->lun < common->nluns)
+ common->curlun = &common->luns[common->lun];
+ else
+ common->curlun = NULL;
+ common->tag = cbw->Tag;
+ return 0;
+}
+
+static int get_next_command(struct fsg_common *common)
+{
+ struct fsg_buffhd *bh;
+ int rc = 0;
+
+ /* Wait for the next buffer to become available */
+ bh = common->next_buffhd_to_fill;
+ while (bh->state != BUF_STATE_EMPTY) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+
+ /* Queue a request to read a Bulk-only CBW */
+ set_bulk_out_req_length(common, bh, US_BULK_CB_WRAP_LEN);
+ if (!start_out_transfer(common, bh))
+ /* Don't know what to do if common->fsg is NULL */
+ return -EIO;
+
+ /*
+ * We will drain the buffer in software, which means we
+ * can reuse it for the next filling. No need to advance
+ * next_buffhd_to_fill.
+ */
+
+ /* Wait for the CBW to arrive */
+ while (bh->state != BUF_STATE_FULL) {
+ rc = sleep_thread(common);
+ if (rc)
+ return rc;
+ }
+ smp_rmb();
+ rc = fsg_is_set(common) ? received_cbw(common->fsg, bh) : -EIO;
+ bh->state = BUF_STATE_EMPTY;
+
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int alloc_request(struct fsg_common *common, struct usb_ep *ep,
+ struct usb_request **preq)
+{
+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (*preq)
+ return 0;
+ ERROR(common, "can't allocate request for %s\n", ep->name);
+ return -ENOMEM;
+}
+
+/* Reset interface setting and re-init endpoint state (toggle etc). */
+static int do_set_interface(struct fsg_common *common, struct fsg_dev *new_fsg)
+{
+ struct fsg_dev *fsg;
+ int i, rc = 0;
+
+ if (common->running)
+ DBG(common, "reset interface\n");
+
+reset:
+ /* Deallocate the requests */
+ if (common->fsg) {
+ fsg = common->fsg;
+
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ if (bh->inreq) {
+ usb_ep_free_request(fsg->bulk_in, bh->inreq);
+ bh->inreq = NULL;
+ }
+ if (bh->outreq) {
+ usb_ep_free_request(fsg->bulk_out, bh->outreq);
+ bh->outreq = NULL;
+ }
+ }
+
+ /* Disable the endpoints */
+ if (fsg->bulk_in_enabled) {
+ usb_ep_disable(fsg->bulk_in);
+ fsg->bulk_in_enabled = 0;
+ }
+ if (fsg->bulk_out_enabled) {
+ usb_ep_disable(fsg->bulk_out);
+ fsg->bulk_out_enabled = 0;
+ }
+
+ common->fsg = NULL;
+ wake_up(&common->fsg_wait);
+ }
+
+ common->running = 0;
+ if (!new_fsg || rc)
+ return rc;
+
+ common->fsg = new_fsg;
+ fsg = common->fsg;
+
+ /* Enable the endpoints */
+ rc = config_ep_by_speed(common->gadget, &(fsg->function), fsg->bulk_in);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_in);
+ if (rc)
+ goto reset;
+ fsg->bulk_in->driver_data = common;
+ fsg->bulk_in_enabled = 1;
+
+ rc = config_ep_by_speed(common->gadget, &(fsg->function),
+ fsg->bulk_out);
+ if (rc)
+ goto reset;
+ rc = usb_ep_enable(fsg->bulk_out);
+ if (rc)
+ goto reset;
+ fsg->bulk_out->driver_data = common;
+ fsg->bulk_out_enabled = 1;
+ common->bulk_out_maxpacket = usb_endpoint_maxp(fsg->bulk_out->desc);
+ clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags);
+
+ /* Allocate the requests */
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ struct fsg_buffhd *bh = &common->buffhds[i];
+
+ rc = alloc_request(common, fsg->bulk_in, &bh->inreq);
+ if (rc)
+ goto reset;
+ rc = alloc_request(common, fsg->bulk_out, &bh->outreq);
+ if (rc)
+ goto reset;
+ bh->inreq->buf = bh->outreq->buf = bh->buf;
+ bh->inreq->context = bh->outreq->context = bh;
+ bh->inreq->complete = bulk_in_complete;
+ bh->outreq->complete = bulk_out_complete;
+ }
+
+ common->running = 1;
+ for (i = 0; i < common->nluns; ++i)
+ common->luns[i].unit_attention_data = SS_RESET_OCCURRED;
+ return rc;
+}
+
+
+/****************************** ALT CONFIGS ******************************/
+
+static int fsg_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = fsg;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ return USB_GADGET_DELAYED_STATUS;
+}
+
+static void fsg_disable(struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void handle_exception(struct fsg_common *common)
+{
+ siginfo_t info;
+ int i;
+ struct fsg_buffhd *bh;
+ enum fsg_state old_state;
+ struct fsg_lun *curlun;
+ unsigned int exception_req_tag;
+
+ /*
+ * Clear the existing signals. Anything but SIGUSR1 is converted
+ * into a high-priority EXIT exception.
+ */
+ for (;;) {
+ int sig =
+ dequeue_signal_lock(current, ¤t->blocked, &info);
+ if (!sig)
+ break;
+ if (sig != SIGUSR1) {
+ if (common->state < FSG_STATE_EXIT)
+ DBG(common, "Main thread exiting on signal\n");
+ raise_exception(common, FSG_STATE_EXIT);
+ }
+ }
+
+ /* Cancel all the pending transfers */
+ if (likely(common->fsg)) {
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ if (bh->inreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_in, bh->inreq);
+ if (bh->outreq_busy)
+ usb_ep_dequeue(common->fsg->bulk_out,
+ bh->outreq);
+ }
+
+ /* Wait until everything is idle */
+ for (;;) {
+ int num_active = 0;
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ num_active += bh->inreq_busy + bh->outreq_busy;
+ }
+ if (num_active == 0)
+ break;
+ if (sleep_thread(common))
+ return;
+ }
+
+ /* Clear out the controller's fifos */
+ if (common->fsg->bulk_in_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_in);
+ if (common->fsg->bulk_out_enabled)
+ usb_ep_fifo_flush(common->fsg->bulk_out);
+ }
+
+ /*
+ * Reset the I/O buffer states and pointers, the SCSI
+ * state, and the exception. Then invoke the handler.
+ */
+ spin_lock_irq(&common->lock);
+
+ for (i = 0; i < fsg_num_buffers; ++i) {
+ bh = &common->buffhds[i];
+ bh->state = BUF_STATE_EMPTY;
+ }
+ common->next_buffhd_to_fill = &common->buffhds[0];
+ common->next_buffhd_to_drain = &common->buffhds[0];
+ exception_req_tag = common->exception_req_tag;
+ old_state = common->state;
+
+ if (old_state == FSG_STATE_ABORT_BULK_OUT)
+ common->state = FSG_STATE_STATUS_PHASE;
+ else {
+ for (i = 0; i < common->nluns; ++i) {
+ curlun = &common->luns[i];
+ curlun->prevent_medium_removal = 0;
+ curlun->sense_data = SS_NO_SENSE;
+ curlun->unit_attention_data = SS_NO_SENSE;
+ curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
+ }
+ common->state = FSG_STATE_IDLE;
+ }
+ spin_unlock_irq(&common->lock);
+
+ /* Carry out any extra actions required for the exception */
+ switch (old_state) {
+ case FSG_STATE_ABORT_BULK_OUT:
+ send_status(common);
+ spin_lock_irq(&common->lock);
+ if (common->state == FSG_STATE_STATUS_PHASE)
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_RESET:
+ /*
+ * In case we were forced against our will to halt a
+ * bulk endpoint, clear the halt now. (The SuperH UDC
+ * requires this.)
+ */
+ if (!fsg_is_set(common))
+ break;
+ if (test_and_clear_bit(IGNORE_BULK_OUT,
+ &common->fsg->atomic_bitflags))
+ usb_ep_clear_halt(common->fsg->bulk_in);
+
+ if (common->ep0_req_tag == exception_req_tag)
+ ep0_queue(common); /* Complete the status stage */
+
+ /*
+ * Technically this should go here, but it would only be
+ * a waste of time. Ditto for the INTERFACE_CHANGE and
+ * CONFIG_CHANGE cases.
+ */
+ /* for (i = 0; i < common->nluns; ++i) */
+ /* common->luns[i].unit_attention_data = */
+ /* SS_RESET_OCCURRED; */
+ break;
+
+ case FSG_STATE_CONFIG_CHANGE:
+ do_set_interface(common, common->new_fsg);
+ if (common->new_fsg)
+ usb_composite_setup_continue(common->cdev);
+ break;
+
+ case FSG_STATE_EXIT:
+ case FSG_STATE_TERMINATED:
+ do_set_interface(common, NULL); /* Free resources */
+ spin_lock_irq(&common->lock);
+ common->state = FSG_STATE_TERMINATED; /* Stop the thread */
+ spin_unlock_irq(&common->lock);
+ break;
+
+ case FSG_STATE_INTERFACE_CHANGE:
+ case FSG_STATE_DISCONNECT:
+ case FSG_STATE_COMMAND_PHASE:
+ case FSG_STATE_DATA_PHASE:
+ case FSG_STATE_STATUS_PHASE:
+ case FSG_STATE_IDLE:
+ break;
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static int fsg_main_thread(void *common_)
+{
+ struct fsg_common *common = common_;
+
+ /*
+ * Allow the thread to be killed by a signal, but set the signal mask
+ * to block everything but INT, TERM, KILL, and USR1.
+ */
+ allow_signal(SIGINT);
+ allow_signal(SIGTERM);
+ allow_signal(SIGKILL);
+ allow_signal(SIGUSR1);
+
+ /* Allow the thread to be frozen */
+ set_freezable();
+
+ /*
+ * Arrange for userspace references to be interpreted as kernel
+ * pointers. That way we can pass a kernel pointer to a routine
+ * that expects a __user pointer and it will work okay.
+ */
+ set_fs(get_ds());
+
+ /* The main loop */
+ while (common->state != FSG_STATE_TERMINATED) {
+ if (exception_in_progress(common) || signal_pending(current)) {
+ handle_exception(common);
+ continue;
+ }
+
+ if (!common->running) {
+ sleep_thread(common);
+ continue;
+ }
+
+ if (get_next_command(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_DATA_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (do_scsi_command(common) || finish_reply(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_STATUS_PHASE;
+ spin_unlock_irq(&common->lock);
+
+ if (send_status(common))
+ continue;
+
+ spin_lock_irq(&common->lock);
+ if (!exception_in_progress(common))
+ common->state = FSG_STATE_IDLE;
+ spin_unlock_irq(&common->lock);
+ }
+
+ spin_lock_irq(&common->lock);
+ common->thread_task = NULL;
+ spin_unlock_irq(&common->lock);
+
+ if (!common->ops || !common->ops->thread_exits
+ || common->ops->thread_exits(common) < 0) {
+ struct fsg_lun *curlun = common->luns;
+ unsigned i = common->nluns;
+
+ down_write(&common->filesem);
+ for (; i--; ++curlun) {
+ if (!fsg_lun_is_open(curlun))
+ continue;
+
+ fsg_lun_close(curlun);
+ curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+ }
+ up_write(&common->filesem);
+ }
+
+ /* Let fsg_unbind() know the thread has exited */
+ complete_and_exit(&common->thread_notifier, 0);
+}
+
+
+/*************************** DEVICE ATTRIBUTES ***************************/
+
+static DEVICE_ATTR(ro, 0644, fsg_show_ro, fsg_store_ro);
+static DEVICE_ATTR(nofua, 0644, fsg_show_nofua, fsg_store_nofua);
+static DEVICE_ATTR(file, 0644, fsg_show_file, fsg_store_file);
+
+static struct device_attribute dev_attr_ro_cdrom =
+ __ATTR(ro, 0444, fsg_show_ro, NULL);
+static struct device_attribute dev_attr_file_nonremovable =
+ __ATTR(file, 0444, fsg_show_file, NULL);
+
+
+/****************************** FSG COMMON ******************************/
+
+static void fsg_common_release(struct kref *ref);
+
+static void fsg_lun_release(struct device *dev)
+{
+ /* Nothing needs to be done */
+}
+
+static inline void fsg_common_get(struct fsg_common *common)
+{
+ kref_get(&common->ref);
+}
+
+static inline void fsg_common_put(struct fsg_common *common)
+{
+ kref_put(&common->ref, fsg_common_release);
+}
+
+static struct fsg_common *fsg_common_init(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ struct fsg_config *cfg)
+{
+ struct usb_gadget *gadget = cdev->gadget;
+ struct fsg_buffhd *bh;
+ struct fsg_lun *curlun;
+ struct fsg_lun_config *lcfg;
+ int nluns, i, rc;
+ char *pathbuf;
+
+ rc = fsg_num_buffers_validate();
+ if (rc != 0)
+ return ERR_PTR(rc);
+
+ /* Find out how many LUNs there should be */
+ nluns = cfg->nluns;
+ if (nluns < 1 || nluns > FSG_MAX_LUNS) {
+ dev_err(&gadget->dev, "invalid number of LUNs: %u\n", nluns);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* Allocate? */
+ if (!common) {
+ common = kzalloc(sizeof *common, GFP_KERNEL);
+ if (!common)
+ return ERR_PTR(-ENOMEM);
+ common->free_storage_on_release = 1;
+ } else {
+ memset(common, 0, sizeof *common);
+ common->free_storage_on_release = 0;
+ }
+
+ common->buffhds = kcalloc(fsg_num_buffers,
+ sizeof *(common->buffhds), GFP_KERNEL);
+ if (!common->buffhds) {
+ if (common->free_storage_on_release)
+ kfree(common);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ common->ops = cfg->ops;
+ common->private_data = cfg->private_data;
+
+ common->gadget = gadget;
+ common->ep0 = gadget->ep0;
+ common->ep0req = cdev->req;
+ common->cdev = cdev;
+
+ /* Maybe allocate device-global string IDs, and patch descriptors */
+ if (fsg_strings[FSG_STRING_INTERFACE].id == 0) {
+ rc = usb_string_id(cdev);
+ if (unlikely(rc < 0))
+ goto error_release;
+ fsg_strings[FSG_STRING_INTERFACE].id = rc;
+ fsg_intf_desc.iInterface = rc;
+ }
+
+ /*
+ * Create the LUNs, open their backing files, and register the
+ * LUN devices in sysfs.
+ */
+ curlun = kcalloc(nluns, sizeof(*curlun), GFP_KERNEL);
+ if (unlikely(!curlun)) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ common->luns = curlun;
+
+ init_rwsem(&common->filesem);
+
+ for (i = 0, lcfg = cfg->luns; i < nluns; ++i, ++curlun, ++lcfg) {
+ curlun->cdrom = !!lcfg->cdrom;
+ curlun->ro = lcfg->cdrom || lcfg->ro;
+ curlun->initially_ro = curlun->ro;
+ curlun->removable = lcfg->removable;
+ curlun->dev.release = fsg_lun_release;
+ curlun->dev.parent = &gadget->dev;
+ /* curlun->dev.driver = &fsg_driver.driver; XXX */
+ dev_set_drvdata(&curlun->dev, &common->filesem);
+ dev_set_name(&curlun->dev, "lun%d", i);
+
+ rc = device_register(&curlun->dev);
+ if (rc) {
+ INFO(common, "failed to register LUN%d: %d\n", i, rc);
+ common->nluns = i;
+ put_device(&curlun->dev);
+ goto error_release;
+ }
+
+ rc = device_create_file(&curlun->dev,
+ curlun->cdrom
+ ? &dev_attr_ro_cdrom
+ : &dev_attr_ro);
+ if (rc)
+ goto error_luns;
+ rc = device_create_file(&curlun->dev,
+ curlun->removable
+ ? &dev_attr_file
+ : &dev_attr_file_nonremovable);
+ if (rc)
+ goto error_luns;
+ rc = device_create_file(&curlun->dev, &dev_attr_nofua);
+ if (rc)
+ goto error_luns;
+
+ if (lcfg->filename) {
+ rc = fsg_lun_open(curlun, lcfg->filename);
+ if (rc)
+ goto error_luns;
+ } else if (!curlun->removable) {
+ ERROR(common, "no file given for LUN%d\n", i);
+ rc = -EINVAL;
+ goto error_luns;
+ }
+ }
+ common->nluns = nluns;
+
+ /* Data buffers cyclic list */
+ bh = common->buffhds;
+ i = fsg_num_buffers;
+ goto buffhds_first_it;
+ do {
+ bh->next = bh + 1;
+ ++bh;
+buffhds_first_it:
+ bh->buf = kmalloc(FSG_BUFLEN, GFP_KERNEL);
+ if (unlikely(!bh->buf)) {
+ rc = -ENOMEM;
+ goto error_release;
+ }
+ } while (--i);
+ bh->next = common->buffhds;
+
+ /* Prepare inquiryString */
+ if (cfg->release != 0xffff) {
+ i = cfg->release;
+ } else {
+ i = usb_gadget_controller_number(gadget);
+ if (i >= 0) {
+ i = 0x0300 + i;
+ } else {
+ WARNING(common, "controller '%s' not recognized\n",
+ gadget->name);
+ i = 0x0399;
+ }
+ }
+ snprintf(common->inquiry_string, sizeof common->inquiry_string,
+ "%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
+ /* Assume product name dependent on the first LUN */
+ cfg->product_name ?: (common->luns->cdrom
+ ? "File-Stor Gadget"
+ : "File-CD Gadget"),
+ i);
+
+ /*
+ * Some peripheral controllers are known not to be able to
+ * halt bulk endpoints correctly. If one of them is present,
+ * disable stalls.
+ */
+ common->can_stall = cfg->can_stall &&
+ !(gadget_is_at91(common->gadget));
+
+ spin_lock_init(&common->lock);
+ kref_init(&common->ref);
+
+ /* Tell the thread to start working */
+ common->thread_task =
+ kthread_create(fsg_main_thread, common, "file-storage");
+ if (IS_ERR(common->thread_task)) {
+ rc = PTR_ERR(common->thread_task);
+ goto error_release;
+ }
+ init_completion(&common->thread_notifier);
+ init_waitqueue_head(&common->fsg_wait);
+
+ /* Information */
+ INFO(common, FSG_DRIVER_DESC ", version: " FSG_DRIVER_VERSION "\n");
+ INFO(common, "Number of LUNs=%d\n", common->nluns);
+
+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL);
+ for (i = 0, nluns = common->nluns, curlun = common->luns;
+ i < nluns;
+ ++curlun, ++i) {
+ char *p = "(no medium)";
+ if (fsg_lun_is_open(curlun)) {
+ p = "(error)";
+ if (pathbuf) {
+ p = d_path(&curlun->filp->f_path,
+ pathbuf, PATH_MAX);
+ if (IS_ERR(p))
+ p = "(error)";
+ }
+ }
+ LINFO(curlun, "LUN: %s%s%sfile: %s\n",
+ curlun->removable ? "removable " : "",
+ curlun->ro ? "read only " : "",
+ curlun->cdrom ? "CD-ROM " : "",
+ p);
+ }
+ kfree(pathbuf);
+
+ DBG(common, "I/O thread pid: %d\n", task_pid_nr(common->thread_task));
+
+ wake_up_process(common->thread_task);
+
+ return common;
+
+error_luns:
+ common->nluns = i + 1;
+error_release:
+ common->state = FSG_STATE_TERMINATED; /* The thread is dead */
+ /* Call fsg_common_release() directly, ref might be not initialised. */
+ fsg_common_release(&common->ref);
+ return ERR_PTR(rc);
+}
+
+static void fsg_common_release(struct kref *ref)
+{
+ struct fsg_common *common = container_of(ref, struct fsg_common, ref);
+
+ /* If the thread isn't already dead, tell it to exit now */
+ if (common->state != FSG_STATE_TERMINATED) {
+ raise_exception(common, FSG_STATE_EXIT);
+ wait_for_completion(&common->thread_notifier);
+ }
+
+ if (likely(common->luns)) {
+ struct fsg_lun *lun = common->luns;
+ unsigned i = common->nluns;
+
+ /* In error recovery common->nluns may be zero. */
+ for (; i; --i, ++lun) {
+ device_remove_file(&lun->dev, &dev_attr_nofua);
+ device_remove_file(&lun->dev,
+ lun->cdrom
+ ? &dev_attr_ro_cdrom
+ : &dev_attr_ro);
+ device_remove_file(&lun->dev,
+ lun->removable
+ ? &dev_attr_file
+ : &dev_attr_file_nonremovable);
+ fsg_lun_close(lun);
+ device_unregister(&lun->dev);
+ }
+
+ kfree(common->luns);
+ }
+
+ {
+ struct fsg_buffhd *bh = common->buffhds;
+ unsigned i = fsg_num_buffers;
+ do {
+ kfree(bh->buf);
+ } while (++bh, --i);
+ }
+
+ kfree(common->buffhds);
+ if (common->free_storage_on_release)
+ kfree(common);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static void fsg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct fsg_common *common = fsg->common;
+
+ DBG(fsg, "unbind\n");
+ if (fsg->common->fsg == fsg) {
+ fsg->common->new_fsg = NULL;
+ raise_exception(fsg->common, FSG_STATE_CONFIG_CHANGE);
+ /* FIXME: make interruptible or killable somehow? */
+ wait_event(common->fsg_wait, common->fsg != fsg);
+ }
+
+ fsg_common_put(common);
+ usb_free_descriptors(fsg->function.descriptors);
+ usb_free_descriptors(fsg->function.hs_descriptors);
+ usb_free_descriptors(fsg->function.ss_descriptors);
+ kfree(fsg);
+}
+
+static int fsg_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct fsg_dev *fsg = fsg_from_func(f);
+ struct usb_gadget *gadget = c->cdev->gadget;
+ int i;
+ struct usb_ep *ep;
+
+ fsg->gadget = gadget;
+
+ /* New interface */
+ i = usb_interface_id(c, f);
+ if (i < 0)
+ return i;
+ fsg_intf_desc.bInterfaceNumber = i;
+ fsg->interface_number = i;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_in_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_in = ep;
+
+ ep = usb_ep_autoconfig(gadget, &fsg_fs_bulk_out_desc);
+ if (!ep)
+ goto autoconf_fail;
+ ep->driver_data = fsg->common; /* claim the endpoint */
+ fsg->bulk_out = ep;
+
+ /* Copy descriptors */
+ f->descriptors = usb_copy_descriptors(fsg_fs_function);
+ if (unlikely(!f->descriptors))
+ return -ENOMEM;
+
+ if (gadget_is_dualspeed(gadget)) {
+ /* Assume endpoint addresses are the same for both speeds */
+ fsg_hs_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_hs_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ f->hs_descriptors = usb_copy_descriptors(fsg_hs_function);
+ if (unlikely(!f->hs_descriptors)) {
+ usb_free_descriptors(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+
+ if (gadget_is_superspeed(gadget)) {
+ unsigned max_burst;
+
+ /* Calculate bMaxBurst, we know packet size is 1024 */
+ max_burst = min_t(unsigned, FSG_BUFLEN / 1024, 15);
+
+ fsg_ss_bulk_in_desc.bEndpointAddress =
+ fsg_fs_bulk_in_desc.bEndpointAddress;
+ fsg_ss_bulk_in_comp_desc.bMaxBurst = max_burst;
+
+ fsg_ss_bulk_out_desc.bEndpointAddress =
+ fsg_fs_bulk_out_desc.bEndpointAddress;
+ fsg_ss_bulk_out_comp_desc.bMaxBurst = max_burst;
+
+ f->ss_descriptors = usb_copy_descriptors(fsg_ss_function);
+ if (unlikely(!f->ss_descriptors)) {
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+
+autoconf_fail:
+ ERROR(fsg, "unable to autoconfigure all endpoints\n");
+ return -ENOTSUPP;
+}
+
+
+/****************************** ADD FUNCTION ******************************/
+
+static struct usb_gadget_strings *fsg_strings_array[] = {
+ &fsg_stringtab,
+ NULL,
+};
+
+static int fsg_bind_config(struct usb_composite_dev *cdev,
+ struct usb_configuration *c,
+ struct fsg_common *common)
+{
+ struct fsg_dev *fsg;
+ int rc;
+
+ fsg = kzalloc(sizeof *fsg, GFP_KERNEL);
+ if (unlikely(!fsg))
+ return -ENOMEM;
+
+ fsg->function.name = FSG_DRIVER_DESC;
+ fsg->function.strings = fsg_strings_array;
+ fsg->function.bind = fsg_bind;
+ fsg->function.unbind = fsg_unbind;
+ fsg->function.setup = fsg_setup;
+ fsg->function.set_alt = fsg_set_alt;
+ fsg->function.disable = fsg_disable;
+
+ fsg->common = common;
+ /*
+ * Our caller holds a reference to common structure so we
+ * don't have to be worry about it being freed until we return
+ * from this function. So instead of incrementing counter now
+ * and decrement in error recovery we increment it only when
+ * call to usb_add_function() was successful.
+ */
+
+ rc = usb_add_function(c, &fsg->function);
+ if (unlikely(rc))
+ kfree(fsg);
+ else
+ fsg_common_get(fsg->common);
+ return rc;
+}
+
+
+/************************* Module parameters *************************/
+
+struct fsg_module_parameters {
+ char *file[FSG_MAX_LUNS];
+ bool ro[FSG_MAX_LUNS];
+ bool removable[FSG_MAX_LUNS];
+ bool cdrom[FSG_MAX_LUNS];
+ bool nofua[FSG_MAX_LUNS];
+
+ unsigned int file_count, ro_count, removable_count, cdrom_count;
+ unsigned int nofua_count;
+ unsigned int luns; /* nluns */
+ bool stall; /* can_stall */
+};
+
+#define _FSG_MODULE_PARAM_ARRAY(prefix, params, name, type, desc) \
+ module_param_array_named(prefix ## name, params.name, type, \
+ &prefix ## params.name ## _count, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define _FSG_MODULE_PARAM(prefix, params, name, type, desc) \
+ module_param_named(prefix ## name, params.name, type, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(prefix ## name, desc)
+
+#define FSG_MODULE_PARAMETERS(prefix, params) \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, file, charp, \
+ "names of backing files or devices"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, ro, bool, \
+ "true to force read-only"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, removable, bool, \
+ "true to simulate removable media"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, cdrom, bool, \
+ "true to simulate CD-ROM instead of disk"); \
+ _FSG_MODULE_PARAM_ARRAY(prefix, params, nofua, bool, \
+ "true to ignore SCSI WRITE(10,12) FUA bit"); \
+ _FSG_MODULE_PARAM(prefix, params, luns, uint, \
+ "number of LUNs"); \
+ _FSG_MODULE_PARAM(prefix, params, stall, bool, \
+ "false to prevent bulk stalls")
+
+static void
+fsg_config_from_params(struct fsg_config *cfg,
+ const struct fsg_module_parameters *params)
+{
+ struct fsg_lun_config *lun;
+ unsigned i;
+
+ /* Configure LUNs */
+ cfg->nluns =
+ min(params->luns ?: (params->file_count ?: 1u),
+ (unsigned)FSG_MAX_LUNS);
+ for (i = 0, lun = cfg->luns; i < cfg->nluns; ++i, ++lun) {
+ lun->ro = !!params->ro[i];
+ lun->cdrom = !!params->cdrom[i];
+ lun->removable = !!params->removable[i];
+ lun->filename =
+ params->file_count > i && params->file[i][0]
+ ? params->file[i]
+ : 0;
+ }
+
+ /* Let MSF use defaults */
+ cfg->vendor_name = 0;
+ cfg->product_name = 0;
+ cfg->release = 0xffff;
+
+ cfg->ops = NULL;
+ cfg->private_data = NULL;
+
+ /* Finalise */
+ cfg->can_stall = params->stall;
+}
+
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+ __attribute__((unused));
+static inline struct fsg_common *
+fsg_common_from_params(struct fsg_common *common,
+ struct usb_composite_dev *cdev,
+ const struct fsg_module_parameters *params)
+{
+ struct fsg_config cfg;
+ fsg_config_from_params(&cfg, params);
+ return fsg_common_init(common, cdev, &cfg);
+}
diff --git a/drivers/staging/ccg/f_rndis.c b/drivers/staging/ccg/f_rndis.c
new file mode 100644
index 0000000..b1681e4
--- /dev/null
+++ b/drivers/staging/ccg/f_rndis.c
@@ -0,0 +1,918 @@
+/*
+ * f_rndis.c -- RNDIS link function driver
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.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.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/etherdevice.h>
+
+#include <linux/atomic.h>
+
+#include "u_ether.h"
+#include "rndis.h"
+
+
+/*
+ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
+ * been promoted instead of the standard CDC Ethernet. The published RNDIS
+ * spec is ambiguous, incomplete, and needlessly complex. Variants such as
+ * ActiveSync have even worse status in terms of specification.
+ *
+ * In short: it's a protocol controlled by (and for) Microsoft, not for an
+ * Open ecosystem or markets. Linux supports it *only* because Microsoft
+ * doesn't support the CDC Ethernet standard.
+ *
+ * The RNDIS data transfer model is complex, with multiple Ethernet packets
+ * per USB message, and out of band data. The control model is built around
+ * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
+ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
+ * useless (they're ignored). RNDIS expects to be the only function in its
+ * configuration, so it's no real help if you need composite devices; and
+ * it expects to be the first configuration too.
+ *
+ * There is a single technical advantage of RNDIS over CDC Ethernet, if you
+ * discount the fluff that its RPC can be made to deliver: it doesn't need
+ * a NOP altsetting for the data interface. That lets it work on some of the
+ * "so smart it's stupid" hardware which takes over configuration changes
+ * from the software, and adds restrictions like "no altsettings".
+ *
+ * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
+ * have all sorts of contrary-to-specification oddities that can prevent
+ * them from working sanely. Since bugfixes (or accurate specs, letting
+ * Linux work around those bugs) are unlikely to ever come from MSFT, you
+ * may want to avoid using RNDIS on purely operational grounds.
+ *
+ * Omissions from the RNDIS 1.0 specification include:
+ *
+ * - Power management ... references data that's scattered around lots
+ * of other documentation, which is incorrect/incomplete there too.
+ *
+ * - There are various undocumented protocol requirements, like the need
+ * to send garbage in some control-OUT messages.
+ *
+ * - MS-Windows drivers sometimes emit undocumented requests.
+ */
+
+struct f_rndis {
+ struct gether port;
+ u8 ctrl_id, data_id;
+ u8 ethaddr[ETH_ALEN];
+ u32 vendorID;
+ const char *manufacturer;
+ int config;
+
+ struct usb_ep *notify;
+ struct usb_request *notify_req;
+ atomic_t notify_count;
+};
+
+static inline struct f_rndis *func_to_rndis(struct usb_function *f)
+{
+ return container_of(f, struct f_rndis, port.func);
+}
+
+/* peak (theoretical) bulk transfer rate in bits-per-second */
+static unsigned int bitrate(struct usb_gadget *g)
+{
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return 13 * 1024 * 8 * 1000 * 8;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return 13 * 512 * 8 * 1000 * 8;
+ else
+ return 19 * 64 * 1 * 1000 * 8;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ */
+
+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+#define STATUS_BYTECOUNT 8 /* 8 bytes data */
+
+
+/* interface descriptor: */
+
+static struct usb_interface_descriptor rndis_control_intf = {
+ .bLength = sizeof rndis_control_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC */
+ /* status endpoint is optional; this could be patched later */
+ .bNumEndpoints = 1,
+ .bInterfaceClass = USB_CLASS_COMM,
+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+ /* .iInterface = DYNAMIC */
+};
+
+static struct usb_cdc_header_desc header_desc = {
+ .bLength = sizeof header_desc,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+
+ .bcdCDC = cpu_to_le16(0x0110),
+};
+
+static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+ .bLength = sizeof call_mgmt_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+
+ .bmCapabilities = 0x00,
+ .bDataInterface = 0x01,
+};
+
+static struct usb_cdc_acm_descriptor rndis_acm_descriptor = {
+ .bLength = sizeof rndis_acm_descriptor,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_ACM_TYPE,
+
+ .bmCapabilities = 0x00,
+};
+
+static struct usb_cdc_union_desc rndis_union_desc = {
+ .bLength = sizeof(rndis_union_desc),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubType = USB_CDC_UNION_TYPE,
+ /* .bMasterInterface0 = DYNAMIC */
+ /* .bSlaveInterface0 = DYNAMIC */
+};
+
+/* the data interface has two bulk endpoints */
+
+static struct usb_interface_descriptor rndis_data_intf = {
+ .bLength = sizeof rndis_data_intf,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_CDC_DATA,
+ .bInterfaceSubClass = 0,
+ .bInterfaceProtocol = 0,
+ /* .iInterface = DYNAMIC */
+};
+
+
+static struct usb_interface_assoc_descriptor
+rndis_iad_descriptor = {
+ .bLength = sizeof rndis_iad_descriptor,
+ .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION,
+
+ .bFirstInterface = 0, /* XXX, hardcoded */
+ .bInterfaceCount = 2, // control + data
+ .bFunctionClass = USB_CLASS_COMM,
+ .bFunctionSubClass = USB_CDC_SUBCLASS_ETHERNET,
+ .bFunctionProtocol = USB_CDC_PROTO_NONE,
+ /* .iFunction = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor fs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+};
+
+static struct usb_endpoint_descriptor fs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *eth_fs_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &fs_notify_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &fs_in_desc,
+ (struct usb_descriptor_header *) &fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor hs_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_endpoint_descriptor hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *eth_hs_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &hs_notify_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &hs_in_desc,
+ (struct usb_descriptor_header *) &hs_out_desc,
+ NULL,
+};
+
+/* super speed support: */
+
+static struct usb_endpoint_descriptor ss_notify_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(STATUS_BYTECOUNT),
+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+};
+
+static struct usb_ss_ep_comp_descriptor ss_intr_comp_desc = {
+ .bLength = sizeof ss_intr_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 3 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+ .wBytesPerInterval = cpu_to_le16(STATUS_BYTECOUNT),
+};
+
+static struct usb_endpoint_descriptor ss_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_endpoint_descriptor ss_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_bulk_comp_desc = {
+ .bLength = sizeof ss_bulk_comp_desc,
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /* the following 2 values can be tweaked if necessary */
+ /* .bMaxBurst = 0, */
+ /* .bmAttributes = 0, */
+};
+
+static struct usb_descriptor_header *eth_ss_function[] = {
+ (struct usb_descriptor_header *) &rndis_iad_descriptor,
+
+ /* control interface matches ACM, not Ethernet */
+ (struct usb_descriptor_header *) &rndis_control_intf,
+ (struct usb_descriptor_header *) &header_desc,
+ (struct usb_descriptor_header *) &call_mgmt_descriptor,
+ (struct usb_descriptor_header *) &rndis_acm_descriptor,
+ (struct usb_descriptor_header *) &rndis_union_desc,
+ (struct usb_descriptor_header *) &ss_notify_desc,
+ (struct usb_descriptor_header *) &ss_intr_comp_desc,
+
+ /* data interface has no altsetting */
+ (struct usb_descriptor_header *) &rndis_data_intf,
+ (struct usb_descriptor_header *) &ss_in_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ (struct usb_descriptor_header *) &ss_out_desc,
+ (struct usb_descriptor_header *) &ss_bulk_comp_desc,
+ NULL,
+};
+
+/* string descriptors: */
+
+static struct usb_string rndis_string_defs[] = {
+ [0].s = "RNDIS Communications Control",
+ [1].s = "RNDIS Ethernet Data",
+ [2].s = "RNDIS",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings rndis_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = rndis_string_defs,
+};
+
+static struct usb_gadget_strings *rndis_strings[] = {
+ &rndis_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct sk_buff *rndis_add_header(struct gether *port,
+ struct sk_buff *skb)
+{
+ struct sk_buff *skb2;
+
+ skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
+ if (skb2)
+ rndis_add_hdr(skb2);
+
+ dev_kfree_skb_any(skb);
+ return skb2;
+}
+
+static void rndis_response_available(void *_rndis)
+{
+ struct f_rndis *rndis = _rndis;
+ struct usb_request *req = rndis->notify_req;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ __le32 *data = req->buf;
+ int status;
+
+ if (atomic_inc_return(&rndis->notify_count) != 1)
+ return;
+
+ /* Send RNDIS RESPONSE_AVAILABLE notification; a
+ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
+ *
+ * This is the only notification defined by RNDIS.
+ */
+ data[0] = cpu_to_le32(1);
+ data[1] = cpu_to_le32(0);
+
+ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+ if (status) {
+ atomic_dec(&rndis->notify_count);
+ DBG(cdev, "notify/0 --> %d\n", status);
+ }
+}
+
+static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rndis *rndis = req->context;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ int status = req->status;
+
+ /* after TX:
+ * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
+ * - RNDIS_RESPONSE_AVAILABLE (status/irq)
+ */
+ switch (status) {
+ case -ECONNRESET:
+ case -ESHUTDOWN:
+ /* connection gone */
+ atomic_set(&rndis->notify_count, 0);
+ break;
+ default:
+ DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
+ ep->name, status,
+ req->actual, req->length);
+ /* FALLTHROUGH */
+ case 0:
+ if (ep != rndis->notify)
+ break;
+
+ /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
+ * notifications by resending until we're done
+ */
+ if (atomic_dec_and_test(&rndis->notify_count))
+ break;
+ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
+ if (status) {
+ atomic_dec(&rndis->notify_count);
+ DBG(cdev, "notify/1 --> %d\n", status);
+ }
+ break;
+ }
+}
+
+static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct f_rndis *rndis = req->context;
+ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
+ int status;
+
+ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+// spin_lock(&dev->lock);
+ status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
+ if (status < 0)
+ ERROR(cdev, "RNDIS command error %d, %d/%d\n",
+ status, req->actual, req->length);
+// spin_unlock(&dev->lock);
+}
+
+static int
+rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct usb_request *req = cdev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* composite driver infrastructure handles everything except
+ * CDC class messages; interface activation uses set_alt().
+ */
+ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
+
+ /* RNDIS uses the CDC command encapsulation mechanism to implement
+ * an RPC scheme, with much getting/setting of attributes by OID.
+ */
+ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_SEND_ENCAPSULATED_COMMAND:
+ if (w_value || w_index != rndis->ctrl_id)
+ goto invalid;
+ /* read the request; process it later */
+ value = w_length;
+ req->complete = rndis_command_complete;
+ req->context = rndis;
+ /* later, rndis_response_available() sends a notification */
+ break;
+
+ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+ | USB_CDC_GET_ENCAPSULATED_RESPONSE:
+ if (w_value || w_index != rndis->ctrl_id)
+ goto invalid;
+ else {
+ u8 *buf;
+ u32 n;
+
+ /* return the result */
+ buf = rndis_get_next_response(rndis->config, &n);
+ if (buf) {
+ memcpy(req->buf, buf, n);
+ req->complete = rndis_response_complete;
+ req->context = rndis;
+ rndis_free_response(rndis->config, buf);
+ value = n;
+ }
+ /* else stalls ... spec says to avoid that */
+ }
+ break;
+
+ default:
+invalid:
+ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer or status phase? */
+ if (value >= 0) {
+ DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ req->zero = (value < w_length);
+ req->length = value;
+ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0)
+ ERROR(cdev, "rndis response on err %d\n", value);
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+
+static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ /* we know alt == 0 */
+
+ if (intf == rndis->ctrl_id) {
+ if (rndis->notify->driver_data) {
+ VDBG(cdev, "reset rndis control %d\n", intf);
+ usb_ep_disable(rndis->notify);
+ }
+ if (!rndis->notify->desc) {
+ VDBG(cdev, "init rndis ctrl %d\n", intf);
+ if (config_ep_by_speed(cdev->gadget, f, rndis->notify))
+ goto fail;
+ }
+ usb_ep_enable(rndis->notify);
+ rndis->notify->driver_data = rndis;
+
+ } else if (intf == rndis->data_id) {
+ struct net_device *net;
+
+ if (rndis->port.in_ep->driver_data) {
+ DBG(cdev, "reset rndis\n");
+ gether_disconnect(&rndis->port);
+ }
+
+ if (!rndis->port.in_ep->desc || !rndis->port.out_ep->desc) {
+ DBG(cdev, "init rndis\n");
+ if (config_ep_by_speed(cdev->gadget, f,
+ rndis->port.in_ep) ||
+ config_ep_by_speed(cdev->gadget, f,
+ rndis->port.out_ep)) {
+ rndis->port.in_ep->desc = NULL;
+ rndis->port.out_ep->desc = NULL;
+ goto fail;
+ }
+ }
+
+ /* Avoid ZLPs; they can be troublesome. */
+ rndis->port.is_zlp_ok = false;
+
+ /* RNDIS should be in the "RNDIS uninitialized" state,
+ * either never activated or after rndis_uninit().
+ *
+ * We don't want data to flow here until a nonzero packet
+ * filter is set, at which point it enters "RNDIS data
+ * initialized" state ... but we do want the endpoints
+ * to be activated. It's a strange little state.
+ *
+ * REVISIT the RNDIS gadget code has done this wrong for a
+ * very long time. We need another call to the link layer
+ * code -- gether_updown(...bool) maybe -- to do it right.
+ */
+ rndis->port.cdc_filter = 0;
+
+ DBG(cdev, "RNDIS RX/TX early activation ... \n");
+ net = gether_connect(&rndis->port);
+ if (IS_ERR(net))
+ return PTR_ERR(net);
+
+ rndis_set_param_dev(rndis->config, net,
+ &rndis->port.cdc_filter);
+ } else
+ goto fail;
+
+ return 0;
+fail:
+ return -EINVAL;
+}
+
+static void rndis_disable(struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ if (!rndis->notify->driver_data)
+ return;
+
+ DBG(cdev, "rndis deactivated\n");
+
+ rndis_uninit(rndis->config);
+ gether_disconnect(&rndis->port);
+
+ usb_ep_disable(rndis->notify);
+ rndis->notify->driver_data = NULL;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * This isn't quite the same mechanism as CDC Ethernet, since the
+ * notification scheme passes less data, but the same set of link
+ * states must be tested. A key difference is that altsettings are
+ * not used to tell whether the link should send packets or not.
+ */
+
+static void rndis_open(struct gether *geth)
+{
+ struct f_rndis *rndis = func_to_rndis(&geth->func);
+ struct usb_composite_dev *cdev = geth->func.config->cdev;
+
+ DBG(cdev, "%s\n", __func__);
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3,
+ bitrate(cdev->gadget) / 100);
+ rndis_signal_connect(rndis->config);
+}
+
+static void rndis_close(struct gether *geth)
+{
+ struct f_rndis *rndis = func_to_rndis(&geth->func);
+
+ DBG(geth->func.config->cdev, "%s\n", __func__);
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+ rndis_signal_disconnect(rndis->config);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* ethernet function driver setup/binding */
+
+static int
+rndis_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_rndis *rndis = func_to_rndis(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ rndis->ctrl_id = status;
+ rndis_iad_descriptor.bFirstInterface = status;
+
+ rndis_control_intf.bInterfaceNumber = status;
+ rndis_union_desc.bMasterInterface0 = status;
+
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ rndis->data_id = status;
+
+ rndis_data_intf.bInterfaceNumber = status;
+ rndis_union_desc.bSlaveInterface0 = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
+ if (!ep)
+ goto fail;
+ rndis->port.in_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
+ if (!ep)
+ goto fail;
+ rndis->port.out_ep = ep;
+ ep->driver_data = cdev; /* claim */
+
+ /* NOTE: a status/notification endpoint is, strictly speaking,
+ * optional. We don't treat it that way though! It's simpler,
+ * and some newer profiles don't treat it as optional.
+ */
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
+ if (!ep)
+ goto fail;
+ rndis->notify = ep;
+ ep->driver_data = cdev; /* claim */
+
+ status = -ENOMEM;
+
+ /* allocate notification request and buffer */
+ rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
+ if (!rndis->notify_req)
+ goto fail;
+ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
+ if (!rndis->notify_req->buf)
+ goto fail;
+ rndis->notify_req->length = STATUS_BYTECOUNT;
+ rndis->notify_req->context = rndis;
+ rndis->notify_req->complete = rndis_response_complete;
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(eth_fs_function);
+ if (!f->descriptors)
+ goto fail;
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ hs_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ hs_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+ hs_notify_desc.bEndpointAddress =
+ fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
+ if (!f->hs_descriptors)
+ goto fail;
+ }
+
+ if (gadget_is_superspeed(c->cdev->gadget)) {
+ ss_in_desc.bEndpointAddress =
+ fs_in_desc.bEndpointAddress;
+ ss_out_desc.bEndpointAddress =
+ fs_out_desc.bEndpointAddress;
+ ss_notify_desc.bEndpointAddress =
+ fs_notify_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->ss_descriptors = usb_copy_descriptors(eth_ss_function);
+ if (!f->ss_descriptors)
+ goto fail;
+ }
+
+ rndis->port.open = rndis_open;
+ rndis->port.close = rndis_close;
+
+ status = rndis_register(rndis_response_available, rndis);
+ if (status < 0)
+ goto fail;
+ rndis->config = status;
+
+ rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
+ rndis_set_host_mac(rndis->config, rndis->ethaddr);
+
+ if (rndis->manufacturer && rndis->vendorID &&
+ rndis_set_param_vendor(rndis->config, rndis->vendorID,
+ rndis->manufacturer))
+ goto fail;
+
+ /* NOTE: all that is done without knowing or caring about
+ * the network link ... which is unavailable to this code
+ * until we're activated via set_alt().
+ */
+
+ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
+ gadget_is_superspeed(c->cdev->gadget) ? "super" :
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ rndis->port.in_ep->name, rndis->port.out_ep->name,
+ rndis->notify->name);
+ return 0;
+
+fail:
+ if (gadget_is_superspeed(c->cdev->gadget) && f->ss_descriptors)
+ usb_free_descriptors(f->ss_descriptors);
+ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
+ usb_free_descriptors(f->hs_descriptors);
+ if (f->descriptors)
+ usb_free_descriptors(f->descriptors);
+
+ if (rndis->notify_req) {
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+ }
+
+ /* we might as well release our claims on endpoints */
+ if (rndis->notify)
+ rndis->notify->driver_data = NULL;
+ if (rndis->port.out_ep->desc)
+ rndis->port.out_ep->driver_data = NULL;
+ if (rndis->port.in_ep->desc)
+ rndis->port.in_ep->driver_data = NULL;
+
+ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+static void
+rndis_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_rndis *rndis = func_to_rndis(f);
+
+ rndis_deregister(rndis->config);
+ rndis_exit();
+ rndis_string_defs[0].id = 0;
+
+ if (gadget_is_superspeed(c->cdev->gadget))
+ usb_free_descriptors(f->ss_descriptors);
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+
+ kfree(rndis->notify_req->buf);
+ usb_ep_free_request(rndis->notify, rndis->notify_req);
+
+ kfree(rndis);
+}
+
+/* Some controllers can't support RNDIS ... */
+static inline bool can_support_rndis(struct usb_configuration *c)
+{
+ /* everything else is *presumably* fine */
+ return true;
+}
+
+int
+rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer)
+{
+ struct f_rndis *rndis;
+ int status;
+
+ if (!can_support_rndis(c) || !ethaddr)
+ return -EINVAL;
+
+ /* maybe allocate device-global string IDs */
+ if (rndis_string_defs[0].id == 0) {
+
+ /* ... and setup RNDIS itself */
+ status = rndis_init();
+ if (status < 0)
+ return status;
+
+ /* control interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[0].id = status;
+ rndis_control_intf.iInterface = status;
+
+ /* data interface label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[1].id = status;
+ rndis_data_intf.iInterface = status;
+
+ /* IAD iFunction label */
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ rndis_string_defs[2].id = status;
+ rndis_iad_descriptor.iFunction = status;
+ }
+
+ /* allocate and initialize one new instance */
+ status = -ENOMEM;
+ rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
+ if (!rndis)
+ goto fail;
+
+ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
+ rndis->vendorID = vendorID;
+ rndis->manufacturer = manufacturer;
+
+ /* RNDIS activates when the host changes this filter */
+ rndis->port.cdc_filter = 0;
+
+ /* RNDIS has special (and complex) framing */
+ rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
+ rndis->port.wrap = rndis_add_header;
+ rndis->port.unwrap = rndis_rm_hdr;
+
+ rndis->port.func.name = "rndis";
+ rndis->port.func.strings = rndis_strings;
+ /* descriptors are per-instance copies */
+ rndis->port.func.bind = rndis_bind;
+ rndis->port.func.unbind = rndis_unbind;
+ rndis->port.func.set_alt = rndis_set_alt;
+ rndis->port.func.setup = rndis_setup;
+ rndis->port.func.disable = rndis_disable;
+
+ status = usb_add_function(c, &rndis->port.func);
+ if (status) {
+ kfree(rndis);
+fail:
+ rndis_exit();
+ }
+ return status;
+}
diff --git a/drivers/staging/ccg/gadget_chips.h b/drivers/staging/ccg/gadget_chips.h
new file mode 100644
index 0000000..0ccca58
--- /dev/null
+++ b/drivers/staging/ccg/gadget_chips.h
@@ -0,0 +1,150 @@
+/*
+ * USB device controllers have lots of quirks. Use these macros in
+ * gadget drivers or other code that needs to deal with them, and which
+ * autoconfigures instead of using early binding to the hardware.
+ *
+ * This SHOULD eventually work like the ARM mach_is_*() stuff, driven by
+ * some config file that gets updated as new hardware is supported.
+ * (And avoiding all runtime comparisons in typical one-choice configs!)
+ *
+ * NOTE: some of these controller drivers may not be available yet.
+ * Some are available on 2.4 kernels; several are available, but not
+ * yet pushed in the 2.6 mainline tree.
+ */
+
+#ifndef __GADGET_CHIPS_H
+#define __GADGET_CHIPS_H
+
+/*
+ * NOTICE: the entries below are alphabetical and should be kept
+ * that way.
+ *
+ * Always be sure to add new entries to the correct position or
+ * accept the bashing later.
+ *
+ * If you have forgotten the alphabetical order let VIM/EMACS
+ * do that for you.
+ */
+#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
+#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
+#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
+#define gadget_is_bcm63xx(g) (!strcmp("bcm63xx_udc", (g)->name))
+#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
+#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
+#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
+#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
+#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
+#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
+#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
+#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
+#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
+#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
+#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
+#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
+#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
+#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
+#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
+#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
+#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
+#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
+#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
+#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
+#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
+#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+
+/**
+ * usb_gadget_controller_number - support bcdDevice id convention
+ * @gadget: the controller being driven
+ *
+ * Return a 2-digit BCD value associated with the peripheral controller,
+ * suitable for use as part of a bcdDevice value, or a negative error code.
+ *
+ * NOTE: this convention is purely optional, and has no meaning in terms of
+ * any USB specification. If you want to use a different convention in your
+ * gadget driver firmware -- maybe a more formal revision ID -- feel free.
+ *
+ * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
+ * to change their behavior accordingly. For example it might help avoiding
+ * some chip bug.
+ */
+static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
+{
+ if (gadget_is_net2280(gadget))
+ return 0x01;
+ else if (gadget_is_dummy(gadget))
+ return 0x02;
+ else if (gadget_is_pxa(gadget))
+ return 0x03;
+ else if (gadget_is_goku(gadget))
+ return 0x06;
+ else if (gadget_is_omap(gadget))
+ return 0x08;
+ else if (gadget_is_pxa27x(gadget))
+ return 0x11;
+ else if (gadget_is_s3c2410(gadget))
+ return 0x12;
+ else if (gadget_is_at91(gadget))
+ return 0x13;
+ else if (gadget_is_imx(gadget))
+ return 0x14;
+ else if (gadget_is_musbhdrc(gadget))
+ return 0x16;
+ else if (gadget_is_atmel_usba(gadget))
+ return 0x18;
+ else if (gadget_is_fsl_usb2(gadget))
+ return 0x19;
+ else if (gadget_is_amd5536udc(gadget))
+ return 0x20;
+ else if (gadget_is_m66592(gadget))
+ return 0x21;
+ else if (gadget_is_fsl_qe(gadget))
+ return 0x22;
+ else if (gadget_is_ci13xxx_pci(gadget))
+ return 0x23;
+ else if (gadget_is_langwell(gadget))
+ return 0x24;
+ else if (gadget_is_r8a66597(gadget))
+ return 0x25;
+ else if (gadget_is_s3c_hsotg(gadget))
+ return 0x26;
+ else if (gadget_is_pch(gadget))
+ return 0x27;
+ else if (gadget_is_ci13xxx_msm(gadget))
+ return 0x28;
+ else if (gadget_is_renesas_usbhs(gadget))
+ return 0x29;
+ else if (gadget_is_s3c_hsudc(gadget))
+ return 0x30;
+ else if (gadget_is_net2272(gadget))
+ return 0x31;
+ else if (gadget_is_dwc3(gadget))
+ return 0x32;
+ else if (gadget_is_lpc32xx(gadget))
+ return 0x33;
+ else if (gadget_is_bcm63xx(gadget))
+ return 0x34;
+
+ return -ENOENT;
+}
+
+
+/**
+ * gadget_supports_altsettings - return true if altsettings work
+ * @gadget: the gadget in question
+ */
+static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
+{
+ /* PXA 21x/25x/26x has no altsettings at all */
+ if (gadget_is_pxa(gadget))
+ return false;
+
+ /* PXA 27x and 3xx have *broken* altsetting support */
+ if (gadget_is_pxa27x(gadget))
+ return false;
+
+ /* Everything else is *presumably* fine ... */
+ return true;
+}
+
+#endif /* __GADGET_CHIPS_H */
diff --git a/drivers/staging/ccg/ndis.h b/drivers/staging/ccg/ndis.h
new file mode 100644
index 0000000..a19f72d
--- /dev/null
+++ b/drivers/staging/ccg/ndis.h
@@ -0,0 +1,47 @@
+/*
+ * ndis.h
+ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+ *
+ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+ * use, modify or distribute it freely.
+ */
+
+#ifndef _LINUX_NDIS_H
+#define _LINUX_NDIS_H
+
+enum NDIS_DEVICE_POWER_STATE {
+ NdisDeviceStateUnspecified = 0,
+ NdisDeviceStateD0,
+ NdisDeviceStateD1,
+ NdisDeviceStateD2,
+ NdisDeviceStateD3,
+ NdisDeviceStateMaximum
+};
+
+struct NDIS_PM_WAKE_UP_CAPABILITIES {
+ enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
+ enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
+};
+
+struct NDIS_PNP_CAPABILITIES {
+ __le32 Flags;
+ struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
+};
+
+struct NDIS_PM_PACKET_PATTERN {
+ __le32 Priority;
+ __le32 Reserved;
+ __le32 MaskSize;
+ __le32 PatternOffset;
+ __le32 PatternSize;
+ __le32 PatternFlags;
+};
+
+#endif /* _LINUX_NDIS_H */
diff --git a/drivers/staging/ccg/rndis.c b/drivers/staging/ccg/rndis.c
new file mode 100644
index 0000000..e4192b8
--- /dev/null
+++ b/drivers/staging/ccg/rndis.c
@@ -0,0 +1,1175 @@
+/*
+ * RNDIS MSG parser
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * 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 software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ *
+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed message length bug in init_response
+ *
+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de>
+ * Fixed rndis_rm_hdr length bug.
+ *
+ * Copyright (C) 2004 by David Brownell
+ * updates to merge with Linux 2.6, better match RNDIS spec
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/seq_file.h>
+#include <linux/netdevice.h>
+
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/unaligned.h>
+
+
+#undef VERBOSE_DEBUG
+
+#include "rndis.h"
+
+
+/* The driver for your USB chip needs to support ep0 OUT to work with
+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional).
+ *
+ * Windows hosts need an INF file like Documentation/usb/linux.inf
+ * and will be happier if you provide the host_addr module parameter.
+ */
+
+#if 0
+static int rndis_debug = 0;
+module_param (rndis_debug, int, 0);
+MODULE_PARM_DESC (rndis_debug, "enable debugging");
+#else
+#define rndis_debug 0
+#endif
+
+#define RNDIS_MAX_CONFIGS 1
+
+
+static rndis_params rndis_per_dev_params[RNDIS_MAX_CONFIGS];
+
+/* Driver Version */
+static const __le32 rndis_driver_version = cpu_to_le32(1);
+
+/* Function Prototypes */
+static rndis_resp_t *rndis_add_response(int configNr, u32 length);
+
+
+/* supported OIDs */
+static const u32 oid_supported_list[] =
+{
+ /* the general stuff */
+ RNDIS_OID_GEN_SUPPORTED_LIST,
+ RNDIS_OID_GEN_HARDWARE_STATUS,
+ RNDIS_OID_GEN_MEDIA_SUPPORTED,
+ RNDIS_OID_GEN_MEDIA_IN_USE,
+ RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
+ RNDIS_OID_GEN_LINK_SPEED,
+ RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE,
+ RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE,
+ RNDIS_OID_GEN_VENDOR_ID,
+ RNDIS_OID_GEN_VENDOR_DESCRIPTION,
+ RNDIS_OID_GEN_VENDOR_DRIVER_VERSION,
+ RNDIS_OID_GEN_CURRENT_PACKET_FILTER,
+ RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE,
+ RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
+ RNDIS_OID_GEN_PHYSICAL_MEDIUM,
+
+ /* the statistical stuff */
+ RNDIS_OID_GEN_XMIT_OK,
+ RNDIS_OID_GEN_RCV_OK,
+ RNDIS_OID_GEN_XMIT_ERROR,
+ RNDIS_OID_GEN_RCV_ERROR,
+ RNDIS_OID_GEN_RCV_NO_BUFFER,
+#ifdef RNDIS_OPTIONAL_STATS
+ RNDIS_OID_GEN_DIRECTED_BYTES_XMIT,
+ RNDIS_OID_GEN_DIRECTED_FRAMES_XMIT,
+ RNDIS_OID_GEN_MULTICAST_BYTES_XMIT,
+ RNDIS_OID_GEN_MULTICAST_FRAMES_XMIT,
+ RNDIS_OID_GEN_BROADCAST_BYTES_XMIT,
+ RNDIS_OID_GEN_BROADCAST_FRAMES_XMIT,
+ RNDIS_OID_GEN_DIRECTED_BYTES_RCV,
+ RNDIS_OID_GEN_DIRECTED_FRAMES_RCV,
+ RNDIS_OID_GEN_MULTICAST_BYTES_RCV,
+ RNDIS_OID_GEN_MULTICAST_FRAMES_RCV,
+ RNDIS_OID_GEN_BROADCAST_BYTES_RCV,
+ RNDIS_OID_GEN_BROADCAST_FRAMES_RCV,
+ RNDIS_OID_GEN_RCV_CRC_ERROR,
+ RNDIS_OID_GEN_TRANSMIT_QUEUE_LENGTH,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+ /* mandatory 802.3 */
+ /* the general stuff */
+ RNDIS_OID_802_3_PERMANENT_ADDRESS,
+ RNDIS_OID_802_3_CURRENT_ADDRESS,
+ RNDIS_OID_802_3_MULTICAST_LIST,
+ RNDIS_OID_802_3_MAC_OPTIONS,
+ RNDIS_OID_802_3_MAXIMUM_LIST_SIZE,
+
+ /* the statistical stuff */
+ RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT,
+ RNDIS_OID_802_3_XMIT_ONE_COLLISION,
+ RNDIS_OID_802_3_XMIT_MORE_COLLISIONS,
+#ifdef RNDIS_OPTIONAL_STATS
+ RNDIS_OID_802_3_XMIT_DEFERRED,
+ RNDIS_OID_802_3_XMIT_MAX_COLLISIONS,
+ RNDIS_OID_802_3_RCV_OVERRUN,
+ RNDIS_OID_802_3_XMIT_UNDERRUN,
+ RNDIS_OID_802_3_XMIT_HEARTBEAT_FAILURE,
+ RNDIS_OID_802_3_XMIT_TIMES_CRS_LOST,
+ RNDIS_OID_802_3_XMIT_LATE_COLLISIONS,
+#endif /* RNDIS_OPTIONAL_STATS */
+
+#ifdef RNDIS_PM
+ /* PM and wakeup are "mandatory" for USB, but the RNDIS specs
+ * don't say what they mean ... and the NDIS specs are often
+ * confusing and/or ambiguous in this context. (That is, more
+ * so than their specs for the other OIDs.)
+ *
+ * FIXME someone who knows what these should do, please
+ * implement them!
+ */
+
+ /* power management */
+ OID_PNP_CAPABILITIES,
+ OID_PNP_QUERY_POWER,
+ OID_PNP_SET_POWER,
+
+#ifdef RNDIS_WAKEUP
+ /* wake up host */
+ OID_PNP_ENABLE_WAKE_UP,
+ OID_PNP_ADD_WAKE_UP_PATTERN,
+ OID_PNP_REMOVE_WAKE_UP_PATTERN,
+#endif /* RNDIS_WAKEUP */
+#endif /* RNDIS_PM */
+};
+
+
+/* NDIS Functions */
+static int gen_ndis_query_resp(int configNr, u32 OID, u8 *buf,
+ unsigned buf_len, rndis_resp_t *r)
+{
+ int retval = -ENOTSUPP;
+ u32 length = 4; /* usually */
+ __le32 *outbuf;
+ int i, count;
+ rndis_query_cmplt_type *resp;
+ struct net_device *net;
+ struct rtnl_link_stats64 temp;
+ const struct rtnl_link_stats64 *stats;
+
+ if (!r) return -ENOMEM;
+ resp = (rndis_query_cmplt_type *)r->buf;
+
+ if (!resp) return -ENOMEM;
+
+ if (buf_len && rndis_debug > 1) {
+ pr_debug("query OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ pr_debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+
+ /* response goes here, right after the header */
+ outbuf = (__le32 *)&resp[1];
+ resp->InformationBufferOffset = cpu_to_le32(16);
+
+ net = rndis_per_dev_params[configNr].dev;
+ stats = dev_get_stats(net, &temp);
+
+ switch (OID) {
+
+ /* general oids (table 4-1) */
+
+ /* mandatory */
+ case RNDIS_OID_GEN_SUPPORTED_LIST:
+ pr_debug("%s: RNDIS_OID_GEN_SUPPORTED_LIST\n", __func__);
+ length = sizeof(oid_supported_list);
+ count = length / sizeof(u32);
+ for (i = 0; i < count; i++)
+ outbuf[i] = cpu_to_le32(oid_supported_list[i]);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_HARDWARE_STATUS:
+ pr_debug("%s: RNDIS_OID_GEN_HARDWARE_STATUS\n", __func__);
+ /* Bogus question!
+ * Hardware must be ready to receive high level protocols.
+ * BTW:
+ * reddite ergo quae sunt Caesaris Caesari
+ * et quae sunt Dei Deo!
+ */
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_SUPPORTED:
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_SUPPORTED\n", __func__);
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_IN_USE:
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_IN_USE\n", __func__);
+ /* one medium, one transport... (maybe you do it better) */
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr].medium);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_LINK_SPEED:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_LINK_SPEED\n", __func__);
+ if (rndis_per_dev_params[configNr].media_state
+ == RNDIS_MEDIA_STATE_DISCONNECTED)
+ *outbuf = cpu_to_le32(0);
+ else
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].speed);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_TRANSMIT_BLOCK_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_RECEIVE_BLOCK_SIZE\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].dev->mtu);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_VENDOR_ID:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_ID\n", __func__);
+ *outbuf = cpu_to_le32(
+ rndis_per_dev_params[configNr].vendorID);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_VENDOR_DESCRIPTION:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_DESCRIPTION\n", __func__);
+ if (rndis_per_dev_params[configNr].vendorDescr) {
+ length = strlen(rndis_per_dev_params[configNr].
+ vendorDescr);
+ memcpy(outbuf,
+ rndis_per_dev_params[configNr].vendorDescr,
+ length);
+ } else {
+ outbuf[0] = 0;
+ }
+ retval = 0;
+ break;
+
+ case RNDIS_OID_GEN_VENDOR_DRIVER_VERSION:
+ pr_debug("%s: RNDIS_OID_GEN_VENDOR_DRIVER_VERSION\n", __func__);
+ /* Created as LE */
+ *outbuf = rndis_driver_version;
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+ pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER\n", __func__);
+ *outbuf = cpu_to_le32(*rndis_per_dev_params[configNr].filter);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE:
+ pr_debug("%s: RNDIS_OID_GEN_MAXIMUM_TOTAL_SIZE\n", __func__);
+ *outbuf = cpu_to_le32(RNDIS_MAX_TOTAL_SIZE);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_MEDIA_CONNECT_STATUS\n", __func__);
+ *outbuf = cpu_to_le32(rndis_per_dev_params[configNr]
+ .media_state);
+ retval = 0;
+ break;
+
+ case RNDIS_OID_GEN_PHYSICAL_MEDIUM:
+ pr_debug("%s: RNDIS_OID_GEN_PHYSICAL_MEDIUM\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* The RNDIS specification is incomplete/wrong. Some versions
+ * of MS-Windows expect OIDs that aren't specified there. Other
+ * versions emit undefined RNDIS messages. DOCUMENT ALL THESE!
+ */
+ case RNDIS_OID_GEN_MAC_OPTIONS: /* from WinME */
+ pr_debug("%s: RNDIS_OID_GEN_MAC_OPTIONS\n", __func__);
+ *outbuf = cpu_to_le32(
+ RNDIS_MAC_OPTION_RECEIVE_SERIALIZED
+ | RNDIS_MAC_OPTION_FULL_DUPLEX);
+ retval = 0;
+ break;
+
+ /* statistics OIDs (table 4-2) */
+
+ /* mandatory */
+ case RNDIS_OID_GEN_XMIT_OK:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_XMIT_OK\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->tx_packets
+ - stats->tx_errors - stats->tx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_OK:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_RCV_OK\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_packets
+ - stats->rx_errors - stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_XMIT_ERROR:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_XMIT_ERROR\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->tx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_ERROR:
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_OID_GEN_RCV_ERROR\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_GEN_RCV_NO_BUFFER:
+ pr_debug("%s: RNDIS_OID_GEN_RCV_NO_BUFFER\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+ /* ieee802.3 OIDs (table 4-3) */
+
+ /* mandatory */
+ case RNDIS_OID_802_3_PERMANENT_ADDRESS:
+ pr_debug("%s: RNDIS_OID_802_3_PERMANENT_ADDRESS\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf,
+ rndis_per_dev_params[configNr].host_mac,
+ length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_CURRENT_ADDRESS:
+ pr_debug("%s: RNDIS_OID_802_3_CURRENT_ADDRESS\n", __func__);
+ if (rndis_per_dev_params[configNr].dev) {
+ length = ETH_ALEN;
+ memcpy(outbuf,
+ rndis_per_dev_params [configNr].host_mac,
+ length);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_MULTICAST_LIST:
+ pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+ /* Multicast base address only */
+ *outbuf = cpu_to_le32(0xE0000000);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_MAXIMUM_LIST_SIZE:
+ pr_debug("%s: RNDIS_OID_802_3_MAXIMUM_LIST_SIZE\n", __func__);
+ /* Multicast base address only */
+ *outbuf = cpu_to_le32(1);
+ retval = 0;
+ break;
+
+ case RNDIS_OID_802_3_MAC_OPTIONS:
+ pr_debug("%s: RNDIS_OID_802_3_MAC_OPTIONS\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* ieee802.3 statistics OIDs (table 4-4) */
+
+ /* mandatory */
+ case RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT:
+ pr_debug("%s: RNDIS_OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+ if (stats) {
+ *outbuf = cpu_to_le32(stats->rx_frame_errors);
+ retval = 0;
+ }
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_XMIT_ONE_COLLISION:
+ pr_debug("%s: RNDIS_OID_802_3_XMIT_ONE_COLLISION\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ /* mandatory */
+ case RNDIS_OID_802_3_XMIT_MORE_COLLISIONS:
+ pr_debug("%s: RNDIS_OID_802_3_XMIT_MORE_COLLISIONS\n", __func__);
+ *outbuf = cpu_to_le32(0);
+ retval = 0;
+ break;
+
+ default:
+ pr_warning("%s: query unknown OID 0x%08X\n",
+ __func__, OID);
+ }
+ if (retval < 0)
+ length = 0;
+
+ resp->InformationBufferLength = cpu_to_le32(length);
+ r->length = length + sizeof(*resp);
+ resp->MessageLength = cpu_to_le32(r->length);
+ return retval;
+}
+
+static int gen_ndis_set_resp(u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+ rndis_resp_t *r)
+{
+ rndis_set_cmplt_type *resp;
+ int i, retval = -ENOTSUPP;
+ struct rndis_params *params;
+
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *)r->buf;
+ if (!resp)
+ return -ENOMEM;
+
+ if (buf_len && rndis_debug > 1) {
+ pr_debug("set OID %08x value, len %d:\n", OID, buf_len);
+ for (i = 0; i < buf_len; i += 16) {
+ pr_debug("%03d: %08x %08x %08x %08x\n", i,
+ get_unaligned_le32(&buf[i]),
+ get_unaligned_le32(&buf[i + 4]),
+ get_unaligned_le32(&buf[i + 8]),
+ get_unaligned_le32(&buf[i + 12]));
+ }
+ }
+
+ params = &rndis_per_dev_params[configNr];
+ switch (OID) {
+ case RNDIS_OID_GEN_CURRENT_PACKET_FILTER:
+
+ /* these NDIS_PACKET_TYPE_* bitflags are shared with
+ * cdc_filter; it's not RNDIS-specific
+ * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in:
+ * PROMISCUOUS, DIRECTED,
+ * MULTICAST, ALL_MULTICAST, BROADCAST
+ */
+ *params->filter = (u16)get_unaligned_le32(buf);
+ pr_debug("%s: RNDIS_OID_GEN_CURRENT_PACKET_FILTER %08x\n",
+ __func__, *params->filter);
+
+ /* this call has a significant side effect: it's
+ * what makes the packet flow start and stop, like
+ * activating the CDC Ethernet altsetting.
+ */
+ retval = 0;
+ if (*params->filter) {
+ params->state = RNDIS_DATA_INITIALIZED;
+ netif_carrier_on(params->dev);
+ if (netif_running(params->dev))
+ netif_wake_queue(params->dev);
+ } else {
+ params->state = RNDIS_INITIALIZED;
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ break;
+
+ case RNDIS_OID_802_3_MULTICAST_LIST:
+ /* I think we can ignore this */
+ pr_debug("%s: RNDIS_OID_802_3_MULTICAST_LIST\n", __func__);
+ retval = 0;
+ break;
+
+ default:
+ pr_warning("%s: set unknown OID 0x%08X, size %d\n",
+ __func__, OID, buf_len);
+ }
+
+ return retval;
+}
+
+/*
+ * Response Functions
+ */
+
+static int rndis_init_response(int configNr, rndis_init_msg_type *buf)
+{
+ rndis_init_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ if (!params->dev)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr, sizeof(rndis_init_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_init_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_INIT_C);
+ resp->MessageLength = cpu_to_le32(52);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ resp->MajorVersion = cpu_to_le32(RNDIS_MAJOR_VERSION);
+ resp->MinorVersion = cpu_to_le32(RNDIS_MINOR_VERSION);
+ resp->DeviceFlags = cpu_to_le32(RNDIS_DF_CONNECTIONLESS);
+ resp->Medium = cpu_to_le32(RNDIS_MEDIUM_802_3);
+ resp->MaxPacketsPerTransfer = cpu_to_le32(1);
+ resp->MaxTransferSize = cpu_to_le32(
+ params->dev->mtu
+ + sizeof(struct ethhdr)
+ + sizeof(struct rndis_packet_msg_type)
+ + 22);
+ resp->PacketAlignmentFactor = cpu_to_le32(0);
+ resp->AFListOffset = cpu_to_le32(0);
+ resp->AFListSize = cpu_to_le32(0);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_query_response(int configNr, rndis_query_msg_type *buf)
+{
+ rndis_query_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ /* pr_debug("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID)); */
+ if (!params->dev)
+ return -ENOTSUPP;
+
+ /*
+ * we need more memory:
+ * gen_ndis_query_resp expects enough space for
+ * rndis_query_cmplt_type followed by data.
+ * oid_supported_list is the largest data reply
+ */
+ r = rndis_add_response(configNr,
+ sizeof(oid_supported_list) + sizeof(rndis_query_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_query_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_QUERY_C);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+
+ if (gen_ndis_query_resp(configNr, le32_to_cpu(buf->OID),
+ le32_to_cpu(buf->InformationBufferOffset)
+ + 8 + (u8 *)buf,
+ le32_to_cpu(buf->InformationBufferLength),
+ r)) {
+ /* OID not supported */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ resp->MessageLength = cpu_to_le32(sizeof *resp);
+ resp->InformationBufferLength = cpu_to_le32(0);
+ resp->InformationBufferOffset = cpu_to_le32(0);
+ } else
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_set_response(int configNr, rndis_set_msg_type *buf)
+{
+ u32 BufLength, BufOffset;
+ rndis_set_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response(configNr, sizeof(rndis_set_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_set_cmplt_type *)r->buf;
+
+ BufLength = le32_to_cpu(buf->InformationBufferLength);
+ BufOffset = le32_to_cpu(buf->InformationBufferOffset);
+
+#ifdef VERBOSE_DEBUG
+ pr_debug("%s: Length: %d\n", __func__, BufLength);
+ pr_debug("%s: Offset: %d\n", __func__, BufOffset);
+ pr_debug("%s: InfoBuffer: ", __func__);
+
+ for (i = 0; i < BufLength; i++) {
+ pr_debug("%02x ", *(((u8 *) buf) + i + 8 + BufOffset));
+ }
+
+ pr_debug("\n");
+#endif
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_SET_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ if (gen_ndis_set_resp(configNr, le32_to_cpu(buf->OID),
+ ((u8 *)buf) + 8 + BufOffset, BufLength, r))
+ resp->Status = cpu_to_le32(RNDIS_STATUS_NOT_SUPPORTED);
+ else
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_reset_response(int configNr, rndis_reset_msg_type *buf)
+{
+ rndis_reset_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response(configNr, sizeof(rndis_reset_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_reset_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_RESET_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+ /* resent information */
+ resp->AddressingReset = cpu_to_le32(1);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+static int rndis_keepalive_response(int configNr,
+ rndis_keepalive_msg_type *buf)
+{
+ rndis_keepalive_cmplt_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ /* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+ r = rndis_add_response(configNr, sizeof(rndis_keepalive_cmplt_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_keepalive_cmplt_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_KEEPALIVE_C);
+ resp->MessageLength = cpu_to_le32(16);
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = cpu_to_le32(RNDIS_STATUS_SUCCESS);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+
+/*
+ * Device to Host Comunication
+ */
+static int rndis_indicate_status_msg(int configNr, u32 status)
+{
+ rndis_indicate_status_msg_type *resp;
+ rndis_resp_t *r;
+ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ if (params->state == RNDIS_UNINITIALIZED)
+ return -ENOTSUPP;
+
+ r = rndis_add_response(configNr,
+ sizeof(rndis_indicate_status_msg_type));
+ if (!r)
+ return -ENOMEM;
+ resp = (rndis_indicate_status_msg_type *)r->buf;
+
+ resp->MessageType = cpu_to_le32(RNDIS_MSG_INDICATE);
+ resp->MessageLength = cpu_to_le32(20);
+ resp->Status = cpu_to_le32(status);
+ resp->StatusBufferLength = cpu_to_le32(0);
+ resp->StatusBufferOffset = cpu_to_le32(0);
+
+ params->resp_avail(params->v);
+ return 0;
+}
+
+int rndis_signal_connect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = RNDIS_MEDIA_STATE_CONNECTED;
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_CONNECT);
+}
+
+int rndis_signal_disconnect(int configNr)
+{
+ rndis_per_dev_params[configNr].media_state
+ = RNDIS_MEDIA_STATE_DISCONNECTED;
+ return rndis_indicate_status_msg(configNr,
+ RNDIS_STATUS_MEDIA_DISCONNECT);
+}
+
+void rndis_uninit(int configNr)
+{
+ u8 *buf;
+ u32 length;
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return;
+ rndis_per_dev_params[configNr].state = RNDIS_UNINITIALIZED;
+
+ /* drain the response queue */
+ while ((buf = rndis_get_next_response(configNr, &length)))
+ rndis_free_response(configNr, buf);
+}
+
+void rndis_set_host_mac(int configNr, const u8 *addr)
+{
+ rndis_per_dev_params[configNr].host_mac = addr;
+}
+
+/*
+ * Message Parser
+ */
+int rndis_msg_parser(u8 configNr, u8 *buf)
+{
+ u32 MsgType, MsgLength;
+ __le32 *tmp;
+ struct rndis_params *params;
+
+ if (!buf)
+ return -ENOMEM;
+
+ tmp = (__le32 *)buf;
+ MsgType = get_unaligned_le32(tmp++);
+ MsgLength = get_unaligned_le32(tmp++);
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return -ENOTSUPP;
+ params = &rndis_per_dev_params[configNr];
+
+ /* NOTE: RNDIS is *EXTREMELY* chatty ... Windows constantly polls for
+ * rx/tx statistics and link status, in addition to KEEPALIVE traffic
+ * and normal HC level polling to see if there's any IN traffic.
+ */
+
+ /* For USB: responses may take up to 10 seconds */
+ switch (MsgType) {
+ case RNDIS_MSG_INIT:
+ pr_debug("%s: RNDIS_MSG_INIT\n",
+ __func__);
+ params->state = RNDIS_INITIALIZED;
+ return rndis_init_response(configNr,
+ (rndis_init_msg_type *)buf);
+
+ case RNDIS_MSG_HALT:
+ pr_debug("%s: RNDIS_MSG_HALT\n",
+ __func__);
+ params->state = RNDIS_UNINITIALIZED;
+ if (params->dev) {
+ netif_carrier_off(params->dev);
+ netif_stop_queue(params->dev);
+ }
+ return 0;
+
+ case RNDIS_MSG_QUERY:
+ return rndis_query_response(configNr,
+ (rndis_query_msg_type *)buf);
+
+ case RNDIS_MSG_SET:
+ return rndis_set_response(configNr,
+ (rndis_set_msg_type *)buf);
+
+ case RNDIS_MSG_RESET:
+ pr_debug("%s: RNDIS_MSG_RESET\n",
+ __func__);
+ return rndis_reset_response(configNr,
+ (rndis_reset_msg_type *)buf);
+
+ case RNDIS_MSG_KEEPALIVE:
+ /* For USB: host does this every 5 seconds */
+ if (rndis_debug > 1)
+ pr_debug("%s: RNDIS_MSG_KEEPALIVE\n",
+ __func__);
+ return rndis_keepalive_response(configNr,
+ (rndis_keepalive_msg_type *)
+ buf);
+
+ default:
+ /* At least Windows XP emits some undefined RNDIS messages.
+ * In one case those messages seemed to relate to the host
+ * suspending itself.
+ */
+ pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
+ __func__, MsgType, MsgLength);
+ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+ buf, MsgLength);
+ break;
+ }
+
+ return -ENOTSUPP;
+}
+
+int rndis_register(void (*resp_avail)(void *v), void *v)
+{
+ u8 i;
+
+ if (!resp_avail)
+ return -EINVAL;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ if (!rndis_per_dev_params[i].used) {
+ rndis_per_dev_params[i].used = 1;
+ rndis_per_dev_params[i].resp_avail = resp_avail;
+ rndis_per_dev_params[i].v = v;
+ pr_debug("%s: configNr = %d\n", __func__, i);
+ return i;
+ }
+ }
+ pr_debug("failed\n");
+
+ return -ENODEV;
+}
+
+void rndis_deregister(int configNr)
+{
+ pr_debug("%s:\n", __func__);
+
+ if (configNr >= RNDIS_MAX_CONFIGS) return;
+ rndis_per_dev_params[configNr].used = 0;
+}
+
+int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
+{
+ pr_debug("%s:\n", __func__);
+ if (!dev)
+ return -EINVAL;
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].dev = dev;
+ rndis_per_dev_params[configNr].filter = cdc_filter;
+
+ return 0;
+}
+
+int rndis_set_param_vendor(u8 configNr, u32 vendorID, const char *vendorDescr)
+{
+ pr_debug("%s:\n", __func__);
+ if (!vendorDescr) return -1;
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].vendorID = vendorID;
+ rndis_per_dev_params[configNr].vendorDescr = vendorDescr;
+
+ return 0;
+}
+
+int rndis_set_param_medium(u8 configNr, u32 medium, u32 speed)
+{
+ pr_debug("%s: %u %u\n", __func__, medium, speed);
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params[configNr].medium = medium;
+ rndis_per_dev_params[configNr].speed = speed;
+
+ return 0;
+}
+
+void rndis_add_hdr(struct sk_buff *skb)
+{
+ struct rndis_packet_msg_type *header;
+
+ if (!skb)
+ return;
+ header = (void *)skb_push(skb, sizeof(*header));
+ memset(header, 0, sizeof *header);
+ header->MessageType = cpu_to_le32(RNDIS_MSG_PACKET);
+ header->MessageLength = cpu_to_le32(skb->len);
+ header->DataOffset = cpu_to_le32(36);
+ header->DataLength = cpu_to_le32(skb->len - sizeof(*header));
+}
+
+void rndis_free_response(int configNr, u8 *buf)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (r && r->buf == buf) {
+ list_del(&r->list);
+ kfree(r);
+ }
+ }
+}
+
+u8 *rndis_get_next_response(int configNr, u32 *length)
+{
+ rndis_resp_t *r;
+ struct list_head *act, *tmp;
+
+ if (!length) return NULL;
+
+ list_for_each_safe(act, tmp,
+ &(rndis_per_dev_params[configNr].resp_queue))
+ {
+ r = list_entry(act, rndis_resp_t, list);
+ if (!r->send) {
+ r->send = 1;
+ *length = r->length;
+ return r->buf;
+ }
+ }
+
+ return NULL;
+}
+
+static rndis_resp_t *rndis_add_response(int configNr, u32 length)
+{
+ rndis_resp_t *r;
+
+ /* NOTE: this gets copied into ether.c USB_BUFSIZ bytes ... */
+ r = kmalloc(sizeof(rndis_resp_t) + length, GFP_ATOMIC);
+ if (!r) return NULL;
+
+ r->buf = (u8 *)(r + 1);
+ r->length = length;
+ r->send = 0;
+
+ list_add_tail(&r->list,
+ &(rndis_per_dev_params[configNr].resp_queue));
+ return r;
+}
+
+int rndis_rm_hdr(struct gether *port,
+ struct sk_buff *skb,
+ struct sk_buff_head *list)
+{
+ /* tmp points to a struct rndis_packet_msg_type */
+ __le32 *tmp = (void *)skb->data;
+
+ /* MessageType, MessageLength */
+ if (cpu_to_le32(RNDIS_MSG_PACKET)
+ != get_unaligned(tmp++)) {
+ dev_kfree_skb_any(skb);
+ return -EINVAL;
+ }
+ tmp++;
+
+ /* DataOffset, DataLength */
+ if (!skb_pull(skb, get_unaligned_le32(tmp++) + 8)) {
+ dev_kfree_skb_any(skb);
+ return -EOVERFLOW;
+ }
+ skb_trim(skb, get_unaligned_le32(tmp++));
+
+ skb_queue_tail(list, skb);
+ return 0;
+}
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static int rndis_proc_show(struct seq_file *m, void *v)
+{
+ rndis_params *param = m->private;
+
+ seq_printf(m,
+ "Config Nr. %d\n"
+ "used : %s\n"
+ "state : %s\n"
+ "medium : 0x%08X\n"
+ "speed : %d\n"
+ "cable : %s\n"
+ "vendor ID : 0x%08X\n"
+ "vendor : %s\n",
+ param->confignr, (param->used) ? "y" : "n",
+ ({ char *s = "?";
+ switch (param->state) {
+ case RNDIS_UNINITIALIZED:
+ s = "RNDIS_UNINITIALIZED"; break;
+ case RNDIS_INITIALIZED:
+ s = "RNDIS_INITIALIZED"; break;
+ case RNDIS_DATA_INITIALIZED:
+ s = "RNDIS_DATA_INITIALIZED"; break;
+ }; s; }),
+ param->medium,
+ (param->media_state) ? 0 : param->speed*100,
+ (param->media_state) ? "disconnected" : "connected",
+ param->vendorID, param->vendorDescr);
+ return 0;
+}
+
+static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
+ u32 speed = 0;
+ int i, fl_speed = 0;
+
+ for (i = 0; i < count; i++) {
+ char c;
+ if (get_user(c, buffer))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ fl_speed = 1;
+ speed = speed * 10 + c - '0';
+ break;
+ case 'C':
+ case 'c':
+ rndis_signal_connect(p->confignr);
+ break;
+ case 'D':
+ case 'd':
+ rndis_signal_disconnect(p->confignr);
+ break;
+ default:
+ if (fl_speed) p->speed = speed;
+ else pr_debug("%c is not valid\n", c);
+ break;
+ }
+
+ buffer++;
+ }
+
+ return count;
+}
+
+static int rndis_proc_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, rndis_proc_show, PDE(inode)->data);
+}
+
+static const struct file_operations rndis_proc_fops = {
+ .owner = THIS_MODULE,
+ .open = rndis_proc_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+ .write = rndis_proc_write,
+};
+
+#define NAME_TEMPLATE "driver/rndis-%03d"
+
+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
+int rndis_init(void)
+{
+ u8 i;
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ char name [20];
+
+ sprintf(name, NAME_TEMPLATE, i);
+ rndis_connect_state[i] = proc_create_data(name, 0660, NULL,
+ &rndis_proc_fops,
+ (void *)(rndis_per_dev_params + i));
+ if (!rndis_connect_state[i]) {
+ pr_debug("%s: remove entries", __func__);
+ while (i) {
+ sprintf(name, NAME_TEMPLATE, --i);
+ remove_proc_entry(name, NULL);
+ }
+ pr_debug("\n");
+ return -EIO;
+ }
+#endif
+ rndis_per_dev_params[i].confignr = i;
+ rndis_per_dev_params[i].used = 0;
+ rndis_per_dev_params[i].state = RNDIS_UNINITIALIZED;
+ rndis_per_dev_params[i].media_state
+ = RNDIS_MEDIA_STATE_DISCONNECTED;
+ INIT_LIST_HEAD(&(rndis_per_dev_params[i].resp_queue));
+ }
+
+ return 0;
+}
+
+void rndis_exit(void)
+{
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+ u8 i;
+ char name[20];
+
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ sprintf(name, NAME_TEMPLATE, i);
+ remove_proc_entry(name, NULL);
+ }
+#endif
+}
diff --git a/drivers/staging/ccg/rndis.h b/drivers/staging/ccg/rndis.h
new file mode 100644
index 0000000..0647f2f
--- /dev/null
+++ b/drivers/staging/ccg/rndis.h
@@ -0,0 +1,222 @@
+/*
+ * RNDIS Definitions for Remote NDIS
+ *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+ * 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 software was originally developed in conformance with
+ * Microsoft's Remote NDIS Specification License Agreement.
+ */
+
+#ifndef _LINUX_RNDIS_H
+#define _LINUX_RNDIS_H
+
+#include <linux/rndis.h>
+#include "ndis.h"
+
+#define RNDIS_MAXIMUM_FRAME_SIZE 1518
+#define RNDIS_MAX_TOTAL_SIZE 1558
+
+typedef struct rndis_init_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 MaxTransferSize;
+} rndis_init_msg_type;
+
+typedef struct rndis_init_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 MajorVersion;
+ __le32 MinorVersion;
+ __le32 DeviceFlags;
+ __le32 Medium;
+ __le32 MaxPacketsPerTransfer;
+ __le32 MaxTransferSize;
+ __le32 PacketAlignmentFactor;
+ __le32 AFListOffset;
+ __le32 AFListSize;
+} rndis_init_cmplt_type;
+
+typedef struct rndis_halt_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_halt_msg_type;
+
+typedef struct rndis_query_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_query_msg_type;
+
+typedef struct rndis_query_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+} rndis_query_cmplt_type;
+
+typedef struct rndis_set_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 OID;
+ __le32 InformationBufferLength;
+ __le32 InformationBufferOffset;
+ __le32 DeviceVcHandle;
+} rndis_set_msg_type;
+
+typedef struct rndis_set_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_set_cmplt_type;
+
+typedef struct rndis_reset_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Reserved;
+} rndis_reset_msg_type;
+
+typedef struct rndis_reset_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 AddressingReset;
+} rndis_reset_cmplt_type;
+
+typedef struct rndis_indicate_status_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 Status;
+ __le32 StatusBufferLength;
+ __le32 StatusBufferOffset;
+} rndis_indicate_status_msg_type;
+
+typedef struct rndis_keepalive_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+} rndis_keepalive_msg_type;
+
+typedef struct rndis_keepalive_cmplt_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 RequestID;
+ __le32 Status;
+} rndis_keepalive_cmplt_type;
+
+struct rndis_packet_msg_type
+{
+ __le32 MessageType;
+ __le32 MessageLength;
+ __le32 DataOffset;
+ __le32 DataLength;
+ __le32 OOBDataOffset;
+ __le32 OOBDataLength;
+ __le32 NumOOBDataElements;
+ __le32 PerPacketInfoOffset;
+ __le32 PerPacketInfoLength;
+ __le32 VcHandle;
+ __le32 Reserved;
+} __attribute__ ((packed));
+
+struct rndis_config_parameter
+{
+ __le32 ParameterNameOffset;
+ __le32 ParameterNameLength;
+ __le32 ParameterType;
+ __le32 ParameterValueOffset;
+ __le32 ParameterValueLength;
+};
+
+/* implementation specific */
+enum rndis_state
+{
+ RNDIS_UNINITIALIZED,
+ RNDIS_INITIALIZED,
+ RNDIS_DATA_INITIALIZED,
+};
+
+typedef struct rndis_resp_t
+{
+ struct list_head list;
+ u8 *buf;
+ u32 length;
+ int send;
+} rndis_resp_t;
+
+typedef struct rndis_params
+{
+ u8 confignr;
+ u8 used;
+ u16 saved_filter;
+ enum rndis_state state;
+ u32 medium;
+ u32 speed;
+ u32 media_state;
+
+ const u8 *host_mac;
+ u16 *filter;
+ struct net_device *dev;
+
+ u32 vendorID;
+ const char *vendorDescr;
+ void (*resp_avail)(void *v);
+ void *v;
+ struct list_head resp_queue;
+} rndis_params;
+
+/* RNDIS Message parser and other useless functions */
+int rndis_msg_parser (u8 configNr, u8 *buf);
+int rndis_register(void (*resp_avail)(void *v), void *v);
+void rndis_deregister (int configNr);
+int rndis_set_param_dev (u8 configNr, struct net_device *dev,
+ u16 *cdc_filter);
+int rndis_set_param_vendor (u8 configNr, u32 vendorID,
+ const char *vendorDescr);
+int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed);
+void rndis_add_hdr (struct sk_buff *skb);
+int rndis_rm_hdr(struct gether *port, struct sk_buff *skb,
+ struct sk_buff_head *list);
+u8 *rndis_get_next_response (int configNr, u32 *length);
+void rndis_free_response (int configNr, u8 *buf);
+
+void rndis_uninit (int configNr);
+int rndis_signal_connect (int configNr);
+int rndis_signal_disconnect (int configNr);
+int rndis_state (int configNr);
+extern void rndis_set_host_mac (int configNr, const u8 *addr);
+
+int rndis_init(void);
+void rndis_exit (void);
+
+#endif /* _LINUX_RNDIS_H */
diff --git a/drivers/staging/ccg/storage_common.c b/drivers/staging/ccg/storage_common.c
new file mode 100644
index 0000000..8d9bcd8
--- /dev/null
+++ b/drivers/staging/ccg/storage_common.c
@@ -0,0 +1,893 @@
+/*
+ * storage_common.c -- Common definitions for mass storage functionality
+ *
+ * Copyright (C) 2003-2008 Alan Stern
+ * Copyeight (C) 2009 Samsung Electronics
+ * Author: Michal Nazarewicz (mina86@mina86.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 file requires the following identifiers used in USB strings to
+ * be defined (each of type pointer to char):
+ * - fsg_string_manufacturer -- name of the manufacturer
+ * - fsg_string_product -- name of the product
+ * - fsg_string_config -- name of the configuration
+ * - fsg_string_interface -- name of the interface
+ * The first four are only needed when FSG_DESCRIPTORS_DEVICE_STRINGS
+ * macro is defined prior to including this file.
+ */
+
+/*
+ * When FSG_NO_INTR_EP is defined fsg_fs_intr_in_desc and
+ * fsg_hs_intr_in_desc objects as well as
+ * FSG_FS_FUNCTION_PRE_EP_ENTRIES and FSG_HS_FUNCTION_PRE_EP_ENTRIES
+ * macros are not defined.
+ *
+ * When FSG_NO_DEVICE_STRINGS is defined FSG_STRING_MANUFACTURER,
+ * FSG_STRING_PRODUCT, FSG_STRING_SERIAL and FSG_STRING_CONFIG are not
+ * defined (as well as corresponding entries in string tables are
+ * missing) and FSG_STRING_INTERFACE has value of zero.
+ *
+ * When FSG_NO_OTG is defined fsg_otg_desc won't be defined.
+ */
+
+/*
+ * When USB_GADGET_DEBUG_FILES is defined the module param num_buffers
+ * sets the number of pipeline buffers (length of the fsg_buffhd array).
+ * The valid range of num_buffers is: num >= 2 && num <= 4.
+ */
+
+
+#include <linux/usb/storage.h>
+#include <scsi/scsi.h>
+#include <asm/unaligned.h>
+
+
+/*
+ * Thanks to NetChip Technologies for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with any other driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define FSG_VENDOR_ID 0x0525 /* NetChip */
+#define FSG_PRODUCT_ID 0xa4a5 /* Linux-USB File-backed Storage Gadget */
+
+
+/*-------------------------------------------------------------------------*/
+
+
+#ifndef DEBUG
+#undef VERBOSE_DEBUG
+#undef DUMP_MSGS
+#endif /* !DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VLDBG LDBG
+#else
+#define VLDBG(lun, fmt, args...) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define LDBG(lun, fmt, args...) dev_dbg (&(lun)->dev, fmt, ## args)
+#define LERROR(lun, fmt, args...) dev_err (&(lun)->dev, fmt, ## args)
+#define LWARN(lun, fmt, args...) dev_warn(&(lun)->dev, fmt, ## args)
+#define LINFO(lun, fmt, args...) dev_info(&(lun)->dev, fmt, ## args)
+
+/*
+ * Keep those macros in sync with those in
+ * include/linux/usb/composite.h or else GCC will complain. If they
+ * are identical (the same names of arguments, white spaces in the
+ * same places) GCC will allow redefinition otherwise (even if some
+ * white space is removed or added) warning will be issued.
+ *
+ * Those macros are needed here because File Storage Gadget does not
+ * include the composite.h header. For composite gadgets those macros
+ * are redundant since composite.h is included any way.
+ *
+ * One could check whether those macros are already defined (which
+ * would indicate composite.h had been included) or not (which would
+ * indicate we were in FSG) but this is not done because a warning is
+ * desired if definitions here differ from the ones in composite.h.
+ *
+ * We want the definitions to match and be the same in File Storage
+ * Gadget as well as Mass Storage Function (and so composite gadgets
+ * using MSF). If someone changes them in composite.h it will produce
+ * a warning in this file when building MSF.
+ */
+#define DBG(d, fmt, args...) dev_dbg(&(d)->gadget->dev , fmt , ## args)
+#define VDBG(d, fmt, args...) dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+#define ERROR(d, fmt, args...) dev_err(&(d)->gadget->dev , fmt , ## args)
+#define WARNING(d, fmt, args...) dev_warn(&(d)->gadget->dev , fmt , ## args)
+#define INFO(d, fmt, args...) dev_info(&(d)->gadget->dev , fmt , ## args)
+
+
+
+#ifdef DUMP_MSGS
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { \
+ if (length < 512) { \
+ DBG(fsg, "%s, length %u:\n", label, length); \
+ print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+ 16, 1, buf, length, 0); \
+ } \
+} while (0)
+
+# define dump_cdb(fsg) do { } while (0)
+
+#else
+
+# define dump_msg(fsg, /* const char * */ label, \
+ /* const u8 * */ buf, /* unsigned */ length) do { } while (0)
+
+# ifdef VERBOSE_DEBUG
+
+# define dump_cdb(fsg) \
+ print_hex_dump(KERN_DEBUG, "SCSI CDB: ", DUMP_PREFIX_NONE, \
+ 16, 1, (fsg)->cmnd, (fsg)->cmnd_size, 0) \
+
+# else
+
+# define dump_cdb(fsg) do { } while (0)
+
+# endif /* VERBOSE_DEBUG */
+
+#endif /* DUMP_MSGS */
+
+/*-------------------------------------------------------------------------*/
+
+/* CBI Interrupt data structure */
+struct interrupt_data {
+ u8 bType;
+ u8 bValue;
+};
+
+#define CBI_INTERRUPT_DATA_LEN 2
+
+/* CBI Accept Device-Specific Command request */
+#define USB_CBI_ADSC_REQUEST 0x00
+
+
+/* Length of a SCSI Command Data Block */
+#define MAX_COMMAND_SIZE 16
+
+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */
+#define SS_NO_SENSE 0
+#define SS_COMMUNICATION_FAILURE 0x040800
+#define SS_INVALID_COMMAND 0x052000
+#define SS_INVALID_FIELD_IN_CDB 0x052400
+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100
+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500
+#define SS_MEDIUM_NOT_PRESENT 0x023a00
+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302
+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800
+#define SS_RESET_OCCURRED 0x062900
+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900
+#define SS_UNRECOVERED_READ_ERROR 0x031100
+#define SS_WRITE_ERROR 0x030c02
+#define SS_WRITE_PROTECTED 0x072700
+
+#define SK(x) ((u8) ((x) >> 16)) /* Sense Key byte, etc. */
+#define ASC(x) ((u8) ((x) >> 8))
+#define ASCQ(x) ((u8) (x))
+
+
+/*-------------------------------------------------------------------------*/
+
+
+struct fsg_lun {
+ struct file *filp;
+ loff_t file_length;
+ loff_t num_sectors;
+
+ unsigned int initially_ro:1;
+ unsigned int ro:1;
+ unsigned int removable:1;
+ unsigned int cdrom:1;
+ unsigned int prevent_medium_removal:1;
+ unsigned int registered:1;
+ unsigned int info_valid:1;
+ unsigned int nofua:1;
+
+ u32 sense_data;
+ u32 sense_data_info;
+ u32 unit_attention_data;
+
+ unsigned int blkbits; /* Bits of logical block size of bound block device */
+ unsigned int blksize; /* logical block size of bound block device */
+ struct device dev;
+};
+
+#define fsg_lun_is_open(curlun) ((curlun)->filp != NULL)
+
+static struct fsg_lun *fsg_lun_from_dev(struct device *dev)
+{
+ return container_of(dev, struct fsg_lun, dev);
+}
+
+
+/* Big enough to hold our biggest descriptor */
+#define EP0_BUFSIZE 256
+#define DELAYED_STATUS (EP0_BUFSIZE + 999) /* An impossibly large value */
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
+module_param_named(num_buffers, fsg_num_buffers, uint, S_IRUGO);
+MODULE_PARM_DESC(num_buffers, "Number of pipeline buffers");
+
+#else
+
+/*
+ * Number of buffers we will use.
+ * 2 is usually enough for good buffering pipeline
+ */
+#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
+
+#endif /* CONFIG_USB_DEBUG */
+
+/* check if fsg_num_buffers is within a valid range */
+static inline int fsg_num_buffers_validate(void)
+{
+ if (fsg_num_buffers >= 2 && fsg_num_buffers <= 4)
+ return 0;
+ pr_err("fsg_num_buffers %u is out of range (%d to %d)\n",
+ fsg_num_buffers, 2 ,4);
+ return -EINVAL;
+}
+
+/* Default size of buffer length. */
+#define FSG_BUFLEN ((u32)16384)
+
+/* Maximal number of LUNs supported in mass storage function */
+#define FSG_MAX_LUNS 8
+
+enum fsg_buffer_state {
+ BUF_STATE_EMPTY = 0,
+ BUF_STATE_FULL,
+ BUF_STATE_BUSY
+};
+
+struct fsg_buffhd {
+ void *buf;
+ enum fsg_buffer_state state;
+ struct fsg_buffhd *next;
+
+ /*
+ * The NetChip 2280 is faster, and handles some protocol faults
+ * better, if we don't submit any short bulk-out read requests.
+ * So we will record the intended request length here.
+ */
+ unsigned int bulk_out_intended_length;
+
+ struct usb_request *inreq;
+ int inreq_busy;
+ struct usb_request *outreq;
+ int outreq_busy;
+};
+
+enum fsg_state {
+ /* This one isn't used anywhere */
+ FSG_STATE_COMMAND_PHASE = -10,
+ FSG_STATE_DATA_PHASE,
+ FSG_STATE_STATUS_PHASE,
+
+ FSG_STATE_IDLE = 0,
+ FSG_STATE_ABORT_BULK_OUT,
+ FSG_STATE_RESET,
+ FSG_STATE_INTERFACE_CHANGE,
+ FSG_STATE_CONFIG_CHANGE,
+ FSG_STATE_DISCONNECT,
+ FSG_STATE_EXIT,
+ FSG_STATE_TERMINATED
+};
+
+enum data_direction {
+ DATA_DIR_UNKNOWN = 0,
+ DATA_DIR_FROM_HOST,
+ DATA_DIR_TO_HOST,
+ DATA_DIR_NONE
+};
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static inline u32 get_unaligned_be24(u8 *buf)
+{
+ return 0xffffff & (u32) get_unaligned_be32(buf - 1);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+enum {
+#ifndef FSG_NO_DEVICE_STRINGS
+ FSG_STRING_MANUFACTURER = 1,
+ FSG_STRING_PRODUCT,
+ FSG_STRING_SERIAL,
+ FSG_STRING_CONFIG,
+#endif
+ FSG_STRING_INTERFACE
+};
+
+
+#ifndef FSG_NO_OTG
+static struct usb_otg_descriptor
+fsg_otg_desc = {
+ .bLength = sizeof fsg_otg_desc,
+ .bDescriptorType = USB_DT_OTG,
+
+ .bmAttributes = USB_OTG_SRP,
+};
+#endif
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor
+fsg_intf_desc = {
+ .bLength = sizeof fsg_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 2, /* Adjusted during fsg_bind() */
+ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
+ .bInterfaceSubClass = USB_SC_SCSI, /* Adjusted during fsg_bind() */
+ .bInterfaceProtocol = USB_PR_BULK, /* Adjusted during fsg_bind() */
+ .iInterface = FSG_STRING_INTERFACE,
+};
+
+/*
+ * Three full-speed endpoint descriptors: bulk-in, bulk-out, and
+ * interrupt-in.
+ */
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+static struct usb_endpoint_descriptor
+fsg_fs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ /* wMaxPacketSize set by autoconfiguration */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_fs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 32, /* frames -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_FS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_fs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_fs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_fs_intr_in_desc,
+#endif
+ NULL,
+};
+
+
+/*
+ * USB 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ *
+ * That means alternate endpoint descriptors (bigger packets)
+ * and a "device qualifier" ... plus more construction options
+ * for the configuration descriptor.
+ */
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor
+fsg_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(512),
+ .bInterval = 1, /* NAK every 1 uframe */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_hs_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_HS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static struct usb_descriptor_header *fsg_hs_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_hs_bulk_out_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_hs_intr_in_desc,
+#endif
+ NULL,
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+static struct usb_endpoint_descriptor
+fsg_ss_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_bulk_out_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ /*.bMaxBurst = DYNAMIC, */
+};
+
+#ifndef FSG_NO_INTR_EP
+
+static struct usb_endpoint_descriptor
+fsg_ss_intr_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+
+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = cpu_to_le16(2),
+ .bInterval = 9, /* 2**(9-1) = 256 uframes -> 32 ms */
+};
+
+static struct usb_ss_ep_comp_descriptor fsg_ss_intr_in_comp_desc = {
+ .bLength = sizeof(fsg_ss_bulk_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+
+ .wBytesPerInterval = cpu_to_le16(2),
+};
+
+#ifndef FSG_NO_OTG
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 2
+#else
+# define FSG_SS_FUNCTION_PRE_EP_ENTRIES 1
+#endif
+
+#endif
+
+static __maybe_unused struct usb_ext_cap_descriptor fsg_ext_cap_desc = {
+ .bLength = USB_DT_USB_EXT_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_CAP_TYPE_EXT,
+
+ .bmAttributes = cpu_to_le32(USB_LPM_SUPPORT),
+};
+
+static __maybe_unused struct usb_ss_cap_descriptor fsg_ss_cap_desc = {
+ .bLength = USB_DT_USB_SS_CAP_SIZE,
+ .bDescriptorType = USB_DT_DEVICE_CAPABILITY,
+ .bDevCapabilityType = USB_SS_CAP_TYPE,
+
+ /* .bmAttributes = LTM is not supported yet */
+
+ .wSpeedSupported = cpu_to_le16(USB_LOW_SPEED_OPERATION
+ | USB_FULL_SPEED_OPERATION
+ | USB_HIGH_SPEED_OPERATION
+ | USB_5GBPS_OPERATION),
+ .bFunctionalitySupport = USB_LOW_SPEED_OPERATION,
+ .bU1devExitLat = USB_DEFAULT_U1_DEV_EXIT_LAT,
+ .bU2DevExitLat = cpu_to_le16(USB_DEFAULT_U2_DEV_EXIT_LAT),
+};
+
+static __maybe_unused struct usb_bos_descriptor fsg_bos_desc = {
+ .bLength = USB_DT_BOS_SIZE,
+ .bDescriptorType = USB_DT_BOS,
+
+ .wTotalLength = cpu_to_le16(USB_DT_BOS_SIZE
+ + USB_DT_USB_EXT_CAP_SIZE
+ + USB_DT_USB_SS_CAP_SIZE),
+
+ .bNumDeviceCaps = 2,
+};
+
+static struct usb_descriptor_header *fsg_ss_function[] = {
+#ifndef FSG_NO_OTG
+ (struct usb_descriptor_header *) &fsg_otg_desc,
+#endif
+ (struct usb_descriptor_header *) &fsg_intf_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_in_comp_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_desc,
+ (struct usb_descriptor_header *) &fsg_ss_bulk_out_comp_desc,
+#ifndef FSG_NO_INTR_EP
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_desc,
+ (struct usb_descriptor_header *) &fsg_ss_intr_in_comp_desc,
+#endif
+ NULL,
+};
+
+/* Maxpacket and other transfer characteristics vary by speed. */
+static __maybe_unused struct usb_endpoint_descriptor *
+fsg_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ if (gadget_is_superspeed(g) && g->speed == USB_SPEED_SUPER)
+ return ss;
+ else if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+ return hs;
+ return fs;
+}
+
+
+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */
+static struct usb_string fsg_strings[] = {
+#ifndef FSG_NO_DEVICE_STRINGS
+ {FSG_STRING_MANUFACTURER, fsg_string_manufacturer},
+ {FSG_STRING_PRODUCT, fsg_string_product},
+ {FSG_STRING_SERIAL, ""},
+ {FSG_STRING_CONFIG, fsg_string_config},
+#endif
+ {FSG_STRING_INTERFACE, fsg_string_interface},
+ {}
+};
+
+static struct usb_gadget_strings fsg_stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = fsg_strings,
+};
+
+
+ /*-------------------------------------------------------------------------*/
+
+/*
+ * If the next two routines are called while the gadget is registered,
+ * the caller must own fsg->filesem for writing.
+ */
+
+static void fsg_lun_close(struct fsg_lun *curlun)
+{
+ if (curlun->filp) {
+ LDBG(curlun, "close backing file\n");
+ fput(curlun->filp);
+ curlun->filp = NULL;
+ }
+}
+
+
+static int fsg_lun_open(struct fsg_lun *curlun, const char *filename)
+{
+ int ro;
+ struct file *filp = NULL;
+ int rc = -EINVAL;
+ struct inode *inode = NULL;
+ loff_t size;
+ loff_t num_sectors;
+ loff_t min_sectors;
+ unsigned int blkbits;
+ unsigned int blksize;
+
+ /* R/W if we can, R/O if we must */
+ ro = curlun->initially_ro;
+ if (!ro) {
+ filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0);
+ if (PTR_ERR(filp) == -EROFS || PTR_ERR(filp) == -EACCES)
+ ro = 1;
+ }
+ if (ro)
+ filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0);
+ if (IS_ERR(filp)) {
+ LINFO(curlun, "unable to open backing file: %s\n", filename);
+ return PTR_ERR(filp);
+ }
+
+ if (!(filp->f_mode & FMODE_WRITE))
+ ro = 1;
+
+ inode = filp->f_path.dentry->d_inode;
+ if ((!S_ISREG(inode->i_mode) && !S_ISBLK(inode->i_mode))) {
+ LINFO(curlun, "invalid file type: %s\n", filename);
+ goto out;
+ }
+
+ /*
+ * If we can't read the file, it's no good.
+ * If we can't write the file, use it read-only.
+ */
+ if (!(filp->f_op->read || filp->f_op->aio_read)) {
+ LINFO(curlun, "file not readable: %s\n", filename);
+ goto out;
+ }
+ if (!(filp->f_op->write || filp->f_op->aio_write))
+ ro = 1;
+
+ size = i_size_read(inode->i_mapping->host);
+ if (size < 0) {
+ LINFO(curlun, "unable to find file size: %s\n", filename);
+ rc = (int) size;
+ goto out;
+ }
+
+ if (curlun->cdrom) {
+ blksize = 2048;
+ blkbits = 11;
+ } else if (inode->i_bdev) {
+ blksize = bdev_logical_block_size(inode->i_bdev);
+ blkbits = blksize_bits(blksize);
+ } else {
+ blksize = 512;
+ blkbits = 9;
+ }
+
+ num_sectors = size >> blkbits; /* File size in logic-block-size blocks */
+ min_sectors = 1;
+ if (curlun->cdrom) {
+ min_sectors = 300; /* Smallest track is 300 frames */
+ if (num_sectors >= 256*60*75) {
+ num_sectors = 256*60*75 - 1;
+ 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;
+ }
+
+ if (fsg_lun_is_open(curlun))
+ fsg_lun_close(curlun);
+
+ curlun->blksize = blksize;
+ curlun->blkbits = blkbits;
+ curlun->ro = ro;
+ curlun->filp = filp;
+ curlun->file_length = size;
+ curlun->num_sectors = num_sectors;
+ LDBG(curlun, "open backing file: %s\n", filename);
+ return 0;
+
+out:
+ fput(filp);
+ return rc;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * Sync the file data, don't bother with the metadata.
+ * This code was copied from fs/buffer.c:sys_fdatasync().
+ */
+static int fsg_lun_fsync_sub(struct fsg_lun *curlun)
+{
+ struct file *filp = curlun->filp;
+
+ if (curlun->ro || !filp)
+ return 0;
+ return vfs_fsync(filp, 1);
+}
+
+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_unaligned_be32(addr, dest);
+ }
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+
+static ssize_t fsg_show_ro(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%d\n", fsg_lun_is_open(curlun)
+ ? curlun->ro
+ : curlun->initially_ro);
+}
+
+static ssize_t fsg_show_nofua(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+
+ return sprintf(buf, "%u\n", curlun->nofua);
+}
+
+static ssize_t fsg_show_file(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ char *p;
+ ssize_t rc;
+
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) { /* Get the complete pathname */
+ p = d_path(&curlun->filp->f_path, buf, PAGE_SIZE - 1);
+ if (IS_ERR(p))
+ rc = PTR_ERR(p);
+ else {
+ rc = strlen(p);
+ memmove(buf, p, rc);
+ buf[rc] = '\n'; /* Add a newline */
+ buf[++rc] = 0;
+ }
+ } else { /* No file, return 0 bytes */
+ *buf = 0;
+ rc = 0;
+ }
+ up_read(filesem);
+ return rc;
+}
+
+
+static ssize_t fsg_store_ro(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ ssize_t rc;
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ unsigned ro;
+
+ rc = kstrtouint(buf, 2, &ro);
+ if (rc)
+ return rc;
+
+ /*
+ * Allow the write-enable status to change only while the
+ * backing file is closed.
+ */
+ down_read(filesem);
+ if (fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "read-only status change prevented\n");
+ rc = -EBUSY;
+ } else {
+ curlun->ro = ro;
+ curlun->initially_ro = ro;
+ LDBG(curlun, "read-only status set to %d\n", curlun->ro);
+ rc = count;
+ }
+ up_read(filesem);
+ return rc;
+}
+
+static ssize_t fsg_store_nofua(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ unsigned nofua;
+ int ret;
+
+ ret = kstrtouint(buf, 2, &nofua);
+ if (ret)
+ return ret;
+
+ /* Sync data when switching from async mode to sync */
+ if (!nofua && curlun->nofua)
+ fsg_lun_fsync_sub(curlun);
+
+ curlun->nofua = nofua;
+
+ return count;
+}
+
+static ssize_t fsg_store_file(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsg_lun *curlun = fsg_lun_from_dev(dev);
+ struct rw_semaphore *filesem = dev_get_drvdata(dev);
+ int rc = 0;
+
+ if (curlun->prevent_medium_removal && fsg_lun_is_open(curlun)) {
+ LDBG(curlun, "eject attempt prevented\n");
+ return -EBUSY; /* "Door is locked" */
+ }
+
+ /* Remove a trailing newline */
+ if (count > 0 && buf[count-1] == '\n')
+ ((char *) buf)[count-1] = 0; /* Ugh! */
+
+ /* Load new medium */
+ down_write(filesem);
+ if (count > 0 && buf[0]) {
+ /* fsg_lun_open() will close existing file if any. */
+ rc = fsg_lun_open(curlun, buf);
+ if (rc == 0)
+ curlun->unit_attention_data =
+ SS_NOT_READY_TO_READY_TRANSITION;
+ } else if (fsg_lun_is_open(curlun)) {
+ fsg_lun_close(curlun);
+ curlun->unit_attention_data = SS_MEDIUM_NOT_PRESENT;
+ }
+ up_write(filesem);
+ return (rc < 0 ? rc : count);
+}
diff --git a/drivers/staging/ccg/u_ether.c b/drivers/staging/ccg/u_ether.c
new file mode 100644
index 0000000..1154a99
--- /dev/null
+++ b/drivers/staging/ccg/u_ether.c
@@ -0,0 +1,986 @@
+/*
+ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/gfp.h>
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+
+#include "u_ether.h"
+
+
+/*
+ * This component encapsulates the Ethernet link glue needed to provide
+ * one (!) network link through the USB gadget stack, normally "usb0".
+ *
+ * The control and data models are handled by the function driver which
+ * connects to this code; such as CDC Ethernet (ECM or EEM),
+ * "CDC Subset", or RNDIS. That includes all descriptor and endpoint
+ * management.
+ *
+ * Link level addressing is handled by this component using module
+ * parameters; if no such parameters are provided, random link level
+ * addresses are used. Each end of the link uses one address. The
+ * host end address is exported in various ways, and is often recorded
+ * in configuration databases.
+ *
+ * The driver which assembles each configuration using such a link is
+ * responsible for ensuring that each configuration includes at most one
+ * instance of is network link. (The network layer provides ways for
+ * this single "physical" link to be used by multiple virtual links.)
+ */
+
+#define UETH__VERSION "29-May-2008"
+
+struct eth_dev {
+ /* lock is held while accessing port_usb
+ * or updating its backlink port_usb->ioport
+ */
+ spinlock_t lock;
+ struct gether *port_usb;
+
+ struct net_device *net;
+ struct usb_gadget *gadget;
+
+ spinlock_t req_lock; /* guard {rx,tx}_reqs */
+ struct list_head tx_reqs, rx_reqs;
+ atomic_t tx_qlen;
+
+ struct sk_buff_head rx_frames;
+
+ unsigned header_len;
+ struct sk_buff *(*wrap)(struct gether *, struct sk_buff *skb);
+ int (*unwrap)(struct gether *,
+ struct sk_buff *skb,
+ struct sk_buff_head *list);
+
+ struct work_struct work;
+
+ unsigned long todo;
+#define WORK_RX_MEMORY 0
+
+ bool zlp;
+ u8 host_mac[ETH_ALEN];
+};
+
+/*-------------------------------------------------------------------------*/
+
+#define RX_EXTRA 20 /* bytes guarding against rx overflows */
+
+#define DEFAULT_QLEN 2 /* double buffering by default */
+
+static unsigned qmult = 5;
+module_param(qmult, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
+
+/* for dual-speed hardware, use deeper queues at high/super speed */
+static inline int qlen(struct usb_gadget *gadget)
+{
+ if (gadget_is_dualspeed(gadget) && (gadget->speed == USB_SPEED_HIGH ||
+ gadget->speed == USB_SPEED_SUPER))
+ return qmult * DEFAULT_QLEN;
+ else
+ return DEFAULT_QLEN;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* REVISIT there must be a better way than having two sets
+ * of debug calls ...
+ */
+
+#undef DBG
+#undef VDBG
+#undef ERROR
+#undef INFO
+
+#define xprintk(d, level, fmt, args...) \
+ printk(level "%s: " fmt , (d)->net->name , ## args)
+
+#ifdef DEBUG
+#undef DEBUG
+#define DBG(dev, fmt, args...) \
+ xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE_DEBUG
+#define VDBG DBG
+#else
+#define VDBG(dev, fmt, args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#define ERROR(dev, fmt, args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+#define INFO(dev, fmt, args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+
+/*-------------------------------------------------------------------------*/
+
+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+
+static int ueth_change_mtu(struct net_device *net, int new_mtu)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ unsigned long flags;
+ int status = 0;
+
+ /* don't change MTU on "live" link (peer won't know) */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ status = -EBUSY;
+ else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
+ status = -ERANGE;
+ else
+ net->mtu = new_mtu;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return status;
+}
+
+static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+{
+ struct eth_dev *dev = netdev_priv(net);
+
+ strlcpy(p->driver, "g_ether", sizeof p->driver);
+ strlcpy(p->version, UETH__VERSION, sizeof p->version);
+ strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
+ strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
+}
+
+/* REVISIT can also support:
+ * - WOL (by tracking suspends and issuing remote wakeup)
+ * - msglevel (implies updated messaging)
+ * - ... probably more ethtool ops
+ */
+
+static const struct ethtool_ops ops = {
+ .get_drvinfo = eth_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
+
+static void defer_kevent(struct eth_dev *dev, int flag)
+{
+ if (test_and_set_bit(flag, &dev->todo))
+ return;
+ if (!schedule_work(&dev->work))
+ ERROR(dev, "kevent %d may have been dropped\n", flag);
+ else
+ DBG(dev, "kevent %d scheduled\n", flag);
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req);
+
+static int
+rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
+{
+ struct sk_buff *skb;
+ int retval = -ENOMEM;
+ size_t size = 0;
+ struct usb_ep *out;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ out = dev->port_usb->out_ep;
+ else
+ out = NULL;
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!out)
+ return -ENOTCONN;
+
+
+ /* Padding up to RX_EXTRA handles minor disagreements with host.
+ * Normally we use the USB "terminate on short read" convention;
+ * so allow up to (N*maxpacket), since that memory is normally
+ * already allocated. Some hardware doesn't deal well with short
+ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+ * byte off the end (to force hardware errors on overflow).
+ *
+ * RNDIS uses internal framing, and explicitly allows senders to
+ * pad to end-of-packet. That's potentially nice for speed, but
+ * means receivers can't recover lost synch on their own (because
+ * new packets don't only start after a short RX).
+ */
+ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
+ size += dev->port_usb->header_len;
+ size += out->maxpacket - 1;
+ size -= size % out->maxpacket;
+
+ if (dev->port_usb->is_fixed)
+ size = max_t(size_t, size, dev->port_usb->fixed_out_len);
+
+ skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
+ if (skb == NULL) {
+ DBG(dev, "no rx skb\n");
+ goto enomem;
+ }
+
+ /* Some platforms perform better when IP packets are aligned,
+ * but on at least one, checksumming fails otherwise. Note:
+ * RNDIS headers involve variable numbers of LE32 values.
+ */
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ req->buf = skb->data;
+ req->length = size;
+ req->complete = rx_complete;
+ req->context = skb;
+
+ retval = usb_ep_queue(out, req, gfp_flags);
+ if (retval == -ENOMEM)
+enomem:
+ defer_kevent(dev, WORK_RX_MEMORY);
+ if (retval) {
+ DBG(dev, "rx submit --> %d\n", retval);
+ if (skb)
+ dev_kfree_skb_any(skb);
+ spin_lock_irqsave(&dev->req_lock, flags);
+ list_add(&req->list, &dev->rx_reqs);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ }
+ return retval;
+}
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct sk_buff *skb = req->context, *skb2;
+ struct eth_dev *dev = ep->driver_data;
+ int status = req->status;
+
+ switch (status) {
+
+ /* normal completion */
+ case 0:
+ skb_put(skb, req->actual);
+
+ if (dev->unwrap) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ status = dev->unwrap(dev->port_usb,
+ skb,
+ &dev->rx_frames);
+ } else {
+ dev_kfree_skb_any(skb);
+ status = -ENOTCONN;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+ } else {
+ skb_queue_tail(&dev->rx_frames, skb);
+ }
+ skb = NULL;
+
+ skb2 = skb_dequeue(&dev->rx_frames);
+ while (skb2) {
+ if (status < 0
+ || ETH_HLEN > skb2->len
+ || skb2->len > ETH_FRAME_LEN) {
+ dev->net->stats.rx_errors++;
+ dev->net->stats.rx_length_errors++;
+ DBG(dev, "rx length %d\n", skb2->len);
+ dev_kfree_skb_any(skb2);
+ goto next_frame;
+ }
+ skb2->protocol = eth_type_trans(skb2, dev->net);
+ dev->net->stats.rx_packets++;
+ dev->net->stats.rx_bytes += skb2->len;
+
+ /* no buffer copies needed, unless hardware can't
+ * use skb buffers.
+ */
+ status = netif_rx(skb2);
+next_frame:
+ skb2 = skb_dequeue(&dev->rx_frames);
+ }
+ break;
+
+ /* software-driven interface shutdown */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ VDBG(dev, "rx shutdown, code %d\n", status);
+ goto quiesce;
+
+ /* for hardware automagic (such as pxa) */
+ case -ECONNABORTED: /* endpoint reset */
+ DBG(dev, "rx %s reset\n", ep->name);
+ defer_kevent(dev, WORK_RX_MEMORY);
+quiesce:
+ dev_kfree_skb_any(skb);
+ goto clean;
+
+ /* data overrun */
+ case -EOVERFLOW:
+ dev->net->stats.rx_over_errors++;
+ /* FALLTHROUGH */
+
+ default:
+ dev->net->stats.rx_errors++;
+ DBG(dev, "rx status %d\n", status);
+ break;
+ }
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+ if (!netif_running(dev->net)) {
+clean:
+ spin_lock(&dev->req_lock);
+ list_add(&req->list, &dev->rx_reqs);
+ spin_unlock(&dev->req_lock);
+ req = NULL;
+ }
+ if (req)
+ rx_submit(dev, req, GFP_ATOMIC);
+}
+
+static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
+{
+ unsigned i;
+ struct usb_request *req;
+
+ if (!n)
+ return -ENOMEM;
+
+ /* queue/recycle up to N requests */
+ i = n;
+ list_for_each_entry(req, list, list) {
+ if (i-- == 0)
+ goto extra;
+ }
+ while (i--) {
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (!req)
+ return list_empty(list) ? -ENOMEM : 0;
+ list_add(&req->list, list);
+ }
+ return 0;
+
+extra:
+ /* free extras */
+ for (;;) {
+ struct list_head *next;
+
+ next = req->list.next;
+ list_del(&req->list);
+ usb_ep_free_request(ep, req);
+
+ if (next == list)
+ break;
+
+ req = container_of(next, struct usb_request, list);
+ }
+ return 0;
+}
+
+static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
+{
+ int status;
+
+ spin_lock(&dev->req_lock);
+ status = prealloc(&dev->tx_reqs, link->in_ep, n);
+ if (status < 0)
+ goto fail;
+ status = prealloc(&dev->rx_reqs, link->out_ep, n);
+ if (status < 0)
+ goto fail;
+ goto done;
+fail:
+ DBG(dev, "can't alloc requests\n");
+done:
+ spin_unlock(&dev->req_lock);
+ return status;
+}
+
+static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ struct usb_request *req;
+ unsigned long flags;
+
+ /* fill unused rxq slots with some skb */
+ spin_lock_irqsave(&dev->req_lock, flags);
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del_init(&req->list);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+
+ if (rx_submit(dev, req, gfp_flags) < 0) {
+ defer_kevent(dev, WORK_RX_MEMORY);
+ return;
+ }
+
+ spin_lock_irqsave(&dev->req_lock, flags);
+ }
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+}
+
+static void eth_work(struct work_struct *work)
+{
+ struct eth_dev *dev = container_of(work, struct eth_dev, work);
+
+ if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
+ if (netif_running(dev->net))
+ rx_fill(dev, GFP_KERNEL);
+ }
+
+ if (dev->todo)
+ DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct sk_buff *skb = req->context;
+ struct eth_dev *dev = ep->driver_data;
+
+ switch (req->status) {
+ default:
+ dev->net->stats.tx_errors++;
+ VDBG(dev, "tx err %d\n", req->status);
+ /* FALLTHROUGH */
+ case -ECONNRESET: /* unlink */
+ case -ESHUTDOWN: /* disconnect etc */
+ break;
+ case 0:
+ dev->net->stats.tx_bytes += skb->len;
+ }
+ dev->net->stats.tx_packets++;
+
+ spin_lock(&dev->req_lock);
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock(&dev->req_lock);
+ dev_kfree_skb_any(skb);
+
+ atomic_dec(&dev->tx_qlen);
+ if (netif_carrier_ok(dev->net))
+ netif_wake_queue(dev->net);
+}
+
+static inline int is_promisc(u16 cdc_filter)
+{
+ return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+}
+
+static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
+ struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ int length = skb->len;
+ int retval;
+ struct usb_request *req = NULL;
+ unsigned long flags;
+ struct usb_ep *in;
+ u16 cdc_filter;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ in = dev->port_usb->in_ep;
+ cdc_filter = dev->port_usb->cdc_filter;
+ } else {
+ in = NULL;
+ cdc_filter = 0;
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ if (!in) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+
+ /* apply outgoing CDC or RNDIS filters */
+ if (!is_promisc(cdc_filter)) {
+ u8 *dest = skb->data;
+
+ if (is_multicast_ether_addr(dest)) {
+ u16 type;
+
+ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+ * SET_ETHERNET_MULTICAST_FILTERS requests
+ */
+ if (is_broadcast_ether_addr(dest))
+ type = USB_CDC_PACKET_TYPE_BROADCAST;
+ else
+ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+ if (!(cdc_filter & type)) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_OK;
+ }
+ }
+ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+ }
+
+ spin_lock_irqsave(&dev->req_lock, flags);
+ /*
+ * this freelist can be empty if an interrupt triggered disconnect()
+ * and reconfigured the gadget (shutting down this queue) after the
+ * network stack decided to xmit but before we got the spinlock.
+ */
+ if (list_empty(&dev->tx_reqs)) {
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ return NETDEV_TX_BUSY;
+ }
+
+ req = container_of(dev->tx_reqs.next, struct usb_request, list);
+ list_del(&req->list);
+
+ /* temporarily stop TX queue when the freelist empties */
+ if (list_empty(&dev->tx_reqs))
+ netif_stop_queue(net);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+
+ /* no buffer copies needed, unless the network stack did it
+ * or the hardware can't use skb buffers.
+ * or there's not enough space for extra headers we need
+ */
+ if (dev->wrap) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb)
+ skb = dev->wrap(dev->port_usb, skb);
+ spin_unlock_irqrestore(&dev->lock, flags);
+ if (!skb)
+ goto drop;
+
+ length = skb->len;
+ }
+ req->buf = skb->data;
+ req->context = skb;
+ req->complete = tx_complete;
+
+ /* NCM requires no zlp if transfer is dwNtbInMaxSize */
+ if (dev->port_usb->is_fixed &&
+ length == dev->port_usb->fixed_in_len &&
+ (length % in->maxpacket) == 0)
+ req->zero = 0;
+ else
+ req->zero = 1;
+
+ /* use zlp framing on tx for strict CDC-Ether conformance,
+ * though any robust network rx path ignores extra padding.
+ * and some hardware doesn't like to write zlps.
+ */
+ if (req->zero && !dev->zlp && (length % in->maxpacket) == 0)
+ length++;
+
+ req->length = length;
+
+ /* throttle high/super speed IRQ rate back slightly */
+ if (gadget_is_dualspeed(dev->gadget))
+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH ||
+ dev->gadget->speed == USB_SPEED_SUPER)
+ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
+ : 0;
+
+ retval = usb_ep_queue(in, req, GFP_ATOMIC);
+ switch (retval) {
+ default:
+ DBG(dev, "tx queue err %d\n", retval);
+ break;
+ case 0:
+ net->trans_start = jiffies;
+ atomic_inc(&dev->tx_qlen);
+ }
+
+ if (retval) {
+ dev_kfree_skb_any(skb);
+drop:
+ dev->net->stats.tx_dropped++;
+ spin_lock_irqsave(&dev->req_lock, flags);
+ if (list_empty(&dev->tx_reqs))
+ netif_start_queue(net);
+ list_add(&req->list, &dev->tx_reqs);
+ spin_unlock_irqrestore(&dev->req_lock, flags);
+ }
+ return NETDEV_TX_OK;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
+{
+ DBG(dev, "%s\n", __func__);
+
+ /* fill the rx queue */
+ rx_fill(dev, gfp_flags);
+
+ /* and open the tx floodgates */
+ atomic_set(&dev->tx_qlen, 0);
+ netif_wake_queue(dev->net);
+}
+
+static int eth_open(struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ struct gether *link;
+
+ DBG(dev, "%s\n", __func__);
+ if (netif_carrier_ok(dev->net))
+ eth_start(dev, GFP_KERNEL);
+
+ spin_lock_irq(&dev->lock);
+ link = dev->port_usb;
+ if (link && link->open)
+ link->open(link);
+ spin_unlock_irq(&dev->lock);
+
+ return 0;
+}
+
+static int eth_stop(struct net_device *net)
+{
+ struct eth_dev *dev = netdev_priv(net);
+ unsigned long flags;
+
+ VDBG(dev, "%s\n", __func__);
+ netif_stop_queue(net);
+
+ DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+ dev->net->stats.rx_packets, dev->net->stats.tx_packets,
+ dev->net->stats.rx_errors, dev->net->stats.tx_errors
+ );
+
+ /* ensure there are no more active requests */
+ spin_lock_irqsave(&dev->lock, flags);
+ if (dev->port_usb) {
+ struct gether *link = dev->port_usb;
+
+ if (link->close)
+ link->close(link);
+
+ /* NOTE: we have no abort-queue primitive we could use
+ * to cancel all pending I/O. Instead, we disable then
+ * reenable the endpoints ... this idiom may leave toggle
+ * wrong, but that's a self-correcting error.
+ *
+ * REVISIT: we *COULD* just let the transfers complete at
+ * their own pace; the network stack can handle old packets.
+ * For the moment we leave this here, since it works.
+ */
+ usb_ep_disable(link->in_ep);
+ usb_ep_disable(link->out_ep);
+ if (netif_carrier_ok(net)) {
+ DBG(dev, "host still using in/out endpoints\n");
+ usb_ep_enable(link->in_ep);
+ usb_ep_enable(link->out_ep);
+ }
+ }
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
+static char *dev_addr;
+module_param(dev_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
+
+/* this address is invisible to ifconfig */
+static char *host_addr;
+module_param(host_addr, charp, S_IRUGO);
+MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
+
+static int get_ether_addr(const char *str, u8 *dev_addr)
+{
+ if (str) {
+ unsigned i;
+
+ for (i = 0; i < 6; i++) {
+ unsigned char num;
+
+ if ((*str == '.') || (*str == ':'))
+ str++;
+ num = hex_to_bin(*str++) << 4;
+ num |= hex_to_bin(*str++);
+ dev_addr [i] = num;
+ }
+ if (is_valid_ether_addr(dev_addr))
+ return 0;
+ }
+ eth_random_addr(dev_addr);
+ return 1;
+}
+
+static struct eth_dev *the_dev;
+
+static const struct net_device_ops eth_netdev_ops = {
+ .ndo_open = eth_open,
+ .ndo_stop = eth_stop,
+ .ndo_start_xmit = eth_start_xmit,
+ .ndo_change_mtu = ueth_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+ .ndo_validate_addr = eth_validate_addr,
+};
+
+static struct device_type gadget_type = {
+ .name = "gadget",
+};
+
+/**
+ * gether_setup_name - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ * host side of the link is recorded
+ * @netname: name for network device (for example, "usb")
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework. The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns negative errno, or zero on success
+ */
+int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
+ const char *netname)
+{
+ struct eth_dev *dev;
+ struct net_device *net;
+ int status;
+
+ if (the_dev)
+ return -EBUSY;
+
+ net = alloc_etherdev(sizeof *dev);
+ if (!net)
+ return -ENOMEM;
+
+ dev = netdev_priv(net);
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&dev->req_lock);
+ INIT_WORK(&dev->work, eth_work);
+ INIT_LIST_HEAD(&dev->tx_reqs);
+ INIT_LIST_HEAD(&dev->rx_reqs);
+
+ skb_queue_head_init(&dev->rx_frames);
+
+ /* network device setup */
+ dev->net = net;
+ snprintf(net->name, sizeof(net->name), "%s%%d", netname);
+
+ if (get_ether_addr(dev_addr, net->dev_addr))
+ dev_warn(&g->dev,
+ "using random %s ethernet address\n", "self");
+ if (get_ether_addr(host_addr, dev->host_mac))
+ dev_warn(&g->dev,
+ "using random %s ethernet address\n", "host");
+
+ if (ethaddr)
+ memcpy(ethaddr, dev->host_mac, ETH_ALEN);
+
+ net->netdev_ops = ð_netdev_ops;
+
+ SET_ETHTOOL_OPS(net, &ops);
+
+ dev->gadget = g;
+ SET_NETDEV_DEV(net, &g->dev);
+ SET_NETDEV_DEVTYPE(net, &gadget_type);
+
+ status = register_netdev(net);
+ if (status < 0) {
+ dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
+ free_netdev(net);
+ } else {
+ INFO(dev, "MAC %pM\n", net->dev_addr);
+ INFO(dev, "HOST MAC %pM\n", dev->host_mac);
+
+ the_dev = dev;
+
+ /* two kinds of host-initiated state changes:
+ * - iff DATA transfer is active, carrier is "on"
+ * - tx queueing enabled if open *and* carrier is "on"
+ */
+ netif_carrier_off(net);
+ }
+
+ return status;
+}
+
+/**
+ * gether_cleanup - remove Ethernet-over-USB device
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gether_setup().
+ */
+void gether_cleanup(void)
+{
+ if (!the_dev)
+ return;
+
+ unregister_netdev(the_dev->net);
+ flush_work_sync(&the_dev->work);
+ free_netdev(the_dev->net);
+
+ the_dev = NULL;
+}
+
+
+/**
+ * gether_connect - notify network layer that USB link is active
+ * @link: the USB link, set up with endpoints, descriptors matching
+ * current device speed, and any framing wrapper(s) set up.
+ * Context: irqs blocked
+ *
+ * This is called to activate endpoints and let the network layer know
+ * the connection is active ("carrier detect"). It may cause the I/O
+ * queues to open and start letting network packets flow, but will in
+ * any case activate the endpoints so that they respond properly to the
+ * USB host.
+ *
+ * Verify net_device pointer returned using IS_ERR(). If it doesn't
+ * indicate some error code (negative errno), ep->driver_data values
+ * have been overwritten.
+ */
+struct net_device *gether_connect(struct gether *link)
+{
+ struct eth_dev *dev = the_dev;
+ int result = 0;
+
+ if (!dev)
+ return ERR_PTR(-EINVAL);
+
+ link->in_ep->driver_data = dev;
+ result = usb_ep_enable(link->in_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->in_ep->name, result);
+ goto fail0;
+ }
+
+ link->out_ep->driver_data = dev;
+ result = usb_ep_enable(link->out_ep);
+ if (result != 0) {
+ DBG(dev, "enable %s --> %d\n",
+ link->out_ep->name, result);
+ goto fail1;
+ }
+
+ if (result == 0)
+ result = alloc_requests(dev, link, qlen(dev->gadget));
+
+ if (result == 0) {
+ dev->zlp = link->is_zlp_ok;
+ DBG(dev, "qlen %d\n", qlen(dev->gadget));
+
+ dev->header_len = link->header_len;
+ dev->unwrap = link->unwrap;
+ dev->wrap = link->wrap;
+
+ spin_lock(&dev->lock);
+ dev->port_usb = link;
+ link->ioport = dev;
+ if (netif_running(dev->net)) {
+ if (link->open)
+ link->open(link);
+ } else {
+ if (link->close)
+ link->close(link);
+ }
+ spin_unlock(&dev->lock);
+
+ netif_carrier_on(dev->net);
+ if (netif_running(dev->net))
+ eth_start(dev, GFP_ATOMIC);
+
+ /* on error, disable any endpoints */
+ } else {
+ (void) usb_ep_disable(link->out_ep);
+fail1:
+ (void) usb_ep_disable(link->in_ep);
+ }
+fail0:
+ /* caller is responsible for cleanup on error */
+ if (result < 0)
+ return ERR_PTR(result);
+ return dev->net;
+}
+
+/**
+ * gether_disconnect - notify network layer that USB link is inactive
+ * @link: the USB link, on which gether_connect() was called
+ * Context: irqs blocked
+ *
+ * This is called to deactivate endpoints and let the network layer know
+ * the connection went inactive ("no carrier").
+ *
+ * On return, the state is as if gether_connect() had never been called.
+ * The endpoints are inactive, and accordingly without active USB I/O.
+ * Pointers to endpoint descriptors and endpoint private data are nulled.
+ */
+void gether_disconnect(struct gether *link)
+{
+ struct eth_dev *dev = link->ioport;
+ struct usb_request *req;
+
+ WARN_ON(!dev);
+ if (!dev)
+ return;
+
+ DBG(dev, "%s\n", __func__);
+
+ netif_stop_queue(dev->net);
+ netif_carrier_off(dev->net);
+
+ /* disable endpoints, forcing (synchronous) completion
+ * of all pending i/o. then free the request objects
+ * and forget about the endpoints.
+ */
+ usb_ep_disable(link->in_ep);
+ spin_lock(&dev->req_lock);
+ while (!list_empty(&dev->tx_reqs)) {
+ req = container_of(dev->tx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
+
+ spin_unlock(&dev->req_lock);
+ usb_ep_free_request(link->in_ep, req);
+ spin_lock(&dev->req_lock);
+ }
+ spin_unlock(&dev->req_lock);
+ link->in_ep->driver_data = NULL;
+ link->in_ep->desc = NULL;
+
+ usb_ep_disable(link->out_ep);
+ spin_lock(&dev->req_lock);
+ while (!list_empty(&dev->rx_reqs)) {
+ req = container_of(dev->rx_reqs.next,
+ struct usb_request, list);
+ list_del(&req->list);
+
+ spin_unlock(&dev->req_lock);
+ usb_ep_free_request(link->out_ep, req);
+ spin_lock(&dev->req_lock);
+ }
+ spin_unlock(&dev->req_lock);
+ link->out_ep->driver_data = NULL;
+ link->out_ep->desc = NULL;
+
+ /* finish forgetting about this USB link episode */
+ dev->header_len = 0;
+ dev->unwrap = NULL;
+ dev->wrap = NULL;
+
+ spin_lock(&dev->lock);
+ dev->port_usb = NULL;
+ link->ioport = NULL;
+ spin_unlock(&dev->lock);
+}
diff --git a/drivers/staging/ccg/u_ether.h b/drivers/staging/ccg/u_ether.h
new file mode 100644
index 0000000..6f4a1623
--- /dev/null
+++ b/drivers/staging/ccg/u_ether.h
@@ -0,0 +1,154 @@
+/*
+ * u_ether.h -- interface to USB gadget "ethernet link" utilities
+ *
+ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __U_ETHER_H
+#define __U_ETHER_H
+
+#include <linux/err.h>
+#include <linux/if_ether.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+#include "gadget_chips.h"
+
+
+/*
+ * This represents the USB side of an "ethernet" link, managed by a USB
+ * function which provides control and (maybe) framing. Two functions
+ * in different configurations could share the same ethernet link/netdev,
+ * using different host interaction models.
+ *
+ * There is a current limitation that only one instance of this link may
+ * be present in any given configuration. When that's a problem, network
+ * layer facilities can be used to package multiple logical links on this
+ * single "physical" one.
+ */
+struct gether {
+ struct usb_function func;
+
+ /* updated by gether_{connect,disconnect} */
+ struct eth_dev *ioport;
+
+ /* endpoints handle full and/or high speeds */
+ struct usb_ep *in_ep;
+ struct usb_ep *out_ep;
+
+ bool is_zlp_ok;
+
+ u16 cdc_filter;
+
+ /* hooks for added framing, as needed for RNDIS and EEM. */
+ u32 header_len;
+ /* NCM requires fixed size bundles */
+ bool is_fixed;
+ u32 fixed_out_len;
+ u32 fixed_in_len;
+ struct sk_buff *(*wrap)(struct gether *port,
+ struct sk_buff *skb);
+ int (*unwrap)(struct gether *port,
+ struct sk_buff *skb,
+ struct sk_buff_head *list);
+
+ /* called on network open/close */
+ void (*open)(struct gether *);
+ void (*close)(struct gether *);
+};
+
+#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
+ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+ |USB_CDC_PACKET_TYPE_DIRECTED)
+
+/* variant of gether_setup that allows customizing network device name */
+int gether_setup_name(struct usb_gadget *g, u8 ethaddr[ETH_ALEN],
+ const char *netname);
+
+/* netdev setup/teardown as directed by the gadget driver */
+/* gether_setup - initialize one ethernet-over-usb link
+ * @g: gadget to associated with these links
+ * @ethaddr: NULL, or a buffer in which the ethernet address of the
+ * host side of the link is recorded
+ * Context: may sleep
+ *
+ * This sets up the single network link that may be exported by a
+ * gadget driver using this framework. The link layer addresses are
+ * set up using module parameters.
+ *
+ * Returns negative errno, or zero on success
+ */
+static inline int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
+{
+ return gether_setup_name(g, ethaddr, "usb");
+}
+
+void gether_cleanup(void);
+
+/* connect/disconnect is handled by individual functions */
+struct net_device *gether_connect(struct gether *);
+void gether_disconnect(struct gether *);
+
+/* Some controllers can't support CDC Ethernet (ECM) ... */
+static inline bool can_support_ecm(struct usb_gadget *gadget)
+{
+ if (!gadget_supports_altsettings(gadget))
+ return false;
+
+ /* Everything else is *presumably* fine ... but this is a bit
+ * chancy, so be **CERTAIN** there are no hardware issues with
+ * your controller. Add it above if it can't handle CDC.
+ */
+ return true;
+}
+
+/* each configuration may bind one instance of an ethernet link */
+int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int ncm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
+int eem_bind_config(struct usb_configuration *c);
+
+#ifdef USB_ETH_RNDIS
+
+int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer);
+
+#else
+
+static inline int
+rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
+ u32 vendorID, const char *manufacturer)
+{
+ return 0;
+}
+
+#endif
+
+/**
+ * rndis_bind_config - add RNDIS network link to a configuration
+ * @c: the configuration to support the network link
+ * @ethaddr: a buffer in which the ethernet address of the host side
+ * side of the link was recorded
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gether_setup(). Caller is also responsible
+ * for calling @gether_cleanup() before module unload.
+ */
+static inline int rndis_bind_config(struct usb_configuration *c,
+ u8 ethaddr[ETH_ALEN])
+{
+ return rndis_bind_config_vendor(c, ethaddr, 0, NULL);
+}
+
+
+#endif /* __U_ETHER_H */
diff --git a/drivers/staging/ccg/u_serial.c b/drivers/staging/ccg/u_serial.c
new file mode 100644
index 0000000..5b3f5ff
--- /dev/null
+++ b/drivers/staging/ccg/u_serial.c
@@ -0,0 +1,1341 @@
+/*
+ * u_serial.c - utilities for USB gadget "serial port"/TTY support
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This code also borrows from usbserial.c, which is
+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+/* #define VERBOSE_DEBUG */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/slab.h>
+#include <linux/export.h>
+
+#include "u_serial.h"
+
+
+/*
+ * This component encapsulates the TTY layer glue needed to provide basic
+ * "serial port" functionality through the USB gadget stack. Each such
+ * port is exposed through a /dev/ttyGS* node.
+ *
+ * After initialization (gserial_setup), these TTY port devices stay
+ * available until they are removed (gserial_cleanup). Each one may be
+ * connected to a USB function (gserial_connect), or disconnected (with
+ * gserial_disconnect) when the USB host issues a config change event.
+ * Data can only flow when the port is connected to the host.
+ *
+ * A given TTY port can be made available in multiple configurations.
+ * For example, each one might expose a ttyGS0 node which provides a
+ * login application. In one case that might use CDC ACM interface 0,
+ * while another configuration might use interface 3 for that. The
+ * work to handle that (including descriptor management) is not part
+ * of this component.
+ *
+ * Configurations may expose more than one TTY port. For example, if
+ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
+ * for a telephone or fax link. And ttyGS2 might be something that just
+ * needs a simple byte stream interface for some messaging protocol that
+ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
+ */
+
+#define PREFIX "ttyGS"
+
+/*
+ * gserial is the lifecycle interface, used by USB functions
+ * gs_port is the I/O nexus, used by the tty driver
+ * tty_struct links to the tty/filesystem framework
+ *
+ * gserial <---> gs_port ... links will be null when the USB link is
+ * inactive; managed by gserial_{connect,disconnect}(). each gserial
+ * instance can wrap its own USB control protocol.
+ * gserial->ioport == usb_ep->driver_data ... gs_port
+ * gs_port->port_usb ... gserial
+ *
+ * gs_port <---> tty_struct ... links will be null when the TTY file
+ * isn't opened; managed by gs_open()/gs_close()
+ * gserial->port_tty ... tty_struct
+ * tty_struct->driver_data ... gserial
+ */
+
+/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
+ * next layer of buffering. For TX that's a circular buffer; for RX
+ * consider it a NOP. A third layer is provided by the TTY code.
+ */
+#define QUEUE_SIZE 16
+#define WRITE_BUF_SIZE 8192 /* TX only */
+
+/* circular buffer */
+struct gs_buf {
+ unsigned buf_size;
+ char *buf_buf;
+ char *buf_get;
+ char *buf_put;
+};
+
+/*
+ * The port structure holds info for each port, one for each minor number
+ * (and thus for each /dev/ node).
+ */
+struct gs_port {
+ struct tty_port port;
+ spinlock_t port_lock; /* guard port_* access */
+
+ struct gserial *port_usb;
+
+ bool openclose; /* open/close in progress */
+ u8 port_num;
+
+ struct list_head read_pool;
+ int read_started;
+ int read_allocated;
+ struct list_head read_queue;
+ unsigned n_read;
+ struct tasklet_struct push;
+
+ struct list_head write_pool;
+ int write_started;
+ int write_allocated;
+ struct gs_buf port_write_buf;
+ wait_queue_head_t drain_wait; /* wait while writes drain */
+
+ /* REVISIT this state ... */
+ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+};
+
+/* increase N_PORTS if you need more */
+#define N_PORTS 4
+static struct portmaster {
+ struct mutex lock; /* protect open/close */
+ struct gs_port *port;
+} ports[N_PORTS];
+static unsigned n_ports;
+
+#define GS_CLOSE_TIMEOUT 15 /* seconds */
+
+
+
+#ifdef VERBOSE_DEBUG
+#define pr_vdebug(fmt, arg...) \
+ pr_debug(fmt, ##arg)
+#else
+#define pr_vdebug(fmt, arg...) \
+ ({ if (0) pr_debug(fmt, ##arg); })
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* Circular Buffer */
+
+/*
+ * gs_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
+{
+ gb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (gb->buf_buf == NULL)
+ return -ENOMEM;
+
+ gb->buf_size = size;
+ gb->buf_put = gb->buf_buf;
+ gb->buf_get = gb->buf_buf;
+
+ return 0;
+}
+
+/*
+ * gs_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void gs_buf_free(struct gs_buf *gb)
+{
+ kfree(gb->buf_buf);
+ gb->buf_buf = NULL;
+}
+
+/*
+ * gs_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void gs_buf_clear(struct gs_buf *gb)
+{
+ gb->buf_get = gb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+/*
+ * gs_buf_data_avail
+ *
+ * Return the number of bytes of data written into the circular
+ * buffer.
+ */
+static unsigned gs_buf_data_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+}
+
+/*
+ * gs_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned gs_buf_space_avail(struct gs_buf *gb)
+{
+ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+}
+
+/*
+ * gs_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_space_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_put;
+ if (count > len) {
+ memcpy(gb->buf_put, buf, len);
+ memcpy(gb->buf_buf, buf+len, count - len);
+ gb->buf_put = gb->buf_buf + count - len;
+ } else {
+ memcpy(gb->buf_put, buf, count);
+ if (count < len)
+ gb->buf_put += count;
+ else /* count == len */
+ gb->buf_put = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*
+ * gs_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned
+gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(gb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = gb->buf_buf + gb->buf_size - gb->buf_get;
+ if (count > len) {
+ memcpy(buf, gb->buf_get, len);
+ memcpy(buf+len, gb->buf_buf, count - len);
+ gb->buf_get = gb->buf_buf + count - len;
+ } else {
+ memcpy(buf, gb->buf_get, count);
+ if (count < len)
+ gb->buf_get += count;
+ else /* count == len */
+ gb->buf_get = gb->buf_buf;
+ }
+
+ return count;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* I/O glue between TTY (upper) and USB function (lower) driver layers */
+
+/*
+ * gs_alloc_req
+ *
+ * Allocate a usb_request and its buffer. Returns a pointer to the
+ * usb_request or NULL if there is an error.
+ */
+struct usb_request *
+gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, kmalloc_flags);
+
+ if (req != NULL) {
+ req->length = len;
+ req->buf = kmalloc(len, kmalloc_flags);
+ if (req->buf == NULL) {
+ usb_ep_free_request(ep, req);
+ return NULL;
+ }
+ }
+
+ return req;
+}
+
+/*
+ * gs_free_req
+ *
+ * Free a usb_request and its buffer.
+ */
+void gs_free_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+/*
+ * gs_send_packet
+ *
+ * If there is data to send, a packet is built in the given
+ * buffer and the size is returned. If there is no data to
+ * send, 0 is returned.
+ *
+ * Called with port_lock held.
+ */
+static unsigned
+gs_send_packet(struct gs_port *port, char *packet, unsigned size)
+{
+ unsigned len;
+
+ len = gs_buf_data_avail(&port->port_write_buf);
+ if (len < size)
+ size = len;
+ if (size != 0)
+ size = gs_buf_get(&port->port_write_buf, packet, size);
+ return size;
+}
+
+/*
+ * gs_start_tx
+ *
+ * This function finds available write requests, calls
+ * gs_send_packet to fill these packets with data, and
+ * continues until either there are no more write requests
+ * available or no more data to send. This function is
+ * run whenever data arrives or write requests are available.
+ *
+ * Context: caller owns port_lock; port_usb is non-null.
+ */
+static int gs_start_tx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->write_pool;
+ struct usb_ep *in = port->port_usb->in;
+ int status = 0;
+ bool do_tty_wake = false;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int len;
+
+ if (port->write_started >= QUEUE_SIZE)
+ break;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ len = gs_send_packet(port, req->buf, in->maxpacket);
+ if (len == 0) {
+ wake_up_interruptible(&port->drain_wait);
+ break;
+ }
+ do_tty_wake = true;
+
+ req->length = len;
+ list_del(&req->list);
+ req->zero = (gs_buf_data_avail(&port->port_write_buf) == 0);
+
+ pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
+ port->port_num, len, *((u8 *)req->buf),
+ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
+
+ /* Drop lock while we call out of driver; completions
+ * could be issued while we do so. Disconnection may
+ * happen too; maybe immediately before we queue this!
+ *
+ * NOTE that we may keep sending data for a while after
+ * the TTY closed (dev->ioport->port_tty is NULL).
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(in, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", in->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+
+ port->write_started++;
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+
+ if (do_tty_wake && port->port.tty)
+ tty_wakeup(port->port.tty);
+ return status;
+}
+
+/*
+ * Context: caller owns port_lock, and port_usb is set
+ */
+static unsigned gs_start_rx(struct gs_port *port)
+/*
+__releases(&port->port_lock)
+__acquires(&port->port_lock)
+*/
+{
+ struct list_head *pool = &port->read_pool;
+ struct usb_ep *out = port->port_usb->out;
+
+ while (!list_empty(pool)) {
+ struct usb_request *req;
+ int status;
+ struct tty_struct *tty;
+
+ /* no more rx if closed */
+ tty = port->port.tty;
+ if (!tty)
+ break;
+
+ if (port->read_started >= QUEUE_SIZE)
+ break;
+
+ req = list_entry(pool->next, struct usb_request, list);
+ list_del(&req->list);
+ req->length = out->maxpacket;
+
+ /* drop lock while we call out; the controller driver
+ * may need to call us back (e.g. for disconnect)
+ */
+ spin_unlock(&port->port_lock);
+ status = usb_ep_queue(out, req, GFP_ATOMIC);
+ spin_lock(&port->port_lock);
+
+ if (status) {
+ pr_debug("%s: %s %s err %d\n",
+ __func__, "queue", out->name, status);
+ list_add(&req->list, pool);
+ break;
+ }
+ port->read_started++;
+
+ /* abort immediately after disconnect */
+ if (!port->port_usb)
+ break;
+ }
+ return port->read_started;
+}
+
+/*
+ * RX tasklet takes data out of the RX queue and hands it up to the TTY
+ * layer until it refuses to take any more data (or is throttled back).
+ * Then it issues reads for any further data.
+ *
+ * If the RX queue becomes full enough that no usb_request is queued,
+ * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
+ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
+ * can be buffered before the TTY layer's buffers (currently 64 KB).
+ */
+static void gs_rx_push(unsigned long _port)
+{
+ struct gs_port *port = (void *)_port;
+ struct tty_struct *tty;
+ struct list_head *queue = &port->read_queue;
+ bool disconnect = false;
+ bool do_push = false;
+
+ /* hand any queued data to the tty */
+ spin_lock_irq(&port->port_lock);
+ tty = port->port.tty;
+ while (!list_empty(queue)) {
+ struct usb_request *req;
+
+ req = list_first_entry(queue, struct usb_request, list);
+
+ /* discard data if tty was closed */
+ if (!tty)
+ goto recycle;
+
+ /* leave data queued if tty was rx throttled */
+ if (test_bit(TTY_THROTTLED, &tty->flags))
+ break;
+
+ switch (req->status) {
+ case -ESHUTDOWN:
+ disconnect = true;
+ pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
+ break;
+
+ default:
+ /* presumably a transient fault */
+ pr_warning(PREFIX "%d: unexpected RX status %d\n",
+ port->port_num, req->status);
+ /* FALLTHROUGH */
+ case 0:
+ /* normal completion */
+ break;
+ }
+
+ /* push data to (open) tty */
+ if (req->actual) {
+ char *packet = req->buf;
+ unsigned size = req->actual;
+ unsigned n;
+ int count;
+
+ /* we may have pushed part of this packet already... */
+ n = port->n_read;
+ if (n) {
+ packet += n;
+ size -= n;
+ }
+
+ count = tty_insert_flip_string(tty, packet, size);
+ if (count)
+ do_push = true;
+ if (count != size) {
+ /* stop pushing; TTY layer can't handle more */
+ port->n_read += count;
+ pr_vdebug(PREFIX "%d: rx block %d/%d\n",
+ port->port_num,
+ count, req->actual);
+ break;
+ }
+ port->n_read = 0;
+ }
+recycle:
+ list_move(&req->list, &port->read_pool);
+ port->read_started--;
+ }
+
+ /* Push from tty to ldisc; without low_latency set this is handled by
+ * a workqueue, so we won't get callbacks and can hold port_lock
+ */
+ if (tty && do_push)
+ tty_flip_buffer_push(tty);
+
+
+ /* We want our data queue to become empty ASAP, keeping data
+ * in the tty and ldisc (not here). If we couldn't push any
+ * this time around, there may be trouble unless there's an
+ * implicit tty_unthrottle() call on its way...
+ *
+ * REVISIT we should probably add a timer to keep the tasklet
+ * from starving ... but it's not clear that case ever happens.
+ */
+ if (!list_empty(queue) && tty) {
+ if (!test_bit(TTY_THROTTLED, &tty->flags)) {
+ if (do_push)
+ tasklet_schedule(&port->push);
+ else
+ pr_warning(PREFIX "%d: RX not scheduled?\n",
+ port->port_num);
+ }
+ }
+
+ /* If we're still connected, refill the USB RX queue. */
+ if (!disconnect && port->port_usb)
+ gs_start_rx(port);
+
+ spin_unlock_irq(&port->port_lock);
+}
+
+static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gs_port *port = ep->driver_data;
+
+ /* Queue all received data until the tty layer is ready for it. */
+ spin_lock(&port->port_lock);
+ list_add_tail(&req->list, &port->read_queue);
+ tasklet_schedule(&port->push);
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gs_port *port = ep->driver_data;
+
+ spin_lock(&port->port_lock);
+ list_add(&req->list, &port->write_pool);
+ port->write_started--;
+
+ switch (req->status) {
+ default:
+ /* presumably a transient fault */
+ pr_warning("%s: unexpected %s status %d\n",
+ __func__, ep->name, req->status);
+ /* FALL THROUGH */
+ case 0:
+ /* normal completion */
+ gs_start_tx(port);
+ break;
+
+ case -ESHUTDOWN:
+ /* disconnect */
+ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
+ break;
+ }
+
+ spin_unlock(&port->port_lock);
+}
+
+static void gs_free_requests(struct usb_ep *ep, struct list_head *head,
+ int *allocated)
+{
+ struct usb_request *req;
+
+ while (!list_empty(head)) {
+ req = list_entry(head->next, struct usb_request, list);
+ list_del(&req->list);
+ gs_free_req(ep, req);
+ if (allocated)
+ (*allocated)--;
+ }
+}
+
+static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
+ void (*fn)(struct usb_ep *, struct usb_request *),
+ int *allocated)
+{
+ int i;
+ struct usb_request *req;
+ int n = allocated ? QUEUE_SIZE - *allocated : QUEUE_SIZE;
+
+ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
+ * do quite that many this time, don't fail ... we just won't
+ * be as speedy as we might otherwise be.
+ */
+ for (i = 0; i < n; i++) {
+ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+ if (!req)
+ return list_empty(head) ? -ENOMEM : 0;
+ req->complete = fn;
+ list_add_tail(&req->list, head);
+ if (allocated)
+ (*allocated)++;
+ }
+ return 0;
+}
+
+/**
+ * gs_start_io - start USB I/O streams
+ * @dev: encapsulates endpoints to use
+ * Context: holding port_lock; port_tty and port_usb are non-null
+ *
+ * We only start I/O when something is connected to both sides of
+ * this port. If nothing is listening on the host side, we may
+ * be pointlessly filling up our TX buffers and FIFO.
+ */
+static int gs_start_io(struct gs_port *port)
+{
+ struct list_head *head = &port->read_pool;
+ struct usb_ep *ep = port->port_usb->out;
+ int status;
+ unsigned started;
+
+ /* Allocate RX and TX I/O buffers. We can't easily do this much
+ * earlier (with GFP_KERNEL) because the requests are coupled to
+ * endpoints, as are the packet sizes we'll be using. Different
+ * configurations may use different endpoints with a given port;
+ * and high speed vs full speed changes packet sizes too.
+ */
+ status = gs_alloc_requests(ep, head, gs_read_complete,
+ &port->read_allocated);
+ if (status)
+ return status;
+
+ status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
+ gs_write_complete, &port->write_allocated);
+ if (status) {
+ gs_free_requests(ep, head, &port->read_allocated);
+ return status;
+ }
+
+ /* queue read requests */
+ port->n_read = 0;
+ started = gs_start_rx(port);
+
+ /* unblock any pending writes into our circular buffer */
+ if (started) {
+ tty_wakeup(port->port.tty);
+ } else {
+ gs_free_requests(ep, head, &port->read_allocated);
+ gs_free_requests(port->port_usb->in, &port->write_pool,
+ &port->write_allocated);
+ status = -EIO;
+ }
+
+ return status;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* TTY Driver */
+
+/*
+ * gs_open sets up the link between a gs_port and its associated TTY.
+ * That link is broken *only* by TTY close(), and all driver methods
+ * know that.
+ */
+static int gs_open(struct tty_struct *tty, struct file *file)
+{
+ int port_num = tty->index;
+ struct gs_port *port;
+ int status;
+
+ do {
+ mutex_lock(&ports[port_num].lock);
+ port = ports[port_num].port;
+ if (!port)
+ status = -ENODEV;
+ else {
+ spin_lock_irq(&port->port_lock);
+
+ /* already open? Great. */
+ if (port->port.count) {
+ status = 0;
+ port->port.count++;
+
+ /* currently opening/closing? wait ... */
+ } else if (port->openclose) {
+ status = -EBUSY;
+
+ /* ... else we do the work */
+ } else {
+ status = -EAGAIN;
+ port->openclose = true;
+ }
+ spin_unlock_irq(&port->port_lock);
+ }
+ mutex_unlock(&ports[port_num].lock);
+
+ switch (status) {
+ default:
+ /* fully handled */
+ return status;
+ case -EAGAIN:
+ /* must do the work */
+ break;
+ case -EBUSY:
+ /* wait for EAGAIN task to finish */
+ msleep(1);
+ /* REVISIT could have a waitchannel here, if
+ * concurrent open performance is important
+ */
+ break;
+ }
+ } while (status != -EAGAIN);
+
+ /* Do the "real open" */
+ spin_lock_irq(&port->port_lock);
+
+ /* allocate circular buffer on first open */
+ if (port->port_write_buf.buf_buf == NULL) {
+
+ spin_unlock_irq(&port->port_lock);
+ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
+ spin_lock_irq(&port->port_lock);
+
+ if (status) {
+ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
+ port->port_num, tty, file);
+ port->openclose = false;
+ goto exit_unlock_port;
+ }
+ }
+
+ /* REVISIT if REMOVED (ports[].port NULL), abort the open
+ * to let rmmod work faster (but this way isn't wrong).
+ */
+
+ /* REVISIT maybe wait for "carrier detect" */
+
+ tty->driver_data = port;
+ port->port.tty = tty;
+
+ port->port.count = 1;
+ port->openclose = false;
+
+ /* if connected, start the I/O stream */
+ if (port->port_usb) {
+ struct gserial *gser = port->port_usb;
+
+ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+
+ if (gser->connect)
+ gser->connect(gser);
+ }
+
+ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
+
+ status = 0;
+
+exit_unlock_port:
+ spin_unlock_irq(&port->port_lock);
+ return status;
+}
+
+static int gs_writes_finished(struct gs_port *p)
+{
+ int cond;
+
+ /* return true on disconnect or empty buffer */
+ spin_lock_irq(&p->port_lock);
+ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
+ spin_unlock_irq(&p->port_lock);
+
+ return cond;
+}
+
+static void gs_close(struct tty_struct *tty, struct file *file)
+{
+ struct gs_port *port = tty->driver_data;
+ struct gserial *gser;
+
+ spin_lock_irq(&port->port_lock);
+
+ if (port->port.count != 1) {
+ if (port->port.count == 0)
+ WARN_ON(1);
+ else
+ --port->port.count;
+ goto exit;
+ }
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
+
+ /* mark port as closing but in use; we can drop port lock
+ * and sleep if necessary
+ */
+ port->openclose = true;
+ port->port.count = 0;
+
+ gser = port->port_usb;
+ if (gser && gser->disconnect)
+ gser->disconnect(gser);
+
+ /* wait for circular write buffer to drain, disconnect, or at
+ * most GS_CLOSE_TIMEOUT seconds; then discard the rest
+ */
+ if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
+ spin_unlock_irq(&port->port_lock);
+ wait_event_interruptible_timeout(port->drain_wait,
+ gs_writes_finished(port),
+ GS_CLOSE_TIMEOUT * HZ);
+ spin_lock_irq(&port->port_lock);
+ gser = port->port_usb;
+ }
+
+ /* Iff we're disconnected, there can be no I/O in flight so it's
+ * ok to free the circular buffer; else just scrub it. And don't
+ * let the push tasklet fire again until we're re-opened.
+ */
+ if (gser == NULL)
+ gs_buf_free(&port->port_write_buf);
+ else
+ gs_buf_clear(&port->port_write_buf);
+
+ tty->driver_data = NULL;
+ port->port.tty = NULL;
+
+ port->openclose = false;
+
+ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
+ port->port_num, tty, file);
+
+ wake_up_interruptible(&port->port.close_wait);
+exit:
+ spin_unlock_irq(&port->port_lock);
+}
+
+static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
+ port->port_num, tty, count);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (count)
+ count = gs_buf_put(&port->port_write_buf, buf, count);
+ /* treat count == 0 as flush_chars() */
+ if (port->port_usb)
+ status = gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return count;
+}
+
+static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int status;
+
+ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
+ port->port_num, tty, ch, __builtin_return_address(0));
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ status = gs_buf_put(&port->port_write_buf, &ch, 1);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+}
+
+static void gs_flush_chars(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ gs_start_tx(port);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_write_room(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int room = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb)
+ room = gs_buf_space_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
+ port->port_num, tty, room);
+
+ return room;
+}
+
+static int gs_chars_in_buffer(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+ int chars = 0;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ chars = gs_buf_data_avail(&port->port_write_buf);
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+ port->port_num, tty, chars);
+
+ return chars;
+}
+
+/* undo side effects of setting TTY_THROTTLED */
+static void gs_unthrottle(struct tty_struct *tty)
+{
+ struct gs_port *port = tty->driver_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port_usb) {
+ /* Kickstart read queue processing. We don't do xon/xoff,
+ * rts/cts, or other handshaking with the host, but if the
+ * read queue backs up enough we'll be NAKing OUT packets.
+ */
+ tasklet_schedule(&port->push);
+ pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
+
+static int gs_break_ctl(struct tty_struct *tty, int duration)
+{
+ struct gs_port *port = tty->driver_data;
+ int status = 0;
+ struct gserial *gser;
+
+ pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
+ port->port_num, duration);
+
+ spin_lock_irq(&port->port_lock);
+ gser = port->port_usb;
+ if (gser && gser->send_break)
+ status = gser->send_break(gser, duration);
+ spin_unlock_irq(&port->port_lock);
+
+ return status;
+}
+
+static const struct tty_operations gs_tty_ops = {
+ .open = gs_open,
+ .close = gs_close,
+ .write = gs_write,
+ .put_char = gs_put_char,
+ .flush_chars = gs_flush_chars,
+ .write_room = gs_write_room,
+ .chars_in_buffer = gs_chars_in_buffer,
+ .unthrottle = gs_unthrottle,
+ .break_ctl = gs_break_ctl,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static struct tty_driver *gs_tty_driver;
+
+static int
+gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
+{
+ struct gs_port *port;
+
+ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
+ if (port == NULL)
+ return -ENOMEM;
+
+ tty_port_init(&port->port);
+ spin_lock_init(&port->port_lock);
+ init_waitqueue_head(&port->drain_wait);
+
+ tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
+
+ INIT_LIST_HEAD(&port->read_pool);
+ INIT_LIST_HEAD(&port->read_queue);
+ INIT_LIST_HEAD(&port->write_pool);
+
+ port->port_num = port_num;
+ port->port_line_coding = *coding;
+
+ ports[port_num].port = port;
+
+ return 0;
+}
+
+/**
+ * gserial_setup - initialize TTY driver for one or more ports
+ * @g: gadget to associate with these ports
+ * @count: how many ports to support
+ * Context: may sleep
+ *
+ * The TTY stack needs to know in advance how many devices it should
+ * plan to manage. Use this call to set up the ports you will be
+ * exporting through USB. Later, connect them to functions based
+ * on what configuration is activated by the USB host; and disconnect
+ * them as appropriate.
+ *
+ * An example would be a two-configuration device in which both
+ * configurations expose port 0, but through different functions.
+ * One configuration could even expose port 1 while the other
+ * one doesn't.
+ *
+ * Returns negative errno or zero.
+ */
+int gserial_setup(struct usb_gadget *g, unsigned count)
+{
+ unsigned i;
+ struct usb_cdc_line_coding coding;
+ int status;
+
+ if (count == 0 || count > N_PORTS)
+ return -EINVAL;
+
+ gs_tty_driver = alloc_tty_driver(count);
+ if (!gs_tty_driver)
+ return -ENOMEM;
+
+ gs_tty_driver->driver_name = "g_serial";
+ gs_tty_driver->name = PREFIX;
+ /* uses dynamically assigned dev_t values */
+
+ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+ gs_tty_driver->init_termios = tty_std_termios;
+
+ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
+ * MS-Windows. Otherwise, most of these flags shouldn't affect
+ * anything unless we were to actually hook up to a serial line.
+ */
+ gs_tty_driver->init_termios.c_cflag =
+ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ gs_tty_driver->init_termios.c_ispeed = 9600;
+ gs_tty_driver->init_termios.c_ospeed = 9600;
+
+ coding.dwDTERate = cpu_to_le32(9600);
+ coding.bCharFormat = 8;
+ coding.bParityType = USB_CDC_NO_PARITY;
+ coding.bDataBits = USB_CDC_1_STOP_BITS;
+
+ tty_set_operations(gs_tty_driver, &gs_tty_ops);
+
+ /* make devices be openable */
+ for (i = 0; i < count; i++) {
+ mutex_init(&ports[i].lock);
+ status = gs_port_alloc(i, &coding);
+ if (status) {
+ count = i;
+ goto fail;
+ }
+ }
+ n_ports = count;
+
+ /* export the driver ... */
+ status = tty_register_driver(gs_tty_driver);
+ if (status) {
+ pr_err("%s: cannot register, err %d\n",
+ __func__, status);
+ goto fail;
+ }
+
+ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
+ for (i = 0; i < count; i++) {
+ struct device *tty_dev;
+
+ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
+ if (IS_ERR(tty_dev))
+ pr_warning("%s: no classdev for port %d, err %ld\n",
+ __func__, i, PTR_ERR(tty_dev));
+ }
+
+ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
+ count, (count == 1) ? "" : "s");
+
+ return status;
+fail:
+ while (count--)
+ kfree(ports[count].port);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+ return status;
+}
+
+static int gs_closed(struct gs_port *port)
+{
+ int cond;
+
+ spin_lock_irq(&port->port_lock);
+ cond = (port->port.count == 0) && !port->openclose;
+ spin_unlock_irq(&port->port_lock);
+ return cond;
+}
+
+/**
+ * gserial_cleanup - remove TTY-over-USB driver and devices
+ * Context: may sleep
+ *
+ * This is called to free all resources allocated by @gserial_setup().
+ * Accordingly, it may need to wait until some open /dev/ files have
+ * closed.
+ *
+ * The caller must have issued @gserial_disconnect() for any ports
+ * that had previously been connected, so that there is never any
+ * I/O pending when it's called.
+ */
+void gserial_cleanup(void)
+{
+ unsigned i;
+ struct gs_port *port;
+
+ if (!gs_tty_driver)
+ return;
+
+ /* start sysfs and /dev/ttyGS* node removal */
+ for (i = 0; i < n_ports; i++)
+ tty_unregister_device(gs_tty_driver, i);
+
+ for (i = 0; i < n_ports; i++) {
+ /* prevent new opens */
+ mutex_lock(&ports[i].lock);
+ port = ports[i].port;
+ ports[i].port = NULL;
+ mutex_unlock(&ports[i].lock);
+
+ tasklet_kill(&port->push);
+
+ /* wait for old opens to finish */
+ wait_event(port->port.close_wait, gs_closed(port));
+
+ WARN_ON(port->port_usb != NULL);
+
+ kfree(port);
+ }
+ n_ports = 0;
+
+ tty_unregister_driver(gs_tty_driver);
+ put_tty_driver(gs_tty_driver);
+ gs_tty_driver = NULL;
+
+ pr_debug("%s: cleaned up ttyGS* support\n", __func__);
+}
+
+/**
+ * gserial_connect - notify TTY I/O glue that USB link is active
+ * @gser: the function, set up with endpoints and descriptors
+ * @port_num: which port is active
+ * Context: any (usually from irq)
+ *
+ * This is called activate endpoints and let the TTY layer know that
+ * the connection is active ... not unlike "carrier detect". It won't
+ * necessarily start I/O queues; unless the TTY is held open by any
+ * task, there would be no point. However, the endpoints will be
+ * activated so the USB host can perform I/O, subject to basic USB
+ * hardware flow control.
+ *
+ * Caller needs to have set up the endpoints and USB function in @dev
+ * before calling this, as well as the appropriate (speed-specific)
+ * endpoint descriptors, and also have set up the TTY driver by calling
+ * @gserial_setup().
+ *
+ * Returns negative errno or zero.
+ * On success, ep->driver_data will be overwritten.
+ */
+int gserial_connect(struct gserial *gser, u8 port_num)
+{
+ struct gs_port *port;
+ unsigned long flags;
+ int status;
+
+ if (!gs_tty_driver || port_num >= n_ports)
+ return -ENXIO;
+
+ /* we "know" gserial_cleanup() hasn't been called */
+ port = ports[port_num].port;
+
+ /* activate the endpoints */
+ status = usb_ep_enable(gser->in);
+ if (status < 0)
+ return status;
+ gser->in->driver_data = port;
+
+ status = usb_ep_enable(gser->out);
+ if (status < 0)
+ goto fail_out;
+ gser->out->driver_data = port;
+
+ /* then tell the tty glue that I/O can work */
+ spin_lock_irqsave(&port->port_lock, flags);
+ gser->ioport = port;
+ port->port_usb = gser;
+
+ /* REVISIT unclear how best to handle this state...
+ * we don't really couple it with the Linux TTY.
+ */
+ gser->port_line_coding = port->port_line_coding;
+
+ /* REVISIT if waiting on "carrier detect", signal. */
+
+ /* if it's already open, start I/O ... and notify the serial
+ * protocol about open/close status (connect/disconnect).
+ */
+ if (port->port.count) {
+ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
+ gs_start_io(port);
+ if (gser->connect)
+ gser->connect(gser);
+ } else {
+ if (gser->disconnect)
+ gser->disconnect(gser);
+ }
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ return status;
+
+fail_out:
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+ return status;
+}
+
+/**
+ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
+ * @gser: the function, on which gserial_connect() was called
+ * Context: any (usually from irq)
+ *
+ * This is called to deactivate endpoints and let the TTY layer know
+ * that the connection went inactive ... not unlike "hangup".
+ *
+ * On return, the state is as if gserial_connect() had never been called;
+ * there is no active USB I/O on these endpoints.
+ */
+void gserial_disconnect(struct gserial *gser)
+{
+ struct gs_port *port = gser->ioport;
+ unsigned long flags;
+
+ if (!port)
+ return;
+
+ /* tell the TTY glue not to do I/O here any more */
+ spin_lock_irqsave(&port->port_lock, flags);
+
+ /* REVISIT as above: how best to track this? */
+ port->port_line_coding = gser->port_line_coding;
+
+ port->port_usb = NULL;
+ gser->ioport = NULL;
+ if (port->port.count > 0 || port->openclose) {
+ wake_up_interruptible(&port->drain_wait);
+ if (port->port.tty)
+ tty_hangup(port->port.tty);
+ }
+ spin_unlock_irqrestore(&port->port_lock, flags);
+
+ /* disable endpoints, aborting down any active I/O */
+ usb_ep_disable(gser->out);
+ gser->out->driver_data = NULL;
+
+ usb_ep_disable(gser->in);
+ gser->in->driver_data = NULL;
+
+ /* finally, free any unused/unusable I/O buffers */
+ spin_lock_irqsave(&port->port_lock, flags);
+ if (port->port.count == 0 && !port->openclose)
+ gs_buf_free(&port->port_write_buf);
+ gs_free_requests(gser->out, &port->read_pool, NULL);
+ gs_free_requests(gser->out, &port->read_queue, NULL);
+ gs_free_requests(gser->in, &port->write_pool, NULL);
+
+ port->read_allocated = port->read_started =
+ port->write_allocated = port->write_started = 0;
+
+ spin_unlock_irqrestore(&port->port_lock, flags);
+}
diff --git a/drivers/staging/ccg/u_serial.h b/drivers/staging/ccg/u_serial.h
new file mode 100644
index 0000000..9b0fe64
--- /dev/null
+++ b/drivers/staging/ccg/u_serial.h
@@ -0,0 +1,65 @@
+/*
+ * u_serial.h - interface to USB gadget "serial port"/TTY utilities
+ *
+ * Copyright (C) 2008 David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#ifndef __U_SERIAL_H
+#define __U_SERIAL_H
+
+#include <linux/usb/composite.h>
+#include <linux/usb/cdc.h>
+
+/*
+ * One non-multiplexed "serial" I/O port ... there can be several of these
+ * on any given USB peripheral device, if it provides enough endpoints.
+ *
+ * The "u_serial" utility component exists to do one thing: manage TTY
+ * style I/O using the USB peripheral endpoints listed here, including
+ * hookups to sysfs and /dev for each logical "tty" device.
+ *
+ * REVISIT at least ACM could support tiocmget() if needed.
+ *
+ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
+ */
+struct gserial {
+ struct usb_function func;
+
+ /* port is managed by gserial_{connect,disconnect} */
+ struct gs_port *ioport;
+
+ struct usb_ep *in;
+ struct usb_ep *out;
+
+ /* REVISIT avoid this CDC-ACM support harder ... */
+ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
+
+ /* notification callbacks */
+ void (*connect)(struct gserial *p);
+ void (*disconnect)(struct gserial *p);
+ int (*send_break)(struct gserial *p, int duration);
+};
+
+/* utilities to allocate/free request and buffer */
+struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
+void gs_free_req(struct usb_ep *, struct usb_request *req);
+
+/* port setup/teardown is handled by gadget driver */
+int gserial_setup(struct usb_gadget *g, unsigned n_ports);
+void gserial_cleanup(void);
+
+/* connect/disconnect is handled by individual functions */
+int gserial_connect(struct gserial *, u8 port_num);
+void gserial_disconnect(struct gserial *);
+
+/* functions are bound to configurations by a config or gadget driver */
+int acm_bind_config(struct usb_configuration *c, u8 port_num);
+int gser_bind_config(struct usb_configuration *c, u8 port_num);
+int obex_bind_config(struct usb_configuration *c, u8 port_num);
+
+#endif /* __U_SERIAL_H */
diff --git a/drivers/staging/ccg/usbstring.c b/drivers/staging/ccg/usbstring.c
new file mode 100644
index 0000000..4d25b90
--- /dev/null
+++ b/drivers/staging/ccg/usbstring.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2003 David Brownell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/string.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/nls.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+
+/**
+ * usb_gadget_get_string - fill out a string descriptor
+ * @table: of c strings encoded using UTF-8
+ * @id: string id, from low byte of wValue in get string descriptor
+ * @buf: at least 256 bytes, must be 16-bit aligned
+ *
+ * Finds the UTF-8 string matching the ID, and converts it into a
+ * string descriptor in utf16-le.
+ * Returns length of descriptor (always even) or negative errno
+ *
+ * If your driver needs stings in multiple languages, you'll probably
+ * "switch (wIndex) { ... }" in your ep0 string descriptor logic,
+ * using this routine after choosing which set of UTF-8 strings to use.
+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with
+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1
+ * characters (which are also widely used in C strings).
+ */
+int
+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf)
+{
+ struct usb_string *s;
+ int len;
+
+ /* descriptor 0 has the language id */
+ if (id == 0) {
+ buf [0] = 4;
+ buf [1] = USB_DT_STRING;
+ buf [2] = (u8) table->language;
+ buf [3] = (u8) (table->language >> 8);
+ return 4;
+ }
+ for (s = table->strings; s && s->s; s++)
+ if (s->id == id)
+ break;
+
+ /* unrecognized: stall. */
+ if (!s || !s->s)
+ return -EINVAL;
+
+ /* string descriptors have length, tag, then UTF16-LE text */
+ len = min ((size_t) 126, strlen (s->s));
+ len = utf8s_to_utf16s(s->s, len, UTF16_LITTLE_ENDIAN,
+ (wchar_t *) &buf[2], 126);
+ if (len < 0)
+ return -EINVAL;
+ buf [0] = (len + 1) * 2;
+ buf [1] = USB_DT_STRING;
+ return buf [0];
+}
+
diff --git a/drivers/staging/comedi/drivers.c b/drivers/staging/comedi/drivers.c
index c0fdb00..2359151 100644
--- a/drivers/staging/comedi/drivers.c
+++ b/drivers/staging/comedi/drivers.c
@@ -168,7 +168,7 @@
dev->board_ptr = comedi_recognize(driv, it->board_name);
if (dev->board_ptr)
break;
- } else if (strcmp(driv->driver_name, it->board_name))
+ } else if (strcmp(driv->driver_name, it->board_name) == 0)
break;
module_put(driv->module);
}
diff --git a/drivers/staging/comedi/drivers/adv_pci1710.c b/drivers/staging/comedi/drivers/adv_pci1710.c
index 3198660..6b4d0d6 100644
--- a/drivers/staging/comedi/drivers/adv_pci1710.c
+++ b/drivers/staging/comedi/drivers/adv_pci1710.c
@@ -1349,9 +1349,6 @@
}
if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
continue;
- if (pci_is_enabled(pcidev))
- continue;
-
if (strcmp(this_board->name, DRV_NAME) == 0) {
for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
if (pcidev->device == boardtypes[i].device_id) {
diff --git a/drivers/staging/comedi/drivers/adv_pci1723.c b/drivers/staging/comedi/drivers/adv_pci1723.c
index da5ee69..dfde0f6 100644
--- a/drivers/staging/comedi/drivers/adv_pci1723.c
+++ b/drivers/staging/comedi/drivers/adv_pci1723.c
@@ -301,8 +301,6 @@
}
if (pcidev->vendor != PCI_VENDOR_ID_ADVANTECH)
continue;
- if (pci_is_enabled(pcidev))
- continue;
return pcidev;
}
dev_err(dev->class_dev,
diff --git a/drivers/staging/comedi/drivers/adv_pci_dio.c b/drivers/staging/comedi/drivers/adv_pci_dio.c
index 97f06dc..2d4cb7f 100644
--- a/drivers/staging/comedi/drivers/adv_pci_dio.c
+++ b/drivers/staging/comedi/drivers/adv_pci_dio.c
@@ -1064,8 +1064,6 @@
slot != PCI_SLOT(pcidev->devfn))
continue;
}
- if (pci_is_enabled(pcidev))
- continue;
for (i = 0; i < ARRAY_SIZE(boardtypes); ++i) {
if (boardtypes[i].vendor_id != pcidev->vendor)
continue;
diff --git a/drivers/staging/comedi/drivers/daqboard2000.c b/drivers/staging/comedi/drivers/daqboard2000.c
index ef28385..cad559a 100644
--- a/drivers/staging/comedi/drivers/daqboard2000.c
+++ b/drivers/staging/comedi/drivers/daqboard2000.c
@@ -718,7 +718,8 @@
continue;
}
if (pcidev->vendor != PCI_VENDOR_ID_IOTECH ||
- pcidev->device != 0x0409)
+ pcidev->device != 0x0409 ||
+ pcidev->subsystem_device != PCI_VENDOR_ID_IOTECH)
continue;
for (i = 0; i < ARRAY_SIZE(boardtypes); i++) {
@@ -739,6 +740,7 @@
{
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
void *aux_data;
unsigned int aux_len;
int result;
@@ -758,11 +760,12 @@
"failed to enable PCI device and request regions\n");
return -EIO;
}
- dev->iobase = pci_resource_start(pcidev, 2);
+ dev->iobase = 1; /* the "detach" needs this */
- devpriv->plx =
- ioremap(pci_resource_start(pcidev, 0), DAQBOARD2000_PLX_SIZE);
- devpriv->daq = ioremap(dev->iobase, DAQBOARD2000_DAQ_SIZE);
+ pci_base = pci_resource_start(pcidev, 0);
+ devpriv->plx = ioremap(pci_base, DAQBOARD2000_PLX_SIZE);
+ pci_base = pci_resource_start(pcidev, 2);
+ devpriv->daq = ioremap(pci_base, DAQBOARD2000_DAQ_SIZE);
if (!devpriv->plx || !devpriv->daq)
return -ENOMEM;
@@ -799,8 +802,6 @@
printk("Interrupt after is: %x\n", interrupt);
*/
- dev->iobase = (unsigned long)devpriv->daq;
-
dev->board_name = this_board->name;
s = dev->subdevices + 0;
@@ -824,7 +825,7 @@
s = dev->subdevices + 2;
result = subdev_8255_init(dev, s, daqboard2000_8255_cb,
- (unsigned long)(dev->iobase + 0x40));
+ (unsigned long)(devpriv->daq + 0x40));
out:
return result;
diff --git a/drivers/staging/comedi/drivers/dt3000.c b/drivers/staging/comedi/drivers/dt3000.c
index a6fe6c9..3476cda 100644
--- a/drivers/staging/comedi/drivers/dt3000.c
+++ b/drivers/staging/comedi/drivers/dt3000.c
@@ -804,6 +804,7 @@
{
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
int ret = 0;
dev_dbg(dev->class_dev, "dt3000:\n");
@@ -820,9 +821,10 @@
ret = comedi_pci_enable(pcidev, "dt3000");
if (ret < 0)
return ret;
+ dev->iobase = 1; /* the "detach" needs this */
- dev->iobase = pci_resource_start(pcidev, 0);
- devpriv->io_addr = ioremap(dev->iobase, DT3000_SIZE);
+ pci_base = pci_resource_start(pcidev, 0);
+ devpriv->io_addr = ioremap(pci_base, DT3000_SIZE);
if (!devpriv->io_addr)
return -ENOMEM;
diff --git a/drivers/staging/comedi/drivers/rtd520.c b/drivers/staging/comedi/drivers/rtd520.c
index 112fdc3..5aa8be1 100644
--- a/drivers/staging/comedi/drivers/rtd520.c
+++ b/drivers/staging/comedi/drivers/rtd520.c
@@ -1619,9 +1619,8 @@
struct rtdPrivate *devpriv;
struct pci_dev *pcidev;
struct comedi_subdevice *s;
+ resource_size_t pci_base;
int ret;
- resource_size_t physLas1; /* data area */
- resource_size_t physLcfg; /* PLX9080 */
#ifdef USE_DMA
int index;
#endif
@@ -1655,20 +1654,15 @@
printk(KERN_INFO "Failed to enable PCI device and request regions.\n");
return ret;
}
+ dev->iobase = 1; /* the "detach" needs this */
- /*
- * Initialize base addresses
- */
- /* Get the physical address from PCI config */
- dev->iobase = pci_resource_start(pcidev, LAS0_PCIINDEX);
- physLas1 = pci_resource_start(pcidev, LAS1_PCIINDEX);
- physLcfg = pci_resource_start(pcidev, LCFG_PCIINDEX);
- /* Now have the kernel map this into memory */
- /* ASSUME page aligned */
- devpriv->las0 = ioremap_nocache(dev->iobase, LAS0_PCISIZE);
- devpriv->las1 = ioremap_nocache(physLas1, LAS1_PCISIZE);
- devpriv->lcfg = ioremap_nocache(physLcfg, LCFG_PCISIZE);
-
+ /* Initialize the base addresses */
+ pci_base = pci_resource_start(pcidev, LAS0_PCIINDEX);
+ devpriv->las0 = ioremap_nocache(pci_base, LAS0_PCISIZE);
+ pci_base = pci_resource_start(pcidev, LAS1_PCIINDEX);
+ devpriv->las1 = ioremap_nocache(pci_base, LAS1_PCISIZE);
+ pci_base = pci_resource_start(pcidev, LCFG_PCIINDEX);
+ devpriv->lcfg = ioremap_nocache(pci_base, LCFG_PCISIZE);
if (!devpriv->las0 || !devpriv->las1 || !devpriv->lcfg)
return -ENOMEM;
diff --git a/drivers/staging/comedi/drivers/usbdux.c b/drivers/staging/comedi/drivers/usbdux.c
index 848c7ec..11ee836 100644
--- a/drivers/staging/comedi/drivers/usbdux.c
+++ b/drivers/staging/comedi/drivers/usbdux.c
@@ -102,6 +102,7 @@
#define BULK_TIMEOUT 1000
/* constants for "firmware" upload and download */
+#define FIRMWARE "usbdux_firmware.bin"
#define USBDUXSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -2791,7 +2792,7 @@
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbdux_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxsub + index,
@@ -2850,3 +2851,4 @@
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("Stirling/ITL USB-DUX -- Bernd.Porr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxfast.c b/drivers/staging/comedi/drivers/usbduxfast.c
index d991158..8eb41257 100644
--- a/drivers/staging/comedi/drivers/usbduxfast.c
+++ b/drivers/staging/comedi/drivers/usbduxfast.c
@@ -57,6 +57,7 @@
/*
* constants for "firmware" upload and download
*/
+#define FIRMWARE "usbduxfast_firmware.bin"
#define USBDUXFASTSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -1706,7 +1707,7 @@
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbduxfast_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxfastsub + index,
@@ -1774,3 +1775,4 @@
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("USB-DUXfast, BerndPorr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/comedi/drivers/usbduxsigma.c b/drivers/staging/comedi/drivers/usbduxsigma.c
index 543e604..f54ab8c 100644
--- a/drivers/staging/comedi/drivers/usbduxsigma.c
+++ b/drivers/staging/comedi/drivers/usbduxsigma.c
@@ -63,6 +63,7 @@
#define BULK_TIMEOUT 1000
/* constants for "firmware" upload and download */
+#define FIRMWARE "usbduxsigma_firmware.bin"
#define USBDUXSUB_FIRMWARE 0xA0
#define VENDOR_DIR_IN 0xC0
#define VENDOR_DIR_OUT 0x40
@@ -2780,7 +2781,7 @@
ret = request_firmware_nowait(THIS_MODULE,
FW_ACTION_HOTPLUG,
- "usbduxsigma_firmware.bin",
+ FIRMWARE,
&udev->dev,
GFP_KERNEL,
usbduxsub + index,
@@ -2845,3 +2846,4 @@
MODULE_AUTHOR("Bernd Porr, BerndPorr@f2s.com");
MODULE_DESCRIPTION("Stirling/ITL USB-DUX SIGMA -- Bernd.Porr@f2s.com");
MODULE_LICENSE("GPL");
+MODULE_FIRMWARE(FIRMWARE);
diff --git a/drivers/staging/csr/Kconfig b/drivers/staging/csr/Kconfig
index cee8d48..ad2a109 100644
--- a/drivers/staging/csr/Kconfig
+++ b/drivers/staging/csr/Kconfig
@@ -1,6 +1,6 @@
config CSR_WIFI
tristate "CSR wireless driver"
- depends on MMC && CFG80211_WEXT
+ depends on MMC && CFG80211_WEXT && INET
select WIRELESS_EXT
select WEXT_PRIV
help
diff --git a/drivers/staging/iio/adc/ad7192.c b/drivers/staging/iio/adc/ad7192.c
index 22c3923..0958372 100644
--- a/drivers/staging/iio/adc/ad7192.c
+++ b/drivers/staging/iio/adc/ad7192.c
@@ -754,7 +754,7 @@
else
st->mode &= ~AD7192_MODE_ACX;
- ad7192_write_reg(st, AD7192_REG_GPOCON, 3, st->mode);
+ ad7192_write_reg(st, AD7192_REG_MODE, 3, st->mode);
break;
default:
ret = -EINVAL;
@@ -798,6 +798,11 @@
.attrs = ad7195_attributes,
};
+static unsigned int ad7192_get_temp_scale(bool unipolar)
+{
+ return unipolar ? 2815 * 2 : 2815;
+}
+
static int ad7192_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val,
@@ -824,19 +829,6 @@
*val = (smpl >> chan->scan_type.shift) &
((1 << (chan->scan_type.realbits)) - 1);
- switch (chan->type) {
- case IIO_VOLTAGE:
- if (!unipolar)
- *val -= (1 << (chan->scan_type.realbits - 1));
- break;
- case IIO_TEMP:
- *val -= 0x800000;
- *val /= 2815; /* temp Kelvin */
- *val -= 273; /* temp Celsius */
- break;
- default:
- return -EINVAL;
- }
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -848,11 +840,21 @@
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT_PLUS_NANO;
case IIO_TEMP:
- *val = 1000;
- return IIO_VAL_INT;
+ *val = 0;
+ *val2 = 1000000000 / ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT_PLUS_NANO;
default:
return -EINVAL;
}
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP)
+ *val -= 273 * ad7192_get_temp_scale(unipolar);
+ return IIO_VAL_INT;
}
return -EINVAL;
@@ -890,7 +892,7 @@
}
ret = 0;
}
-
+ break;
default:
ret = -EINVAL;
}
@@ -942,20 +944,22 @@
.channel = _chan, \
.channel2 = _chan2, \
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
#define AD7192_CHAN(_chan, _address, _si) \
{ .type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT | \
- IIO_CHAN_INFO_SCALE_SHARED_BIT, \
+ IIO_CHAN_INFO_SCALE_SHARED_BIT | \
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
#define AD7192_CHAN_TEMP(_chan, _address, _si) \
{ .type = IIO_TEMP, \
@@ -965,7 +969,7 @@
IIO_CHAN_INFO_SCALE_SEPARATE_BIT, \
.address = _address, \
.scan_index = _si, \
- .scan_type = IIO_ST('s', 24, 32, 0)}
+ .scan_type = IIO_ST('u', 24, 32, 0)}
static struct iio_chan_spec ad7192_channels[] = {
AD7192_CHAN_DIFF(1, 2, NULL, AD7192_CH_AIN1P_AIN2M, 0),
diff --git a/drivers/staging/iio/adc/ad7298_ring.c b/drivers/staging/iio/adc/ad7298_ring.c
index fd1d855..506016f 100644
--- a/drivers/staging/iio/adc/ad7298_ring.c
+++ b/drivers/staging/iio/adc/ad7298_ring.c
@@ -76,7 +76,7 @@
struct iio_dev *indio_dev = pf->indio_dev;
struct ad7298_state *st = iio_priv(indio_dev);
struct iio_buffer *ring = indio_dev->buffer;
- s64 time_ns;
+ s64 time_ns = 0;
__u16 buf[16];
int b_sent, i;
diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c
index 1ece2ac..19ee49c 100644
--- a/drivers/staging/iio/adc/ad7780.c
+++ b/drivers/staging/iio/adc/ad7780.c
@@ -131,9 +131,10 @@
.indexed = 1,
.channel = 0,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_type = {
- .sign = 's',
+ .sign = 'u',
.realbits = 24,
.storagebits = 32,
.shift = 8,
@@ -146,9 +147,10 @@
.indexed = 1,
.channel = 0,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_type = {
- .sign = 's',
+ .sign = 'u',
.realbits = 20,
.storagebits = 32,
.shift = 12,
diff --git a/drivers/staging/iio/adc/ad7793.c b/drivers/staging/iio/adc/ad7793.c
index 76fdd71..112e2b7 100644
--- a/drivers/staging/iio/adc/ad7793.c
+++ b/drivers/staging/iio/adc/ad7793.c
@@ -563,8 +563,9 @@
return len;
}
-static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available, in-in_scale_available,
- S_IRUGO, ad7793_show_scale_available, NULL, 0);
+static IIO_DEVICE_ATTR_NAMED(in_m_in_scale_available,
+ in_voltage-voltage_scale_available, S_IRUGO,
+ ad7793_show_scale_available, NULL, 0);
static struct attribute *ad7793_attributes[] = {
&iio_dev_attr_sampling_frequency.dev_attr.attr,
@@ -604,9 +605,6 @@
*val = (smpl >> chan->scan_type.shift) &
((1 << (chan->scan_type.realbits)) - 1);
- if (!unipolar)
- *val -= (1 << (chan->scan_type.realbits - 1));
-
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
@@ -620,25 +618,38 @@
return IIO_VAL_INT_PLUS_NANO;
} else {
/* 1170mV / 2^23 * 6 */
- scale_uv = (1170ULL * 100000000ULL * 6ULL)
- >> (chan->scan_type.realbits -
- (unipolar ? 0 : 1));
+ scale_uv = (1170ULL * 100000000ULL * 6ULL);
}
break;
case IIO_TEMP:
- /* Always uses unity gain and internal ref */
- scale_uv = (2500ULL * 100000000ULL)
- >> (chan->scan_type.realbits -
- (unipolar ? 0 : 1));
+ /* 1170mV / 0.81 mV/C / 2^23 */
+ scale_uv = 1444444444444ULL;
break;
default:
return -EINVAL;
}
- *val2 = do_div(scale_uv, 100000000) * 10;
- *val = scale_uv;
-
+ scale_uv >>= (chan->scan_type.realbits - (unipolar ? 0 : 1));
+ *val = 0;
+ *val2 = scale_uv;
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!unipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ /* Kelvin to Celsius */
+ if (chan->type == IIO_TEMP) {
+ unsigned long long offset;
+ unsigned int shift;
+
+ shift = chan->scan_type.realbits - (unipolar ? 0 : 1);
+ offset = 273ULL << shift;
+ do_div(offset, 1444);
+ *val -= offset;
+ }
+ return IIO_VAL_INT;
}
return -EINVAL;
}
@@ -676,7 +687,7 @@
}
ret = 0;
}
-
+ break;
default:
ret = -EINVAL;
}
@@ -720,9 +731,10 @@
.channel2 = 0,
.address = AD7793_CH_AIN1P_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 0,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[1] = {
.type = IIO_VOLTAGE,
@@ -732,9 +744,10 @@
.channel2 = 1,
.address = AD7793_CH_AIN2P_AIN2M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 1,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[2] = {
.type = IIO_VOLTAGE,
@@ -744,9 +757,10 @@
.channel2 = 2,
.address = AD7793_CH_AIN3P_AIN3M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 2,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[3] = {
.type = IIO_VOLTAGE,
@@ -757,9 +771,10 @@
.channel2 = 2,
.address = AD7793_CH_AIN1M_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 3,
- .scan_type = IIO_ST('s', 24, 32, 0)
+ .scan_type = IIO_ST('u', 24, 32, 0)
},
.channel[4] = {
.type = IIO_TEMP,
@@ -769,7 +784,7 @@
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
.scan_index = 4,
- .scan_type = IIO_ST('s', 24, 32, 0),
+ .scan_type = IIO_ST('u', 24, 32, 0),
},
.channel[5] = {
.type = IIO_VOLTAGE,
@@ -778,9 +793,10 @@
.channel = 4,
.address = AD7793_CH_AVDD_MONITOR,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 5,
- .scan_type = IIO_ST('s', 24, 32, 0),
+ .scan_type = IIO_ST('u', 24, 32, 0),
},
.channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
},
@@ -793,9 +809,10 @@
.channel2 = 0,
.address = AD7793_CH_AIN1P_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 0,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[1] = {
.type = IIO_VOLTAGE,
@@ -805,9 +822,10 @@
.channel2 = 1,
.address = AD7793_CH_AIN2P_AIN2M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 1,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[2] = {
.type = IIO_VOLTAGE,
@@ -817,9 +835,10 @@
.channel2 = 2,
.address = AD7793_CH_AIN3P_AIN3M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 2,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[3] = {
.type = IIO_VOLTAGE,
@@ -830,9 +849,10 @@
.channel2 = 2,
.address = AD7793_CH_AIN1M_AIN1M,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SHARED_BIT,
+ IIO_CHAN_INFO_SCALE_SHARED_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 3,
- .scan_type = IIO_ST('s', 16, 32, 0)
+ .scan_type = IIO_ST('u', 16, 32, 0)
},
.channel[4] = {
.type = IIO_TEMP,
@@ -842,7 +862,7 @@
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
.scan_index = 4,
- .scan_type = IIO_ST('s', 16, 32, 0),
+ .scan_type = IIO_ST('u', 16, 32, 0),
},
.channel[5] = {
.type = IIO_VOLTAGE,
@@ -851,9 +871,10 @@
.channel = 4,
.address = AD7793_CH_AVDD_MONITOR,
.info_mask = IIO_CHAN_INFO_RAW_SEPARATE_BIT |
- IIO_CHAN_INFO_SCALE_SEPARATE_BIT,
+ IIO_CHAN_INFO_SCALE_SEPARATE_BIT |
+ IIO_CHAN_INFO_OFFSET_SHARED_BIT,
.scan_index = 5,
- .scan_type = IIO_ST('s', 16, 32, 0),
+ .scan_type = IIO_ST('u', 16, 32, 0),
},
.channel[6] = IIO_CHAN_SOFT_TIMESTAMP(6),
},
@@ -901,7 +922,7 @@
else if (voltage_uv)
st->int_vref_mv = voltage_uv / 1000;
else
- st->int_vref_mv = 2500; /* Build-in ref */
+ st->int_vref_mv = 1170; /* Build-in ref */
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
diff --git a/drivers/staging/keucr/usb.c b/drivers/staging/keucr/usb.c
index 4833034..55a0b82 100644
--- a/drivers/staging/keucr/usb.c
+++ b/drivers/staging/keucr/usb.c
@@ -320,7 +320,7 @@
us->subclass = idesc->bInterfaceSubClass;
us->protocol = idesc->bInterfaceProtocol;
- us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+ us->fflags = id->driver_info;
us->Power_IsResum = false;
if (us->fflags & US_FL_IGNORE_DEVICE)
diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c
index 70f23026..95beb76 100644
--- a/drivers/staging/usbip/usbip_common.c
+++ b/drivers/staging/usbip/usbip_common.c
@@ -157,8 +157,7 @@
dev_dbg(dev, "have_langid %d, string_langid %d\n",
udev->have_langid, udev->string_langid);
- dev_dbg(dev, "maxchild %d, children %p\n",
- udev->maxchild, udev->children);
+ dev_dbg(dev, "maxchild %d\n", udev->maxchild);
}
static void usbip_dump_request_type(__u8 rt)
diff --git a/drivers/staging/vt6656/main_usb.c b/drivers/staging/vt6656/main_usb.c
index b06fd5b..d536756 100644
--- a/drivers/staging/vt6656/main_usb.c
+++ b/drivers/staging/vt6656/main_usb.c
@@ -189,7 +189,7 @@
// Static vars definitions
//
-static struct usb_device_id vt6656_table[] __devinitdata = {
+static struct usb_device_id vt6656_table[] = {
{USB_DEVICE(VNT_USB_VENDOR_ID, VNT_USB_PRODUCT_ID)},
{}
};
diff --git a/drivers/staging/winbond/wbusb.c b/drivers/staging/winbond/wbusb.c
index ef36054..0ca857a 100644
--- a/drivers/staging/winbond/wbusb.c
+++ b/drivers/staging/winbond/wbusb.c
@@ -25,7 +25,7 @@
MODULE_LICENSE("GPL");
MODULE_VERSION("0.1");
-static const struct usb_device_id wb35_table[] __devinitconst = {
+static const struct usb_device_id wb35_table[] = {
{ USB_DEVICE(0x0416, 0x0035) },
{ USB_DEVICE(0x18E8, 0x6201) },
{ USB_DEVICE(0x18E8, 0x6206) },
diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c
index 6e32ff6..5552fa7 100644
--- a/drivers/target/target_core_pscsi.c
+++ b/drivers/target/target_core_pscsi.c
@@ -673,8 +673,15 @@
struct scsi_device *sd = pdv->pdv_sd;
int result;
struct pscsi_plugin_task *pt = cmd->priv;
- unsigned char *cdb = &pt->pscsi_cdb[0];
+ unsigned char *cdb;
+ /*
+ * Special case for REPORT_LUNs handling where pscsi_plugin_task has
+ * not been allocated because TCM is handling the emulation directly.
+ */
+ if (!pt)
+ return 0;
+ cdb = &pt->pscsi_cdb[0];
result = pt->pscsi_result;
/*
* Hack to make sure that Write-Protect modepage is set if R/O mode is
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c
index 0eaae23..4de3186dc 100644
--- a/drivers/target/target_core_transport.c
+++ b/drivers/target/target_core_transport.c
@@ -1165,8 +1165,6 @@
" 0x%02x\n", cmd->se_tfo->get_fabric_name(),
cmd->data_length, size, cmd->t_task_cdb[0]);
- cmd->cmd_spdtl = size;
-
if (cmd->data_direction == DMA_TO_DEVICE) {
pr_err("Rejecting underflow/overflow"
" WRITE data\n");
@@ -2294,9 +2292,9 @@
return 0;
out:
- while (i >= 0) {
- __free_page(sg_page(&cmd->t_data_sg[i]));
+ while (i > 0) {
i--;
+ __free_page(sg_page(&cmd->t_data_sg[i]));
}
kfree(cmd->t_data_sg);
cmd->t_data_sg = NULL;
@@ -2323,9 +2321,12 @@
if (ret < 0)
goto out_fail;
}
-
- /* Workaround for handling zero-length control CDBs */
- if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->data_length) {
+ /*
+ * If this command doesn't have any payload and we don't have to call
+ * into the fabric for data transfers, go ahead and complete it right
+ * away.
+ */
+ if (!cmd->data_length) {
spin_lock_irq(&cmd->t_state_lock);
cmd->t_state = TRANSPORT_COMPLETE;
cmd->transport_state |= CMD_T_ACTIVE;
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h
index c5eb3c3..eea6935 100644
--- a/drivers/target/tcm_fc/tcm_fc.h
+++ b/drivers/target/tcm_fc/tcm_fc.h
@@ -131,6 +131,7 @@
extern struct mutex ft_lport_lock;
extern struct fc4_prov ft_prov;
extern struct target_fabric_configfs *ft_configfs;
+extern unsigned int ft_debug_logging;
/*
* Fabric methods.
diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c
index b9cb500..823e692 100644
--- a/drivers/target/tcm_fc/tfc_cmd.c
+++ b/drivers/target/tcm_fc/tfc_cmd.c
@@ -48,7 +48,7 @@
/*
* Dump cmd state for debugging.
*/
-void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+static void _ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
{
struct fc_exch *ep;
struct fc_seq *sp;
@@ -80,6 +80,12 @@
}
}
+void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)
+{
+ if (unlikely(ft_debug_logging))
+ _ft_dump_cmd(cmd, caller);
+}
+
static void ft_free_cmd(struct ft_cmd *cmd)
{
struct fc_frame *fp;
diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c
index 87901fa..3c9e5b5 100644
--- a/drivers/target/tcm_fc/tfc_sess.c
+++ b/drivers/target/tcm_fc/tfc_sess.c
@@ -456,7 +456,9 @@
struct ft_tport *tport;
mutex_lock(&ft_lport_lock);
- tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
+ tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP],
+ lockdep_is_held(&ft_lport_lock));
+
if (!tport) {
mutex_unlock(&ft_lport_lock);
return;
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 070b442..4720b4b 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -160,10 +160,12 @@
config SERIAL_CLPS711X
tristate "CLPS711X serial port support"
- depends on ARM && ARCH_CLPS711X
+ depends on ARCH_CLPS711X
select SERIAL_CORE
+ default y
help
- ::: To be written :::
+ This enables the driver for the on-chip UARTs of the Cirrus
+ Logic EP711x/EP721x/EP731x processors.
config SERIAL_CLPS711X_CONSOLE
bool "Support for console on CLPS711X serial port"
@@ -173,9 +175,7 @@
Even if you say Y here, the currently visible virtual console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttyCL1". (Try "man bootparam" or see the documentation of
- your boot loader (lilo or loadlin) about how to pass options to the
- kernel at boot time.)
+ "console=ttyCL1".
config SERIAL_SAMSUNG
tristate "Samsung SoC serial support"
diff --git a/drivers/tty/serial/ifx6x60.c b/drivers/tty/serial/ifx6x60.c
index 144cd39..3ad079f 100644
--- a/drivers/tty/serial/ifx6x60.c
+++ b/drivers/tty/serial/ifx6x60.c
@@ -1331,7 +1331,7 @@
MODULE_DEVICE_TABLE(spi, ifx_id_table);
/* spi operations */
-static const struct spi_driver ifx_spi_driver = {
+static struct spi_driver ifx_spi_driver = {
.driver = {
.name = DRVNAME,
.pm = &ifx_spi_pm,
diff --git a/drivers/tty/serial/mxs-auart.c b/drivers/tty/serial/mxs-auart.c
index 2e341b8..3a667ee 100644
--- a/drivers/tty/serial/mxs-auart.c
+++ b/drivers/tty/serial/mxs-auart.c
@@ -73,6 +73,7 @@
#define AUART_CTRL0_CLKGATE (1 << 30)
#define AUART_CTRL2_CTSEN (1 << 15)
+#define AUART_CTRL2_RTSEN (1 << 14)
#define AUART_CTRL2_RTS (1 << 11)
#define AUART_CTRL2_RXE (1 << 9)
#define AUART_CTRL2_TXE (1 << 8)
@@ -259,9 +260,12 @@
u32 ctrl = readl(u->membase + AUART_CTRL2);
- ctrl &= ~AUART_CTRL2_RTS;
- if (mctrl & TIOCM_RTS)
- ctrl |= AUART_CTRL2_RTS;
+ ctrl &= ~AUART_CTRL2_RTSEN;
+ if (mctrl & TIOCM_RTS) {
+ if (u->state->port.flags & ASYNC_CTS_FLOW)
+ ctrl |= AUART_CTRL2_RTSEN;
+ }
+
s->ctrl = mctrl;
writel(ctrl, u->membase + AUART_CTRL2);
}
@@ -359,9 +363,9 @@
/* figure out the hardware flow control settings */
if (cflag & CRTSCTS)
- ctrl2 |= AUART_CTRL2_CTSEN;
+ ctrl2 |= AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN;
else
- ctrl2 &= ~AUART_CTRL2_CTSEN;
+ ctrl2 &= ~(AUART_CTRL2_CTSEN | AUART_CTRL2_RTSEN);
/* set baud rate */
baud = uart_get_baud_rate(u, termios, old, 0, u->uartclk);
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index 654755a..333c8d0 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1348,10 +1348,16 @@
static int pmz_poll_get_char(struct uart_port *port)
{
struct uart_pmac_port *uap = (struct uart_pmac_port *)port;
+ int tries = 2;
- while ((read_zsreg(uap, R0) & Rx_CH_AV) == 0)
- udelay(5);
- return read_zsdata(uap);
+ while (tries) {
+ if ((read_zsreg(uap, R0) & Rx_CH_AV) != 0)
+ return read_zsdata(uap);
+ if (tries--)
+ udelay(5);
+ }
+
+ return NO_POLL_CHAR;
}
static void pmz_poll_put_char(struct uart_port *port, unsigned char c)
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index a7773a3..2564b54 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -13,7 +13,7 @@
default y if PXA3xx
default y if ARCH_EP93XX
default y if ARCH_AT91
- default y if ARCH_PNX4008 && I2C
+ default y if ARCH_PNX4008
default y if MFD_TC6393XB
default y if ARCH_W90X900
default y if ARCH_DAVINCI_DA8XX
@@ -48,6 +48,7 @@
default y if SPARC_LEON
default y if ARCH_MMP
default y if MACH_LOONGSON1
+ default y if PLAT_ORION
default PCI
# some non-PCI HCDs implement xHCI
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index d7e422d..e1f8b2c 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -307,6 +307,34 @@
#define FW_GET_BYTE(p) (*((__u8 *) (p)))
#define FW_DIR "ueagle-atm/"
+#define EAGLE_FIRMWARE FW_DIR "eagle.fw"
+#define ADI930_FIRMWARE FW_DIR "adi930.fw"
+#define EAGLE_I_FIRMWARE FW_DIR "eagleI.fw"
+#define EAGLE_II_FIRMWARE FW_DIR "eagleII.fw"
+#define EAGLE_III_FIRMWARE FW_DIR "eagleIII.fw"
+#define EAGLE_IV_FIRMWARE FW_DIR "eagleIV.fw"
+
+#define DSP4I_FIRMWARE FW_DIR "DSP4i.bin"
+#define DSP4P_FIRMWARE FW_DIR "DSP4p.bin"
+#define DSP9I_FIRMWARE FW_DIR "DSP9i.bin"
+#define DSP9P_FIRMWARE FW_DIR "DSP9p.bin"
+#define DSPEI_FIRMWARE FW_DIR "DSPei.bin"
+#define DSPEP_FIRMWARE FW_DIR "DSPep.bin"
+#define FPGA930_FIRMWARE FW_DIR "930-fpga.bin"
+
+#define CMV4P_FIRMWARE FW_DIR "CMV4p.bin"
+#define CMV4PV2_FIRMWARE FW_DIR "CMV4p.bin.v2"
+#define CMV4I_FIRMWARE FW_DIR "CMV4i.bin"
+#define CMV4IV2_FIRMWARE FW_DIR "CMV4i.bin.v2"
+#define CMV9P_FIRMWARE FW_DIR "CMV9p.bin"
+#define CMV9PV2_FIRMWARE FW_DIR "CMV9p.bin.v2"
+#define CMV9I_FIRMWARE FW_DIR "CMV9i.bin"
+#define CMV9IV2_FIRMWARE FW_DIR "CMV9i.bin.v2"
+#define CMVEP_FIRMWARE FW_DIR "CMVep.bin"
+#define CMVEPV2_FIRMWARE FW_DIR "CMVep.bin.v2"
+#define CMVEI_FIRMWARE FW_DIR "CMVei.bin"
+#define CMVEIV2_FIRMWARE FW_DIR "CMVei.bin.v2"
+
#define UEA_FW_NAME_MAX 30
#define NB_MODEM 4
@@ -694,26 +722,26 @@
static int uea_load_firmware(struct usb_device *usb, unsigned int ver)
{
int ret;
- char *fw_name = FW_DIR "eagle.fw";
+ char *fw_name = EAGLE_FIRMWARE;
uea_enters(usb);
uea_info(usb, "pre-firmware device, uploading firmware\n");
switch (ver) {
case ADI930:
- fw_name = FW_DIR "adi930.fw";
+ fw_name = ADI930_FIRMWARE;
break;
case EAGLE_I:
- fw_name = FW_DIR "eagleI.fw";
+ fw_name = EAGLE_I_FIRMWARE;
break;
case EAGLE_II:
- fw_name = FW_DIR "eagleII.fw";
+ fw_name = EAGLE_II_FIRMWARE;
break;
case EAGLE_III:
- fw_name = FW_DIR "eagleIII.fw";
+ fw_name = EAGLE_III_FIRMWARE;
break;
case EAGLE_IV:
- fw_name = FW_DIR "eagleIV.fw";
+ fw_name = EAGLE_IV_FIRMWARE;
break;
}
@@ -869,19 +897,19 @@
if (UEA_CHIP_VERSION(sc) == EAGLE_IV) {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSP4i.bin";
+ dsp_name = DSP4I_FIRMWARE;
else
- dsp_name = FW_DIR "DSP4p.bin";
+ dsp_name = DSP4P_FIRMWARE;
} else if (UEA_CHIP_VERSION(sc) == ADI930) {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSP9i.bin";
+ dsp_name = DSP9I_FIRMWARE;
else
- dsp_name = FW_DIR "DSP9p.bin";
+ dsp_name = DSP9P_FIRMWARE;
} else {
if (IS_ISDN(sc))
- dsp_name = FW_DIR "DSPei.bin";
+ dsp_name = DSPEI_FIRMWARE;
else
- dsp_name = FW_DIR "DSPep.bin";
+ dsp_name = DSPEP_FIRMWARE;
}
ret = request_firmware(&sc->dsp_firm, dsp_name, &sc->usb_dev->dev);
@@ -1925,7 +1953,7 @@
int ret, size, u, ln;
const u8 *pfw;
u8 value;
- char *fw_name = FW_DIR "930-fpga.bin";
+ char *fw_name = FPGA930_FIRMWARE;
uea_enters(INS_TO_USBDEV(sc));
@@ -2753,3 +2781,28 @@
MODULE_AUTHOR("Damien Bergamini/Matthieu Castet/Stanislaw W. Gruszka");
MODULE_DESCRIPTION("ADI 930/Eagle USB ADSL Modem driver");
MODULE_LICENSE("Dual BSD/GPL");
+MODULE_FIRMWARE(EAGLE_FIRMWARE);
+MODULE_FIRMWARE(ADI930_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_I_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_II_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_III_FIRMWARE);
+MODULE_FIRMWARE(EAGLE_IV_FIRMWARE);
+MODULE_FIRMWARE(DSP4I_FIRMWARE);
+MODULE_FIRMWARE(DSP4P_FIRMWARE);
+MODULE_FIRMWARE(DSP9I_FIRMWARE);
+MODULE_FIRMWARE(DSP9P_FIRMWARE);
+MODULE_FIRMWARE(DSPEI_FIRMWARE);
+MODULE_FIRMWARE(DSPEP_FIRMWARE);
+MODULE_FIRMWARE(FPGA930_FIRMWARE);
+MODULE_FIRMWARE(CMV4P_FIRMWARE);
+MODULE_FIRMWARE(CMV4PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV4I_FIRMWARE);
+MODULE_FIRMWARE(CMV4IV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9P_FIRMWARE);
+MODULE_FIRMWARE(CMV9PV2_FIRMWARE);
+MODULE_FIRMWARE(CMV9I_FIRMWARE);
+MODULE_FIRMWARE(CMV9IV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEP_FIRMWARE);
+MODULE_FIRMWARE(CMVEPV2_FIRMWARE);
+MODULE_FIRMWARE(CMVEI_FIRMWARE);
+MODULE_FIRMWARE(CMVEIV2_FIRMWARE);
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
index 8337fb5..1ea932a 100644
--- a/drivers/usb/chipidea/Kconfig
+++ b/drivers/usb/chipidea/Kconfig
@@ -1,9 +1,9 @@
config USB_CHIPIDEA
tristate "ChipIdea Highspeed Dual Role Controller"
- depends on USB
+ depends on USB || USB_GADGET
help
- Say Y here if your system has a dual role high speed USB
- controller based on ChipIdea silicon IP. Currently, only the
+ Say Y here if your system has a dual role high speed USB
+ controller based on ChipIdea silicon IP. Currently, only the
peripheral mode is supported.
When compiled dynamically, the module will be called ci-hdrc.ko.
@@ -12,14 +12,14 @@
config USB_CHIPIDEA_UDC
bool "ChipIdea device controller"
- depends on USB_GADGET
- select USB_GADGET_DUALSPEED
+ depends on USB_GADGET=y || USB_GADGET=USB_CHIPIDEA
help
Say Y here to enable device controller functionality of the
ChipIdea driver.
config USB_CHIPIDEA_HOST
bool "ChipIdea host controller"
+ depends on USB=y || USB=USB_CHIPIDEA
select USB_EHCI_ROOT_HUB_TT
help
Say Y here to enable host controller functionality of the
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 56d6bf6..344a51d 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -39,7 +39,6 @@
#include <linux/serial.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
-#include <linux/serial.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/uaccess.h>
@@ -1104,7 +1103,8 @@
}
- if (data_interface->cur_altsetting->desc.bNumEndpoints < 2)
+ if (data_interface->cur_altsetting->desc.bNumEndpoints < 2 ||
+ control_interface->cur_altsetting->desc.bNumEndpoints == 0)
return -EINVAL;
epctrl = &control_interface->cur_altsetting->endpoint[0].desc;
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index f4bdd0c..7199adc 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -702,6 +702,8 @@
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s: %d\n", cfgno, "start", result);
+ if (result != -EPIPE)
+ goto err;
dev_err(ddev, "chopping to %d config(s)\n", cfgno);
dev->descriptor.bNumConfigurations = cfgno;
break;
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index d956965..f4ead12 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -496,6 +496,7 @@
char *pages_start, *data_end, *speed;
unsigned int length;
ssize_t total_written = 0;
+ struct usb_device *childdev = NULL;
/* don't bother with anything else if we're not writing any data */
if (*nbytes <= 0)
@@ -589,14 +590,12 @@
free_pages((unsigned long)pages_start, 1);
/* Now look at all of this device's children. */
- for (chix = 0; chix < usbdev->maxchild; chix++) {
- struct usb_device *childdev = usbdev->children[chix];
-
+ usb_hub_for_each_child(usbdev, chix, childdev) {
if (childdev) {
usb_lock_device(childdev);
ret = usb_device_dump(buffer, nbytes, skip_bytes,
file_offset, childdev, bus,
- level + 1, chix, ++cnt);
+ level + 1, chix - 1, ++cnt);
usb_unlock_device(childdev);
if (ret == -EFAULT)
return total_written;
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index ebb8a9d..e0356cb 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -1928,6 +1928,38 @@
return 0;
}
+static int proc_disconnect_claim(struct dev_state *ps, void __user *arg)
+{
+ struct usbdevfs_disconnect_claim dc;
+ struct usb_interface *intf;
+
+ if (copy_from_user(&dc, arg, sizeof(dc)))
+ return -EFAULT;
+
+ intf = usb_ifnum_to_if(ps->dev, dc.interface);
+ if (!intf)
+ return -EINVAL;
+
+ if (intf->dev.driver) {
+ struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+ if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER) &&
+ strncmp(dc.driver, intf->dev.driver->name,
+ sizeof(dc.driver)) != 0)
+ return -EBUSY;
+
+ if ((dc.flags & USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER) &&
+ strncmp(dc.driver, intf->dev.driver->name,
+ sizeof(dc.driver)) == 0)
+ return -EBUSY;
+
+ dev_dbg(&intf->dev, "disconnect by usbfs\n");
+ usb_driver_release_interface(driver, intf);
+ }
+
+ return claimintf(ps, dc.interface);
+}
+
/*
* NOTE: All requests here that have interface numbers as parameters
* are assuming that somehow the configuration has been prevented from
@@ -2101,6 +2133,9 @@
case USBDEVFS_GET_CAPABILITIES:
ret = proc_get_capabilities(ps, p);
break;
+ case USBDEVFS_DISCONNECT_CLAIM:
+ ret = proc_disconnect_claim(ps, p);
+ break;
}
usb_unlock_device(dev);
if (ret >= 0)
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index db7fe50..68cc653 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -24,10 +24,6 @@
#define to_ep_device(_dev) \
container_of(_dev, struct ep_device, dev)
-struct device_type usb_ep_device_type = {
- .name = "usb_endpoint",
-};
-
struct ep_attribute {
struct attribute attr;
ssize_t (*show)(struct usb_device *,
@@ -172,6 +168,11 @@
kfree(ep_dev);
}
+struct device_type usb_ep_device_type = {
+ .name = "usb_endpoint",
+ .release = ep_device_release,
+};
+
int usb_create_ep_devs(struct device *parent,
struct usb_host_endpoint *endpoint,
struct usb_device *udev)
@@ -190,7 +191,6 @@
ep_dev->dev.groups = ep_dev_groups;
ep_dev->dev.type = &usb_ep_device_type;
ep_dev->dev.parent = parent;
- ep_dev->dev.release = ep_device_release;
dev_set_name(&ep_dev->dev, "ep_%02x", endpoint->desc.bEndpointAddress);
retval = device_register(&ep_dev->dev);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bc84106..35b52f6 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -22,6 +22,7 @@
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/bcd.h>
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
@@ -123,9 +124,8 @@
*/
/*-------------------------------------------------------------------------*/
-
-#define KERNEL_REL ((LINUX_VERSION_CODE >> 16) & 0x0ff)
-#define KERNEL_VER ((LINUX_VERSION_CODE >> 8) & 0x0ff)
+#define KERNEL_REL bin2bcd(((LINUX_VERSION_CODE >> 16) & 0x0ff))
+#define KERNEL_VER bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
/* usb 3.0 root hub device descriptor */
static const u8 usb3_rh_dev_descriptor[18] = {
@@ -2153,15 +2153,8 @@
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- unsigned long flags;
irqreturn_t rc;
- /* IRQF_DISABLED doesn't work correctly with shared IRQs
- * when the first handler doesn't use it. So let's just
- * assume it's never used.
- */
- local_irq_save(flags);
-
if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
rc = IRQ_NONE;
else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2169,7 +2162,6 @@
else
rc = IRQ_HANDLED;
- local_irq_restore(flags);
return rc;
}
EXPORT_SYMBOL_GPL(usb_hcd_irq);
@@ -2357,14 +2349,6 @@
int retval;
if (hcd->driver->irq) {
-
- /* IRQF_DISABLED doesn't work as advertised when used together
- * with IRQF_SHARED. As usb_hcd_irq() will always disable
- * interrupts we can remove it here.
- */
- if (irqflags & IRQF_SHARED)
- irqflags &= ~IRQF_DISABLED;
-
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 128a804..aa45e43 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -39,6 +39,19 @@
#endif
#endif
+enum port_power_policy {
+ USB_PORT_POWER_ON = 0,
+ USB_PORT_POWER_OFF,
+};
+
+struct usb_port {
+ struct usb_device *child;
+ struct device dev;
+ struct dev_state *port_owner;
+ enum usb_port_connect_type connect_type;
+ enum port_power_policy port_power_policy;
+};
+
struct usb_hub {
struct device *intfdev; /* the "interface" device */
struct usb_device *hdev;
@@ -83,9 +96,13 @@
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
- struct dev_state **port_owners;
+ struct usb_port **ports;
};
+static const char on_string[] = "on";
+static const char off_string[] = "off";
+static const struct attribute_group *port_dev_group[];
+
static inline int hub_is_superspeed(struct usb_device *hdev)
{
return (hdev->descriptor.bDeviceProtocol == USB_HUB_PR_SS);
@@ -156,6 +173,8 @@
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
+#define to_usb_port(_dev) \
+ container_of(_dev, struct usb_port, dev)
static int usb_reset_and_verify_device(struct usb_device *udev);
@@ -174,7 +193,7 @@
/* Note that hdev or one of its children must be locked! */
static struct usb_hub *hdev_to_hub(struct usb_device *hdev)
{
- if (!hdev || !hdev->actconfig)
+ if (!hdev || !hdev->actconfig || !hdev->maxchild)
return NULL;
return usb_get_intfdata(hdev->actconfig->interface[0]);
}
@@ -836,7 +855,12 @@
dev_dbg(hub->intfdev, "trying to enable port power on "
"non-switchable hub\n");
for (port1 = 1; port1 <= hub->descriptor->bNbrPorts; port1++)
- set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
+ if (hub->ports[port1 - 1]->port_power_policy
+ == USB_PORT_POWER_ON)
+ set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER);
+ else
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_POWER);
/* Wait at least 100 msec for power to become stable */
delay = max(pgood_delay, (unsigned) 100);
@@ -869,8 +893,8 @@
struct usb_device *hdev = hub->hdev;
int ret = 0;
- if (hdev->children[port1-1] && set_state)
- usb_set_device_state(hdev->children[port1-1],
+ if (hub->ports[port1 - 1]->child && set_state)
+ usb_set_device_state(hub->ports[port1 - 1]->child,
USB_STATE_NOTATTACHED);
if (!hub->error && !hub_is_superspeed(hub->hdev))
ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
@@ -1026,7 +1050,7 @@
* which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *udev = hdev->children[port1-1];
+ struct usb_device *udev = hub->ports[port1 - 1]->child;
u16 portstatus, portchange;
portstatus = portchange = 0;
@@ -1191,8 +1215,8 @@
if (type != HUB_SUSPEND) {
/* Disconnect all the children */
for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
+ if (hub->ports[i]->child)
+ usb_disconnect(&hub->ports[i]->child);
}
}
@@ -1222,6 +1246,53 @@
return 0;
}
+static void usb_port_device_release(struct device *dev)
+{
+ struct usb_port *port_dev = to_usb_port(dev);
+
+ kfree(port_dev);
+}
+
+static void usb_hub_remove_port_device(struct usb_hub *hub,
+ int port1)
+{
+ device_unregister(&hub->ports[port1 - 1]->dev);
+}
+
+struct device_type usb_port_device_type = {
+ .name = "usb_port",
+ .release = usb_port_device_release,
+};
+
+static int usb_hub_create_port_device(struct usb_hub *hub,
+ int port1)
+{
+ struct usb_port *port_dev = NULL;
+ int retval;
+
+ port_dev = kzalloc(sizeof(*port_dev), GFP_KERNEL);
+ if (!port_dev) {
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ hub->ports[port1 - 1] = port_dev;
+ port_dev->dev.parent = hub->intfdev;
+ port_dev->dev.groups = port_dev_group;
+ port_dev->dev.type = &usb_port_device_type;
+ dev_set_name(&port_dev->dev, "port%d", port1);
+
+ retval = device_register(&port_dev->dev);
+ if (retval)
+ goto error_register;
+ return 0;
+
+error_register:
+ put_device(&port_dev->dev);
+exit:
+ return retval;
+}
+
static int hub_configure(struct usb_hub *hub,
struct usb_endpoint_descriptor *endpoint)
{
@@ -1231,7 +1302,7 @@
u16 hubstatus, hubchange;
u16 wHubCharacteristics;
unsigned int pipe;
- int maxp, ret;
+ int maxp, ret, i;
char *message = "out of memory";
hub->buffer = kmalloc(sizeof(*hub->buffer), GFP_KERNEL);
@@ -1271,11 +1342,9 @@
dev_info (hub_dev, "%d port%s detected\n", hdev->maxchild,
(hdev->maxchild == 1) ? "" : "s");
- hdev->children = kzalloc(hdev->maxchild *
- sizeof(struct usb_device *), GFP_KERNEL);
- hub->port_owners = kzalloc(hdev->maxchild * sizeof(struct dev_state *),
- GFP_KERNEL);
- if (!hdev->children || !hub->port_owners) {
+ hub->ports = kzalloc(hdev->maxchild * sizeof(struct usb_port *),
+ GFP_KERNEL);
+ if (!hub->ports) {
ret = -ENOMEM;
goto fail;
}
@@ -1484,6 +1553,11 @@
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
+ for (i = 0; i < hdev->maxchild; i++)
+ if (usb_hub_create_port_device(hub, i + 1) < 0)
+ dev_err(hub->intfdev,
+ "couldn't create port%d device.\n", i + 1);
+
hub_activate(hub, HUB_INIT);
return 0;
@@ -1508,6 +1582,10 @@
{
struct usb_hub *hub = usb_get_intfdata(intf);
struct usb_device *hdev = interface_to_usbdev(intf);
+ int i;
+
+ for (i = 0; i < hdev->maxchild; i++)
+ usb_hub_remove_port_device(hub, i + 1);
/* Take the hub off the event list and don't let it be added again */
spin_lock_irq(&hub_event_lock);
@@ -1529,8 +1607,7 @@
highspeed_hubs--;
usb_free_urb(hub->urb);
- kfree(hdev->children);
- kfree(hub->port_owners);
+ kfree(hub->ports);
kfree(hub->descriptor);
kfree(hub->status);
kfree(hub->buffer);
@@ -1617,6 +1694,7 @@
hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data)
{
struct usb_device *hdev = interface_to_usbdev (intf);
+ struct usb_hub *hub = hdev_to_hub(hdev);
/* assert ifno == 0 (part of hub spec) */
switch (code) {
@@ -1630,11 +1708,11 @@
else {
info->nports = hdev->maxchild;
for (i = 0; i < info->nports; i++) {
- if (hdev->children[i] == NULL)
+ if (hub->ports[i]->child == NULL)
info->port[i] = 0;
else
info->port[i] =
- hdev->children[i]->devnum;
+ hub->ports[i]->child->devnum;
}
}
spin_unlock_irq(&device_state_lock);
@@ -1662,7 +1740,7 @@
/* This assumes that devices not managed by the hub driver
* will always have maxchild equal to 0.
*/
- *ppowner = &(hdev_to_hub(hdev)->port_owners[port1 - 1]);
+ *ppowner = &(hdev_to_hub(hdev)->ports[port1 - 1]->port_owner);
return 0;
}
@@ -1699,16 +1777,14 @@
void usb_hub_release_all_ports(struct usb_device *hdev, struct dev_state *owner)
{
+ struct usb_hub *hub = hdev_to_hub(hdev);
int n;
- struct dev_state **powner;
- n = find_port_owner(hdev, 1, &powner);
- if (n == 0) {
- for (; n < hdev->maxchild; (++n, ++powner)) {
- if (*powner == owner)
- *powner = NULL;
- }
+ for (n = 0; n < hdev->maxchild; n++) {
+ if (hub->ports[n]->port_owner == owner)
+ hub->ports[n]->port_owner = NULL;
}
+
}
/* The caller must hold udev's lock */
@@ -1719,17 +1795,17 @@
if (udev->state == USB_STATE_NOTATTACHED || !udev->parent)
return false;
hub = hdev_to_hub(udev->parent);
- return !!hub->port_owners[udev->portnum - 1];
+ return !!hub->ports[udev->portnum - 1]->port_owner;
}
-
static void recursively_mark_NOTATTACHED(struct usb_device *udev)
{
+ struct usb_hub *hub = hdev_to_hub(udev);
int i;
for (i = 0; i < udev->maxchild; ++i) {
- if (udev->children[i])
- recursively_mark_NOTATTACHED(udev->children[i]);
+ if (hub->ports[i]->child)
+ recursively_mark_NOTATTACHED(hub->ports[i]->child);
}
if (udev->state == USB_STATE_SUSPENDED)
udev->active_duration -= jiffies;
@@ -1893,6 +1969,7 @@
void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *udev = *pdev;
+ struct usb_hub *hub = hdev_to_hub(udev);
int i;
/* mark the device as inactive, so any further urb submissions for
@@ -1907,8 +1984,8 @@
/* Free up all the children before we remove this device */
for (i = 0; i < udev->maxchild; i++) {
- if (udev->children[i])
- usb_disconnect(&udev->children[i]);
+ if (hub->ports[i]->child)
+ usb_disconnect(&hub->ports[i]->child);
}
/* deallocate hcd/hardware state ... nuking all pending urbs and
@@ -2113,7 +2190,8 @@
return;
if (hub_is_superspeed(hdev)) {
- if (hub->descriptor->u.ss.DeviceRemovable & (1 << port))
+ if (le16_to_cpu(hub->descriptor->u.ss.DeviceRemovable)
+ & (1 << port))
removable = false;
} else {
if (hub->descriptor->u.hs.DeviceRemovable[port / 8] & (1 << (port % 8)))
@@ -2566,6 +2644,25 @@
return ret;
}
+static int usb_get_hub_port_power_state(struct usb_device *hdev, int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+ struct usb_port_status data;
+ u16 portstatus;
+ int ret;
+
+ ret = get_port_status(hub->hdev, port1, &data);
+ if (ret < 4) {
+ dev_err(hub->intfdev,
+ "%s failed (err = %d)\n", __func__, ret);
+ if (ret >= 0)
+ ret = -EIO;
+ return ret;
+ } else
+ portstatus = le16_to_cpu(data.wPortStatus);
+ return port_is_power_on(hub, portstatus);
+}
+
#ifdef CONFIG_PM
/* Check if a port is suspended(USB2.0 port) or in U3 state(USB3.0 port) */
@@ -3072,7 +3169,7 @@
for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev;
- udev = hdev->children [port1-1];
+ udev = hub->ports[port1 - 1]->child;
if (udev && udev->can_submit) {
dev_warn(&intf->dev, "port %d nyet suspended\n", port1);
if (PMSG_IS_AUTO(msg))
@@ -3999,7 +4096,7 @@
remaining = hdev->bus_mA - hub->descriptor->bHubContrCurrent;
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
- struct usb_device *udev = hdev->children[port1 - 1];
+ struct usb_device *udev = hub->ports[port1 - 1]->child;
int delta;
if (!udev)
@@ -4063,7 +4160,7 @@
#endif
/* Try to resuscitate an existing device */
- udev = hdev->children[port1-1];
+ udev = hub->ports[port1 - 1]->child;
if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
udev->state != USB_STATE_NOTATTACHED) {
usb_lock_device(udev);
@@ -4092,7 +4189,7 @@
/* Disconnect any existing devices under this port */
if (udev)
- usb_disconnect(&hdev->children[port1-1]);
+ usb_disconnect(&hub->ports[port1 - 1]->child);
clear_bit(port1, hub->change_bits);
/* We can forget about a "removed" device when there's a physical
@@ -4228,7 +4325,7 @@
if (hdev->state == USB_STATE_NOTATTACHED)
status = -ENOTCONN;
else
- hdev->children[port1-1] = udev;
+ hub->ports[port1 - 1]->child = udev;
spin_unlock_irq(&device_state_lock);
/* Run it through the hoops (find a driver, etc) */
@@ -4236,7 +4333,7 @@
status = usb_new_device(udev);
if (status) {
spin_lock_irq(&device_state_lock);
- hdev->children[port1-1] = NULL;
+ hub->ports[port1 - 1]->child = NULL;
spin_unlock_irq(&device_state_lock);
}
}
@@ -4282,7 +4379,7 @@
int ret;
hdev = hub->hdev;
- udev = hdev->children[port-1];
+ udev = hub->ports[port - 1]->child;
if (!hub_is_superspeed(hdev)) {
if (!(portchange & USB_PORT_STAT_C_SUSPEND))
return 0;
@@ -4436,7 +4533,7 @@
*/
if (!(portstatus & USB_PORT_STAT_ENABLE)
&& !connect_change
- && hdev->children[i-1]) {
+ && hub->ports[i - 1]->child) {
dev_err (hub_dev,
"port %i "
"disabled by hub (EMI?), "
@@ -4572,6 +4669,102 @@
return 0;
}
+static ssize_t show_port_power_state(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_device *udev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ int port1, power_state;
+ const char *result;
+
+ sscanf(dev_name(dev), "port%d", &port1);
+ usb_autopm_get_interface(intf);
+ power_state = usb_get_hub_port_power_state(udev, port1);
+ usb_autopm_put_interface(intf);
+ if (power_state == 1)
+ result = on_string;
+ else if (!power_state)
+ result = off_string;
+ else
+ result = "error";
+ return sprintf(buf, "%s\n", result);
+}
+static DEVICE_ATTR(state, S_IRUGO, show_port_power_state, NULL);
+
+static ssize_t show_port_power_control(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct usb_port *hub_port = to_usb_port(dev);
+ const char *result;
+
+ switch (hub_port->port_power_policy) {
+ case USB_PORT_POWER_ON:
+ result = on_string;
+ break;
+ case USB_PORT_POWER_OFF:
+ result = off_string;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return sprintf(buf, "%s\n", result);
+}
+
+static ssize_t store_port_power_control(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct usb_device *hdev = to_usb_device(dev->parent->parent);
+ struct usb_interface *intf = to_usb_interface(dev->parent);
+ struct usb_port *hub_port = to_usb_port(dev);
+ int port1, ret, len = count;
+ char *cp;
+
+ sscanf(dev_name(dev), "port%d", &port1);
+ cp = memchr(buf, '\n', count);
+ if (cp)
+ len = cp - buf;
+ if (len == sizeof(on_string) - 1
+ && strncmp(buf, on_string, len) == 0) {
+ hub_port->port_power_policy = USB_PORT_POWER_ON;
+ usb_autopm_get_interface(intf);
+ ret = set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+ usb_autopm_put_interface(intf);
+ if (ret < 0)
+ return -EIO;
+ } else if (len == sizeof(off_string) - 1
+ && strncmp(buf, off_string, len) == 0) {
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ hub_port->port_power_policy = USB_PORT_POWER_OFF;
+ usb_autopm_get_interface(intf);
+ hub_port_logical_disconnect(hub, port1);
+ ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
+ usb_autopm_put_interface(intf);
+ if (ret < 0)
+ return -EIO;
+ } else
+ return -EINVAL;
+
+ return count;
+}
+static DEVICE_ATTR(control, S_IWUSR | S_IRUGO, show_port_power_control,
+ store_port_power_control);
+
+static struct attribute *port_dev_attrs[] = {
+ &dev_attr_control.attr,
+ &dev_attr_state.attr,
+ NULL,
+};
+
+static struct attribute_group port_dev_attr_grp = {
+ .attrs = port_dev_attrs,
+};
+
+static const struct attribute_group *port_dev_group[] = {
+ &port_dev_attr_grp,
+ NULL,
+};
+
static const struct usb_device_id hub_id_table[] = {
{ .match_flags = USB_DEVICE_ID_MATCH_DEV_CLASS,
.bDeviceClass = USB_CLASS_HUB},
@@ -4993,3 +5186,75 @@
schedule_work(&iface->reset_ws);
}
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
+
+/**
+ * usb_hub_find_child - Get the pointer of child device
+ * attached to the port which is specified by @port1.
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num to indicate which port the child device
+ * is attached to.
+ *
+ * USB drivers call this function to get hub's child device
+ * pointer.
+ *
+ * Return NULL if input param is invalid and
+ * child's usb_device pointer if non-NULL.
+ */
+struct usb_device *usb_hub_find_child(struct usb_device *hdev,
+ int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ if (port1 < 1 || port1 > hdev->maxchild)
+ return NULL;
+ return hub->ports[port1 - 1]->child;
+}
+EXPORT_SYMBOL_GPL(usb_hub_find_child);
+
+/**
+ * usb_set_hub_port_connect_type - set hub port connect type.
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ * @type: connect type of the port
+ */
+void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
+ enum usb_port_connect_type type)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ hub->ports[port1 - 1]->connect_type = type;
+}
+
+/**
+ * usb_get_hub_port_connect_type - Get the port's connect type
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ *
+ * Return connect type of the port and if input params are
+ * invalid, return USB_PORT_CONNECT_TYPE_UNKNOWN.
+ */
+enum usb_port_connect_type
+usb_get_hub_port_connect_type(struct usb_device *hdev, int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ return hub->ports[port1 - 1]->connect_type;
+}
+
+#ifdef CONFIG_ACPI
+/**
+ * usb_get_hub_port_acpi_handle - Get the usb port's acpi handle
+ * @hdev: USB device belonging to the usb hub
+ * @port1: port num of the port
+ *
+ * Return port's acpi handle if successful, NULL if params are
+ * invaild.
+ */
+acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+ int port1)
+{
+ struct usb_hub *hub = hdev_to_hub(hdev);
+
+ return DEVICE_ACPI_HANDLE(&hub->ports[port1 - 1]->dev);
+}
+#endif
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f15501f4c..2dbb515 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -205,7 +205,7 @@
* for all devices. It will affect things like hub resets
* and EMF-related port disables.
*/
- if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
+ if (!(udev->quirks & USB_QUIRK_RESET))
udev->persist_enabled = 1;
#endif /* CONFIG_PM */
}
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 682e825..818e4a0 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -196,7 +196,7 @@
struct usb_device *udev;
udev = to_usb_device(dev);
- return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS));
+ return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET));
}
static ssize_t
@@ -204,15 +204,15 @@
const char *buf, size_t count)
{
struct usb_device *udev = to_usb_device(dev);
- int config;
+ int val;
- if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1)
+ if (sscanf(buf, "%d", &val) != 1 || val < 0 || val > 1)
return -EINVAL;
usb_lock_device(udev);
- if (config)
- udev->quirks |= USB_QUIRK_RESET_MORPHS;
+ if (val)
+ udev->quirks |= USB_QUIRK_RESET;
else
- udev->quirks &= ~USB_QUIRK_RESET_MORPHS;
+ udev->quirks &= ~USB_QUIRK_RESET;
usb_unlock_device(udev);
return count;
}
diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c
index 8947b20..0ef7d42 100644
--- a/drivers/usb/core/usb-acpi.c
+++ b/drivers/usb/core/usb-acpi.c
@@ -19,20 +19,91 @@
#include "usb.h"
-static int usb_acpi_check_upc(struct usb_device *udev, acpi_handle handle)
+/**
+ * usb_acpi_power_manageable - check whether usb port has
+ * acpi power resource.
+ * @hdev: USB device belonging to the usb hub
+ * @index: port index based zero
+ *
+ * Return true if the port has acpi power resource and false if no.
+ */
+bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
+{
+ acpi_handle port_handle;
+ int port1 = index + 1;
+
+ port_handle = usb_get_hub_port_acpi_handle(hdev,
+ port1);
+ if (port_handle)
+ return acpi_bus_power_manageable(port_handle);
+ else
+ return false;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_power_manageable);
+
+/**
+ * usb_acpi_set_power_state - control usb port's power via acpi power
+ * resource
+ * @hdev: USB device belonging to the usb hub
+ * @index: port index based zero
+ * @enable: power state expected to be set
+ *
+ * Notice to use usb_acpi_power_manageable() to check whether the usb port
+ * has acpi power resource before invoking this function.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int usb_acpi_set_power_state(struct usb_device *hdev, int index, bool enable)
+{
+ acpi_handle port_handle;
+ unsigned char state;
+ int port1 = index + 1;
+ int error = -EINVAL;
+
+ port_handle = (acpi_handle)usb_get_hub_port_acpi_handle(hdev,
+ port1);
+ if (!port_handle)
+ return error;
+
+ if (enable)
+ state = ACPI_STATE_D0;
+ else
+ state = ACPI_STATE_D3_COLD;
+
+ error = acpi_bus_set_power(port_handle, state);
+ if (!error)
+ dev_dbg(&hdev->dev, "The power of hub port %d was set to %d\n",
+ port1, enable);
+ else
+ dev_dbg(&hdev->dev, "The power of hub port failed to be set\n");
+
+ return error;
+}
+EXPORT_SYMBOL_GPL(usb_acpi_set_power_state);
+
+static int usb_acpi_check_port_connect_type(struct usb_device *hdev,
+ acpi_handle handle, int port1)
{
acpi_status status;
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *upc;
+ struct acpi_pld pld;
int ret = 0;
- status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
-
+ /*
+ * Accoding to ACPI Spec 9.13. PLD indicates whether usb port is
+ * user visible and _UPC indicates whether it is connectable. If
+ * the port was visible and connectable, it could be freely connected
+ * and disconnected with USB devices. If no visible and connectable,
+ * a usb device is directly hard-wired to the port. If no visible and
+ * no connectable, the port would be not used.
+ */
+ status = acpi_get_physical_device_location(handle, &pld);
if (ACPI_FAILURE(status))
return -ENODEV;
+ status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
upc = buffer.pointer;
-
if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
|| upc->package.count != 4) {
ret = -EINVAL;
@@ -40,69 +111,107 @@
}
if (upc->package.elements[0].integer.value)
- udev->removable = USB_DEVICE_REMOVABLE;
- else
- udev->removable = USB_DEVICE_FIXED;
+ if (pld.user_visible)
+ usb_set_hub_port_connect_type(hdev, port1,
+ USB_PORT_CONNECT_TYPE_HOT_PLUG);
+ else
+ usb_set_hub_port_connect_type(hdev, port1,
+ USB_PORT_CONNECT_TYPE_HARD_WIRED);
+ else if (!pld.user_visible)
+ usb_set_hub_port_connect_type(hdev, port1, USB_PORT_NOT_USED);
out:
kfree(upc);
return ret;
}
-static int usb_acpi_check_pld(struct usb_device *udev, acpi_handle handle)
-{
- acpi_status status;
- struct acpi_pld pld;
-
- status = acpi_get_physical_device_location(handle, &pld);
-
- if (ACPI_FAILURE(status))
- return -ENODEV;
-
- if (pld.user_visible)
- udev->removable = USB_DEVICE_REMOVABLE;
- else
- udev->removable = USB_DEVICE_FIXED;
-
- return 0;
-}
-
static int usb_acpi_find_device(struct device *dev, acpi_handle *handle)
{
struct usb_device *udev;
- struct device *parent;
acpi_handle *parent_handle;
-
- if (!is_usb_device(dev))
- return -ENODEV;
-
- udev = to_usb_device(dev);
- parent = dev->parent;
- parent_handle = DEVICE_ACPI_HANDLE(parent);
-
- if (!parent_handle)
- return -ENODEV;
-
- *handle = acpi_get_child(parent_handle, udev->portnum);
-
- if (!*handle)
- return -ENODEV;
+ int port_num;
/*
- * PLD will tell us whether a port is removable to the user or
- * not. If we don't get an answer from PLD (it's not present
- * or it's malformed) then try to infer it from UPC. If a
- * device isn't connectable then it's probably not removable.
+ * In the ACPI DSDT table, only usb root hub and usb ports are
+ * acpi device nodes. The hierarchy like following.
+ * Device (EHC1)
+ * Device (HUBN)
+ * Device (PR01)
+ * Device (PR11)
+ * Device (PR12)
+ * Device (PR13)
+ * ...
+ * So all binding process is divided into two parts. binding
+ * root hub and usb ports.
*/
- if (usb_acpi_check_pld(udev, *handle) != 0)
- usb_acpi_check_upc(udev, *handle);
+ if (is_usb_device(dev)) {
+ udev = to_usb_device(dev);
+ if (udev->parent) {
+ enum usb_port_connect_type type;
+
+ /*
+ * According usb port's connect type to set usb device's
+ * removability.
+ */
+ type = usb_get_hub_port_connect_type(udev->parent,
+ udev->portnum);
+ switch (type) {
+ case USB_PORT_CONNECT_TYPE_HOT_PLUG:
+ udev->removable = USB_DEVICE_REMOVABLE;
+ break;
+ case USB_PORT_CONNECT_TYPE_HARD_WIRED:
+ udev->removable = USB_DEVICE_FIXED;
+ break;
+ default:
+ udev->removable = USB_DEVICE_REMOVABLE_UNKNOWN;
+ break;
+ }
+
+ return -ENODEV;
+ }
+
+ /* root hub's parent is the usb hcd. */
+ parent_handle = DEVICE_ACPI_HANDLE(dev->parent);
+ *handle = acpi_get_child(parent_handle, udev->portnum);
+ if (!*handle)
+ return -ENODEV;
+ return 0;
+ } else if (is_usb_port(dev)) {
+ sscanf(dev_name(dev), "port%d", &port_num);
+ /* Get the struct usb_device point of port's hub */
+ udev = to_usb_device(dev->parent->parent);
+
+ /*
+ * The root hub ports' parent is the root hub. The non-root-hub
+ * ports' parent is the parent hub port which the hub is
+ * connected to.
+ */
+ if (!udev->parent) {
+ *handle = acpi_get_child(DEVICE_ACPI_HANDLE(&udev->dev),
+ port_num);
+ if (!*handle)
+ return -ENODEV;
+ } else {
+ parent_handle =
+ usb_get_hub_port_acpi_handle(udev->parent,
+ udev->portnum);
+ if (!parent_handle)
+ return -ENODEV;
+
+ *handle = acpi_get_child(parent_handle, port_num);
+ if (!*handle)
+ return -ENODEV;
+ }
+ usb_acpi_check_port_connect_type(udev, *handle, port_num);
+ } else
+ return -ENODEV;
return 0;
}
static struct acpi_bus_type usb_acpi_bus = {
.bus = &usb_bus_type,
- .find_bridge = NULL,
+ .find_bridge = usb_acpi_find_device,
.find_device = usb_acpi_find_device,
};
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index acb103c..1c528c1 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,4 +1,5 @@
#include <linux/pm.h>
+#include <linux/acpi.h>
struct dev_state;
@@ -115,6 +116,7 @@
extern struct device_type usb_device_type;
extern struct device_type usb_if_device_type;
extern struct device_type usb_ep_device_type;
+extern struct device_type usb_port_device_type;
extern struct usb_device_driver usb_generic_driver;
static inline int is_usb_device(const struct device *dev)
@@ -132,6 +134,11 @@
return dev->type == &usb_ep_device_type;
}
+static inline int is_usb_port(const struct device *dev)
+{
+ return dev->type == &usb_port_device_type;
+}
+
/* Do the same for device drivers and interface drivers. */
static inline int is_usb_device_driver(struct device_driver *drv)
@@ -162,10 +169,16 @@
extern void usb_notify_remove_device(struct usb_device *udev);
extern void usb_notify_add_bus(struct usb_bus *ubus);
extern void usb_notify_remove_bus(struct usb_bus *ubus);
+extern enum usb_port_connect_type
+ usb_get_hub_port_connect_type(struct usb_device *hdev, int port1);
+extern void usb_set_hub_port_connect_type(struct usb_device *hdev, int port1,
+ enum usb_port_connect_type type);
#ifdef CONFIG_ACPI
extern int usb_acpi_register(void);
extern void usb_acpi_unregister(void);
+extern acpi_handle usb_get_hub_port_acpi_handle(struct usb_device *hdev,
+ int port1);
#else
static inline int usb_acpi_register(void) { return 0; };
static inline void usb_acpi_unregister(void) { };
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index d13c60f4..f6a6e07 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -2,8 +2,6 @@
tristate "DesignWare USB3 DRD Core Support"
depends on (USB && USB_GADGET)
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
select USB_XHCI_PLATFORM if USB_SUPPORT && USB_XHCI_HCD
help
Say Y or M here if your system has a Dual Role SuperSpeed
diff --git a/drivers/usb/early/ehci-dbgp.c b/drivers/usb/early/ehci-dbgp.c
index ee0ebac..89dcf15 100644
--- a/drivers/usb/early/ehci-dbgp.c
+++ b/drivers/usb/early/ehci-dbgp.c
@@ -450,7 +450,7 @@
writel(FLAG_CF, &ehci_regs->configured_flag);
/* Wait until the controller is no longer halted */
- loop = 10;
+ loop = 1000;
do {
status = readl(&ehci_regs->status);
if (!(status & STS_HALT))
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 51ab5fd..a53be32 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -154,16 +154,25 @@
config USB_ATMEL_USBA
tristate "Atmel USBA"
- select USB_GADGET_DUALSPEED
depends on AVR32 || ARCH_AT91SAM9RL || ARCH_AT91SAM9G45
help
USBA is the integrated high-speed USB Device controller on
the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+config USB_BCM63XX_UDC
+ tristate "Broadcom BCM63xx Peripheral Controller"
+ depends on BCM63XX
+ help
+ Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
+ high speed USB Device Port with support for four fixed endpoints
+ (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "bcm63xx_udc".
+
config USB_FSL_USB2
tristate "Freescale Highspeed USB DR Peripheral Controller"
depends on FSL_SOC || ARCH_MXC
- select USB_GADGET_DUALSPEED
select USB_FSL_MPH_DR_OF if OF
help
Some of Freescale PowerPC and i.MX processors have a High Speed
@@ -179,7 +188,6 @@
config USB_FUSB300
tristate "Faraday FUSB300 USB Peripheral Controller"
depends on !PHYS_ADDR_T_64BIT
- select USB_GADGET_DUALSPEED
help
Faraday usb device controller FUSB300 driver
@@ -227,7 +235,6 @@
config USB_R8A66597
tristate "Renesas R8A66597 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
R8A66597 is a discrete USB host and peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -240,7 +247,6 @@
config USB_RENESAS_USBHS_UDC
tristate 'Renesas USBHS controller'
depends on USB_RENESAS_USBHS
- select USB_GADGET_DUALSPEED
help
Renesas USBHS is a discrete USB host and peripheral controller chip
that supports both full and high speed USB 2.0 data transfers.
@@ -268,7 +274,6 @@
config USB_S3C_HSOTG
tristate "S3C HS/OtG USB Device controller"
depends on S3C_DEV_USB_HSOTG
- select USB_GADGET_DUALSPEED
help
The Samsung S3C64XX USB2.0 high-speed gadget controller
integrated into the S3C64XX series SoC.
@@ -305,7 +310,6 @@
config USB_S3C_HSUDC
tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
depends on ARCH_S3C24XX
- select USB_GADGET_DUALSPEED
help
Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
integrated with dual speed USB 2.0 device controller. It has
@@ -315,7 +319,6 @@
config USB_MV_UDC
tristate "Marvell USB2.0 Device Controller"
- select USB_GADGET_DUALSPEED
help
Marvell Socs (including PXA and MMP series) include a high speed
USB2.0 OTG controller, which can be configured as high speed or
@@ -338,14 +341,12 @@
config USB_GADGET_MUSB_HDRC
tristate "Inventra HDRC USB Peripheral (TI, ADI, ...)"
depends on USB_MUSB_HDRC
- select USB_GADGET_DUALSPEED
help
This OTG-capable silicon IP is used in dual designs including
the TI DaVinci, OMAP 243x, OMAP 343x, TUSB 6010, and ADI Blackfin
config USB_M66592
tristate "Renesas M66592 USB Peripheral Controller"
- select USB_GADGET_DUALSPEED
help
M66592 is a discrete USB peripheral controller chip that
supports both full and high speed USB 2.0 data transfers.
@@ -362,7 +363,6 @@
config USB_AMD5536UDC
tristate "AMD5536 UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
It is a USB Highspeed DMA capable USB device controller. Beside ep0
@@ -389,7 +389,6 @@
config USB_NET2272
tristate "PLX NET2272"
- select USB_GADGET_DUALSPEED
help
PLX NET2272 is a USB peripheral controller which supports
both full and high speed USB 2.0 data transfers.
@@ -413,7 +412,6 @@
config USB_NET2280
tristate "NetChip 228x"
depends on PCI
- select USB_GADGET_DUALSPEED
help
NetChip 2280 / 2282 is a PCI based USB peripheral controller which
supports both full and high speed USB 2.0 data transfers.
@@ -443,7 +441,6 @@
config USB_EG20T
tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
depends on PCI
- select USB_GADGET_DUALSPEED
help
This is a USB device driver for EG20T PCH.
EG20T PCH is the platform controller hub that is used in Intel's
@@ -470,8 +467,6 @@
config USB_DUMMY_HCD
tristate "Dummy HCD (DEVELOPMENT)"
depends on USB=y || (USB=m && USB_GADGET=m)
- select USB_GADGET_DUALSPEED
- select USB_GADGET_SUPERSPEED
help
This host controller driver emulates USB, looping all data transfer
requests back to a USB "gadget driver" in the same host. The host
@@ -496,18 +491,15 @@
endmenu
-# Selected by UDC drivers that support high-speed operation.
-config USB_GADGET_DUALSPEED
- bool
-
-# Selected by UDC drivers that support super-speed opperation
-config USB_GADGET_SUPERSPEED
- bool
- depends on USB_GADGET_DUALSPEED
-
#
# USB Gadget Drivers
#
+
+# composite based drivers
+config USB_LIBCOMPOSITE
+ tristate
+ depends on USB_GADGET
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
@@ -531,6 +523,7 @@
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
+ select USB_LIBCOMPOSITE
help
Gadget Zero is a two-configuration device. It either sinks and
sources bulk data; or it loops back a configurable number of
@@ -566,6 +559,7 @@
config USB_AUDIO
tristate "Audio Gadget (EXPERIMENTAL)"
depends on SND
+ select USB_LIBCOMPOSITE
select SND_PCM
help
This Gadget Audio driver is compatible with USB Audio Class
@@ -594,6 +588,7 @@
config USB_ETH
tristate "Ethernet Gadget (with CDC Ethernet support)"
depends on NET
+ select USB_LIBCOMPOSITE
select CRC32
help
This driver implements Ethernet style communication, in one of
@@ -629,6 +624,7 @@
config USB_ETH_RNDIS
bool "RNDIS support"
depends on USB_ETH
+ select USB_LIBCOMPOSITE
default y
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
@@ -647,6 +643,7 @@
config USB_ETH_EEM
bool "Ethernet Emulation Model (EEM) support"
depends on USB_ETH
+ select USB_LIBCOMPOSITE
default n
help
CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
@@ -663,6 +660,7 @@
config USB_G_NCM
tristate "Network Control Model (NCM) support"
depends on NET
+ select USB_LIBCOMPOSITE
select CRC32
help
This driver implements USB CDC NCM subclass standard. NCM is
@@ -692,6 +690,7 @@
config USB_FUNCTIONFS
tristate "Function Filesystem (EXPERIMENTAL)"
depends on EXPERIMENTAL
+ select USB_LIBCOMPOSITE
select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
help
The Function Filesystem (FunctionFS) lets one create USB
@@ -755,6 +754,7 @@
config USB_MASS_STORAGE
tristate "Mass Storage Gadget"
depends on BLOCK
+ select USB_LIBCOMPOSITE
help
The Mass Storage Gadget acts as a USB Mass Storage disk drive.
As its storage repository it can use a regular file or a block
@@ -770,6 +770,7 @@
config USB_GADGET_TARGET
tristate "USB Gadget Target Fabric Module"
depends on TARGET_CORE
+ select USB_LIBCOMPOSITE
help
This fabric is an USB gadget. Two USB protocols are supported that is
BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
@@ -779,6 +780,7 @@
config USB_G_SERIAL
tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+ select USB_LIBCOMPOSITE
help
The Serial Gadget talks to the Linux-USB generic serial driver.
This driver supports a CDC-ACM module option, which can be used
@@ -799,6 +801,7 @@
config USB_MIDI_GADGET
tristate "MIDI Gadget (EXPERIMENTAL)"
depends on SND && EXPERIMENTAL
+ select USB_LIBCOMPOSITE
select SND_RAWMIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
@@ -812,6 +815,7 @@
config USB_G_PRINTER
tristate "Printer Gadget"
+ select USB_LIBCOMPOSITE
help
The Printer Gadget channels data between the USB host and a
userspace program driving the print engine. The user space
@@ -828,6 +832,7 @@
config USB_CDC_COMPOSITE
tristate "CDC Composite Device (Ethernet and ACM)"
depends on NET
+ select USB_LIBCOMPOSITE
help
This driver provides two functions in one configuration:
a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
@@ -842,6 +847,7 @@
config USB_G_NOKIA
tristate "Nokia composite gadget"
depends on PHONET
+ select USB_LIBCOMPOSITE
help
The Nokia composite gadget provides support for acm, obex
and phonet in only one composite gadget driver.
@@ -852,6 +858,7 @@
config USB_G_ACM_MS
tristate "CDC Composite Device (ACM and mass storage)"
depends on BLOCK
+ select USB_LIBCOMPOSITE
help
This driver provides two functions in one configuration:
a mass storage, and a CDC ACM (serial port) link.
@@ -863,6 +870,7 @@
tristate "Multifunction Composite Gadget (EXPERIMENTAL)"
depends on BLOCK && NET
select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+ select USB_LIBCOMPOSITE
help
The Multifunction Composite Gadget provides Ethernet (RNDIS
and/or CDC Ethernet), mass storage and ACM serial link
@@ -903,6 +911,7 @@
config USB_G_HID
tristate "HID Gadget"
+ select USB_LIBCOMPOSITE
help
The HID gadget driver provides generic emulation of USB
Human Interface Devices (HID).
@@ -913,8 +922,10 @@
Say "y" to link the driver statically, or "m" to build a
dynamically linked module called "g_hid".
+# Standalone / single function gadgets
config USB_G_DBGP
tristate "EHCI Debug Device Gadget"
+ select USB_LIBCOMPOSITE
help
This gadget emulates an EHCI Debug device. This is useful when you want
to interact with an EHCI Debug Port.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 3fd8cd0..307be5f 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -4,6 +4,9 @@
ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
+libcomposite-y := usbstring.o config.o epautoconf.o
+libcomposite-y += composite.o
obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
obj-$(CONFIG_USB_NET2272) += net2272.o
obj-$(CONFIG_USB_NET2280) += net2280.o
@@ -16,6 +19,7 @@
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
fsl_usb2_udc-y := fsl_udc_core.o
fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c
index 75b8a69..5a7f289 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/acm_ms.c
@@ -15,7 +15,7 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
+#include <linux/module.h>
#include "u_serial.h"
@@ -41,15 +41,12 @@
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "u_serial.c"
#include "f_acm.c"
#include "f_mass_storage.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -89,17 +86,11 @@
NULL,
};
-
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -157,7 +148,6 @@
static int __init acm_ms_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
void *retp;
@@ -174,44 +164,22 @@
goto fail0;
}
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized; trying %s\n",
- gadget->name,
- acm_ms_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
/*
* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
-
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail1;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &acm_ms_config_driver, acm_ms_do_config);
if (status < 0)
goto fail1;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
fsg_common_put(&fsg_common);
@@ -232,11 +200,12 @@
return 0;
}
-static struct usb_composite_driver acm_ms_driver = {
+static __refdata struct usb_composite_driver acm_ms_driver = {
.name = "g_acm_ms",
.dev = &device_desc,
.max_speed = USB_SPEED_SUPER,
.strings = dev_strings,
+ .bind = acm_ms_bind,
.unbind = __exit_p(acm_ms_unbind),
};
@@ -246,7 +215,7 @@
static int __init init(void)
{
- return usb_composite_probe(&acm_ms_driver, acm_ms_bind);
+ return usb_composite_probe(&acm_ms_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 187d211..fc0ec5e 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -1401,7 +1401,7 @@
}
static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int amd5536_stop(struct usb_gadget_driver *driver);
/* gadget operations */
static const struct usb_gadget_ops udc_ops = {
@@ -1914,7 +1914,7 @@
/* Called by gadget driver to register itself */
static int amd5536_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct udc *dev = udc;
int retval;
@@ -1932,7 +1932,7 @@
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
/* Some gadget drivers use both ep0 directions.
* NOTE: to gadget driver, ep0 is just one endpoint...
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index c9e66df..af93128 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -469,7 +469,7 @@
const struct usb_endpoint_descriptor *desc)
{
struct at91_ep *ep = container_of(_ep, struct at91_ep, ep);
- struct at91_udc *udc = ep->udc;
+ struct at91_udc *udc;
u16 maxpacket;
u32 tmp;
unsigned long flags;
@@ -484,6 +484,7 @@
return -EINVAL;
}
+ udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) {
DBG("bogus device state\n");
return -ESHUTDOWN;
@@ -1703,7 +1704,7 @@
int retval;
struct resource *res;
- if (!dev->platform_data) {
+ if (!dev->platform_data && !pdev->dev.of_node) {
/* small (so we copy it) but critical! */
DBG("missing platform_data\n");
return -ENODEV;
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/audio.c
index 9889924..231b0ef 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/audio.c
@@ -12,35 +12,21 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include "gadget_chips.h"
#define DRIVER_DESC "Linux USB Audio Gadget"
#define DRIVER_VERSION "Feb 2, 2012"
-/*-------------------------------------------------------------------------*/
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
+USB_GADGET_COMPOSITE_OPTIONS();
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -149,39 +135,18 @@
static int __init audio_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
int status;
- gcnum = usb_gadget_controller_number(cdev->gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- ERROR(cdev, "controller '%s' not recognized; trying %s\n",
- cdev->gadget->name,
- audio_config_driver.label);
- device_desc.bcdDevice =
- __constant_cpu_to_le16(0x0300 | 0x0099);
- }
-
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- cdev->gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &audio_config_driver, audio_do_config);
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: %s\n", DRIVER_DESC, DRIVER_VERSION);
return 0;
@@ -198,17 +163,18 @@
return 0;
}
-static struct usb_composite_driver audio_driver = {
+static __refdata struct usb_composite_driver audio_driver = {
.name = "g_audio",
.dev = &device_desc,
.strings = audio_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = audio_bind,
.unbind = __exit_p(audio_unbind),
};
static int __init init(void)
{
- return usb_composite_probe(&audio_driver, audio_bind);
+ return usb_composite_probe(&audio_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c
new file mode 100644
index 0000000..9ca7922
--- /dev/null
+++ b/drivers/usb/gadget/bcm63xx_udc.c
@@ -0,0 +1,2464 @@
+/*
+ * bcm63xx_udc.c -- BCM63xx UDC high/full speed USB device controller
+ *
+ * Copyright (C) 2012 Kevin Cernekee <cernekee@gmail.com>
+ * Copyright (C) 2012 Broadcom Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/clk.h>
+#include <linux/compiler.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/kconfig.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/workqueue.h>
+
+#include <bcm63xx_cpu.h>
+#include <bcm63xx_iudma.h>
+#include <bcm63xx_dev_usb_usbd.h>
+#include <bcm63xx_io.h>
+#include <bcm63xx_regs.h>
+
+#define DRV_MODULE_NAME "bcm63xx_udc"
+
+static const char bcm63xx_ep0name[] = "ep0";
+static const char *const bcm63xx_ep_name[] = {
+ bcm63xx_ep0name,
+ "ep1in-bulk", "ep2out-bulk", "ep3in-int", "ep4out-int",
+};
+
+static bool use_fullspeed;
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
+
+/*
+ * RX IRQ coalescing options:
+ *
+ * false (default) - one IRQ per DATAx packet. Slow but reliable. The
+ * driver is able to pass the "testusb" suite and recover from conditions like:
+ *
+ * 1) Device queues up a 2048-byte RX IUDMA transaction on an OUT bulk ep
+ * 2) Host sends 512 bytes of data
+ * 3) Host decides to reconfigure the device and sends SET_INTERFACE
+ * 4) Device shuts down the endpoint and cancels the RX transaction
+ *
+ * true - one IRQ per transfer, for transfers <= 2048B. Generates
+ * considerably fewer IRQs, but error recovery is less robust. Does not
+ * reliably pass "testusb".
+ *
+ * TX always uses coalescing, because we can cancel partially complete TX
+ * transfers by repeatedly flushing the FIFO. The hardware doesn't allow
+ * this on RX.
+ */
+static bool irq_coalesce;
+module_param(irq_coalesce, bool, S_IRUGO);
+MODULE_PARM_DESC(irq_coalesce, "take one IRQ per RX transfer");
+
+#define BCM63XX_NUM_EP 5
+#define BCM63XX_NUM_IUDMA 6
+#define BCM63XX_NUM_FIFO_PAIRS 3
+
+#define IUDMA_RESET_TIMEOUT_US 10000
+
+#define IUDMA_EP0_RXCHAN 0
+#define IUDMA_EP0_TXCHAN 1
+
+#define IUDMA_MAX_FRAGMENT 2048
+#define BCM63XX_MAX_CTRL_PKT 64
+
+#define BCMEP_CTRL 0x00
+#define BCMEP_ISOC 0x01
+#define BCMEP_BULK 0x02
+#define BCMEP_INTR 0x03
+
+#define BCMEP_OUT 0x00
+#define BCMEP_IN 0x01
+
+#define BCM63XX_SPD_FULL 1
+#define BCM63XX_SPD_HIGH 0
+
+#define IUDMA_DMAC_OFFSET 0x200
+#define IUDMA_DMAS_OFFSET 0x400
+
+enum bcm63xx_ep0_state {
+ EP0_REQUEUE,
+ EP0_IDLE,
+ EP0_IN_DATA_PHASE_SETUP,
+ EP0_IN_DATA_PHASE_COMPLETE,
+ EP0_OUT_DATA_PHASE_SETUP,
+ EP0_OUT_DATA_PHASE_COMPLETE,
+ EP0_OUT_STATUS_PHASE,
+ EP0_IN_FAKE_STATUS_PHASE,
+ EP0_SHUTDOWN,
+};
+
+static const char __maybe_unused bcm63xx_ep0_state_names[][32] = {
+ "REQUEUE",
+ "IDLE",
+ "IN_DATA_PHASE_SETUP",
+ "IN_DATA_PHASE_COMPLETE",
+ "OUT_DATA_PHASE_SETUP",
+ "OUT_DATA_PHASE_COMPLETE",
+ "OUT_STATUS_PHASE",
+ "IN_FAKE_STATUS_PHASE",
+ "SHUTDOWN",
+};
+
+/**
+ * struct iudma_ch_cfg - Static configuration for an IUDMA channel.
+ * @ep_num: USB endpoint number.
+ * @n_bds: Number of buffer descriptors in the ring.
+ * @ep_type: Endpoint type (control, bulk, interrupt).
+ * @dir: Direction (in, out).
+ * @n_fifo_slots: Number of FIFO entries to allocate for this channel.
+ * @max_pkt_hs: Maximum packet size in high speed mode.
+ * @max_pkt_fs: Maximum packet size in full speed mode.
+ */
+struct iudma_ch_cfg {
+ int ep_num;
+ int n_bds;
+ int ep_type;
+ int dir;
+ int n_fifo_slots;
+ int max_pkt_hs;
+ int max_pkt_fs;
+};
+
+static const struct iudma_ch_cfg iudma_defaults[] = {
+
+ /* This controller was designed to support a CDC/RNDIS application.
+ It may be possible to reconfigure some of the endpoints, but
+ the hardware limitations (FIFO sizing and number of DMA channels)
+ may significantly impact flexibility and/or stability. Change
+ these values at your own risk.
+
+ ep_num ep_type n_fifo_slots max_pkt_fs
+ idx | n_bds | dir | max_pkt_hs |
+ | | | | | | | | */
+ [0] = { -1, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 },
+ [1] = { 0, 4, BCMEP_CTRL, BCMEP_OUT, 32, 64, 64 },
+ [2] = { 2, 16, BCMEP_BULK, BCMEP_OUT, 128, 512, 64 },
+ [3] = { 1, 16, BCMEP_BULK, BCMEP_IN, 128, 512, 64 },
+ [4] = { 4, 4, BCMEP_INTR, BCMEP_OUT, 32, 64, 64 },
+ [5] = { 3, 4, BCMEP_INTR, BCMEP_IN, 32, 64, 64 },
+};
+
+struct bcm63xx_udc;
+
+/**
+ * struct iudma_ch - Represents the current state of a single IUDMA channel.
+ * @ch_idx: IUDMA channel index (0 to BCM63XX_NUM_IUDMA-1).
+ * @ep_num: USB endpoint number. -1 for ep0 RX.
+ * @enabled: Whether bcm63xx_ep_enable() has been called.
+ * @max_pkt: "Chunk size" on the USB interface. Based on interface speed.
+ * @is_tx: true for TX, false for RX.
+ * @bep: Pointer to the associated endpoint. NULL for ep0 RX.
+ * @udc: Reference to the device controller.
+ * @read_bd: Next buffer descriptor to reap from the hardware.
+ * @write_bd: Next BD available for a new packet.
+ * @end_bd: Points to the final BD in the ring.
+ * @n_bds_used: Number of BD entries currently occupied.
+ * @bd_ring: Base pointer to the BD ring.
+ * @bd_ring_dma: Physical (DMA) address of bd_ring.
+ * @n_bds: Total number of BDs in the ring.
+ *
+ * ep0 has two IUDMA channels (IUDMA_EP0_RXCHAN and IUDMA_EP0_TXCHAN), as it is
+ * bidirectional. The "struct usb_ep" associated with ep0 is for TX (IN)
+ * only.
+ *
+ * Each bulk/intr endpoint has a single IUDMA channel and a single
+ * struct usb_ep.
+ */
+struct iudma_ch {
+ unsigned int ch_idx;
+ int ep_num;
+ bool enabled;
+ int max_pkt;
+ bool is_tx;
+ struct bcm63xx_ep *bep;
+ struct bcm63xx_udc *udc;
+
+ struct bcm_enet_desc *read_bd;
+ struct bcm_enet_desc *write_bd;
+ struct bcm_enet_desc *end_bd;
+ int n_bds_used;
+
+ struct bcm_enet_desc *bd_ring;
+ dma_addr_t bd_ring_dma;
+ unsigned int n_bds;
+};
+
+/**
+ * struct bcm63xx_ep - Internal (driver) state of a single endpoint.
+ * @ep_num: USB endpoint number.
+ * @iudma: Pointer to IUDMA channel state.
+ * @ep: USB gadget layer representation of the EP.
+ * @udc: Reference to the device controller.
+ * @queue: Linked list of outstanding requests for this EP.
+ * @halted: 1 if the EP is stalled; 0 otherwise.
+ */
+struct bcm63xx_ep {
+ unsigned int ep_num;
+ struct iudma_ch *iudma;
+ struct usb_ep ep;
+ struct bcm63xx_udc *udc;
+ struct list_head queue;
+ unsigned halted:1;
+};
+
+/**
+ * struct bcm63xx_req - Internal (driver) state of a single request.
+ * @queue: Links back to the EP's request list.
+ * @req: USB gadget layer representation of the request.
+ * @offset: Current byte offset into the data buffer (next byte to queue).
+ * @bd_bytes: Number of data bytes in outstanding BD entries.
+ * @iudma: IUDMA channel used for the request.
+ */
+struct bcm63xx_req {
+ struct list_head queue; /* ep's requests */
+ struct usb_request req;
+ unsigned int offset;
+ unsigned int bd_bytes;
+ struct iudma_ch *iudma;
+};
+
+/**
+ * struct bcm63xx_udc - Driver/hardware private context.
+ * @lock: Spinlock to mediate access to this struct, and (most) HW regs.
+ * @dev: Generic Linux device structure.
+ * @pd: Platform data (board/port info).
+ * @usbd_clk: Clock descriptor for the USB device block.
+ * @usbh_clk: Clock descriptor for the USB host block.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ * @usbd_regs: Base address of the USBD/USB20D block.
+ * @iudma_regs: Base address of the USBD's associated IUDMA block.
+ * @bep: Array of endpoints, including ep0.
+ * @iudma: Array of all IUDMA channels used by this controller.
+ * @cfg: USB configuration number, from SET_CONFIGURATION wValue.
+ * @iface: USB interface number, from SET_INTERFACE wIndex.
+ * @alt_iface: USB alt interface number, from SET_INTERFACE wValue.
+ * @ep0_ctrl_req: Request object for bcm63xx_udc-initiated ep0 transactions.
+ * @ep0_ctrl_buf: Data buffer for ep0_ctrl_req.
+ * @ep0state: Current state of the ep0 state machine.
+ * @ep0_wq: Workqueue struct used to wake up the ep0 state machine.
+ * @wedgemap: Bitmap of wedged endpoints.
+ * @ep0_req_reset: USB reset is pending.
+ * @ep0_req_set_cfg: Need to spoof a SET_CONFIGURATION packet.
+ * @ep0_req_set_iface: Need to spoof a SET_INTERFACE packet.
+ * @ep0_req_shutdown: Driver is shutting down; requesting ep0 to halt activity.
+ * @ep0_req_completed: ep0 request has completed; worker has not seen it yet.
+ * @ep0_reply: Pending reply from gadget driver.
+ * @ep0_request: Outstanding ep0 request.
+ * @debugfs_root: debugfs directory: /sys/kernel/debug/<DRV_MODULE_NAME>.
+ * @debugfs_usbd: debugfs file "usbd" for controller state.
+ * @debugfs_iudma: debugfs file "usbd" for IUDMA state.
+ */
+struct bcm63xx_udc {
+ spinlock_t lock;
+
+ struct device *dev;
+ struct bcm63xx_usbd_platform_data *pd;
+ struct clk *usbd_clk;
+ struct clk *usbh_clk;
+
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *driver;
+
+ void __iomem *usbd_regs;
+ void __iomem *iudma_regs;
+
+ struct bcm63xx_ep bep[BCM63XX_NUM_EP];
+ struct iudma_ch iudma[BCM63XX_NUM_IUDMA];
+
+ int cfg;
+ int iface;
+ int alt_iface;
+
+ struct bcm63xx_req ep0_ctrl_req;
+ u8 *ep0_ctrl_buf;
+
+ int ep0state;
+ struct work_struct ep0_wq;
+
+ unsigned long wedgemap;
+
+ unsigned ep0_req_reset:1;
+ unsigned ep0_req_set_cfg:1;
+ unsigned ep0_req_set_iface:1;
+ unsigned ep0_req_shutdown:1;
+
+ unsigned ep0_req_completed:1;
+ struct usb_request *ep0_reply;
+ struct usb_request *ep0_request;
+
+ struct dentry *debugfs_root;
+ struct dentry *debugfs_usbd;
+ struct dentry *debugfs_iudma;
+};
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops;
+
+/***********************************************************************
+ * Convenience functions
+ ***********************************************************************/
+
+static inline struct bcm63xx_udc *gadget_to_udc(struct usb_gadget *g)
+{
+ return container_of(g, struct bcm63xx_udc, gadget);
+}
+
+static inline struct bcm63xx_ep *our_ep(struct usb_ep *ep)
+{
+ return container_of(ep, struct bcm63xx_ep, ep);
+}
+
+static inline struct bcm63xx_req *our_req(struct usb_request *req)
+{
+ return container_of(req, struct bcm63xx_req, req);
+}
+
+static inline u32 usbd_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->usbd_regs + off);
+}
+
+static inline void usbd_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->usbd_regs + off);
+}
+
+static inline u32 usb_dma_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + off);
+}
+
+static inline void usb_dma_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + off);
+}
+
+static inline u32 usb_dmac_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + IUDMA_DMAC_OFFSET + off);
+}
+
+static inline void usb_dmac_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + IUDMA_DMAC_OFFSET + off);
+}
+
+static inline u32 usb_dmas_readl(struct bcm63xx_udc *udc, u32 off)
+{
+ return bcm_readl(udc->iudma_regs + IUDMA_DMAS_OFFSET + off);
+}
+
+static inline void usb_dmas_writel(struct bcm63xx_udc *udc, u32 val, u32 off)
+{
+ bcm_writel(val, udc->iudma_regs + IUDMA_DMAS_OFFSET + off);
+}
+
+static inline void set_clocks(struct bcm63xx_udc *udc, bool is_enabled)
+{
+ if (is_enabled) {
+ clk_enable(udc->usbh_clk);
+ clk_enable(udc->usbd_clk);
+ udelay(10);
+ } else {
+ clk_disable(udc->usbd_clk);
+ clk_disable(udc->usbh_clk);
+ }
+}
+
+/***********************************************************************
+ * Low-level IUDMA / FIFO operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_dma_select - Helper function to set up the init_sel signal.
+ * @udc: Reference to the device controller.
+ * @idx: Desired init_sel value.
+ *
+ * The "init_sel" signal is used as a selection index for both endpoints
+ * and IUDMA channels. Since these do not map 1:1, the use of this signal
+ * depends on the context.
+ */
+static void bcm63xx_ep_dma_select(struct bcm63xx_udc *udc, int idx)
+{
+ u32 val = usbd_readl(udc, USBD_CONTROL_REG);
+
+ val &= ~USBD_CONTROL_INIT_SEL_MASK;
+ val |= idx << USBD_CONTROL_INIT_SEL_SHIFT;
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_set_stall - Enable/disable stall on one endpoint.
+ * @udc: Reference to the device controller.
+ * @bep: Endpoint on which to operate.
+ * @is_stalled: true to enable stall, false to disable.
+ *
+ * See notes in bcm63xx_update_wedge() regarding automatic clearing of
+ * halt/stall conditions.
+ */
+static void bcm63xx_set_stall(struct bcm63xx_udc *udc, struct bcm63xx_ep *bep,
+ bool is_stalled)
+{
+ u32 val;
+
+ val = USBD_STALL_UPDATE_MASK |
+ (is_stalled ? USBD_STALL_ENABLE_MASK : 0) |
+ (bep->ep_num << USBD_STALL_EPNUM_SHIFT);
+ usbd_writel(udc, val, USBD_STALL_REG);
+}
+
+/**
+ * bcm63xx_fifo_setup - (Re)initialize FIFO boundaries and settings.
+ * @udc: Reference to the device controller.
+ *
+ * These parameters depend on the USB link speed. Settings are
+ * per-IUDMA-channel-pair.
+ */
+static void bcm63xx_fifo_setup(struct bcm63xx_udc *udc)
+{
+ int is_hs = udc->gadget.speed == USB_SPEED_HIGH;
+ u32 i, val, rx_fifo_slot, tx_fifo_slot;
+
+ /* set up FIFO boundaries and packet sizes; this is done in pairs */
+ rx_fifo_slot = tx_fifo_slot = 0;
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i += 2) {
+ const struct iudma_ch_cfg *rx_cfg = &iudma_defaults[i];
+ const struct iudma_ch_cfg *tx_cfg = &iudma_defaults[i + 1];
+
+ bcm63xx_ep_dma_select(udc, i >> 1);
+
+ val = (rx_fifo_slot << USBD_RXFIFO_CONFIG_START_SHIFT) |
+ ((rx_fifo_slot + rx_cfg->n_fifo_slots - 1) <<
+ USBD_RXFIFO_CONFIG_END_SHIFT);
+ rx_fifo_slot += rx_cfg->n_fifo_slots;
+ usbd_writel(udc, val, USBD_RXFIFO_CONFIG_REG);
+ usbd_writel(udc,
+ is_hs ? rx_cfg->max_pkt_hs : rx_cfg->max_pkt_fs,
+ USBD_RXFIFO_EPSIZE_REG);
+
+ val = (tx_fifo_slot << USBD_TXFIFO_CONFIG_START_SHIFT) |
+ ((tx_fifo_slot + tx_cfg->n_fifo_slots - 1) <<
+ USBD_TXFIFO_CONFIG_END_SHIFT);
+ tx_fifo_slot += tx_cfg->n_fifo_slots;
+ usbd_writel(udc, val, USBD_TXFIFO_CONFIG_REG);
+ usbd_writel(udc,
+ is_hs ? tx_cfg->max_pkt_hs : tx_cfg->max_pkt_fs,
+ USBD_TXFIFO_EPSIZE_REG);
+
+ usbd_readl(udc, USBD_TXFIFO_EPSIZE_REG);
+ }
+}
+
+/**
+ * bcm63xx_fifo_reset_ep - Flush a single endpoint's FIFO.
+ * @udc: Reference to the device controller.
+ * @ep_num: Endpoint number.
+ */
+static void bcm63xx_fifo_reset_ep(struct bcm63xx_udc *udc, int ep_num)
+{
+ u32 val;
+
+ bcm63xx_ep_dma_select(udc, ep_num);
+
+ val = usbd_readl(udc, USBD_CONTROL_REG);
+ val |= USBD_CONTROL_FIFO_RESET_MASK;
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+ usbd_readl(udc, USBD_CONTROL_REG);
+}
+
+/**
+ * bcm63xx_fifo_reset - Flush all hardware FIFOs.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_fifo_reset(struct bcm63xx_udc *udc)
+{
+ int i;
+
+ for (i = 0; i < BCM63XX_NUM_FIFO_PAIRS; i++)
+ bcm63xx_fifo_reset_ep(udc, i);
+}
+
+/**
+ * bcm63xx_ep_init - Initial (one-time) endpoint initialization.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_ep_init(struct bcm63xx_udc *udc)
+{
+ u32 i, val;
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+
+ if (cfg->ep_num < 0)
+ continue;
+
+ bcm63xx_ep_dma_select(udc, cfg->ep_num);
+ val = (cfg->ep_type << USBD_EPNUM_TYPEMAP_TYPE_SHIFT) |
+ ((i >> 1) << USBD_EPNUM_TYPEMAP_DMA_CH_SHIFT);
+ usbd_writel(udc, val, USBD_EPNUM_TYPEMAP_REG);
+ }
+}
+
+/**
+ * bcm63xx_ep_setup - Configure per-endpoint settings.
+ * @udc: Reference to the device controller.
+ *
+ * This needs to be rerun if the speed/cfg/intf/altintf changes.
+ */
+static void bcm63xx_ep_setup(struct bcm63xx_udc *udc)
+{
+ u32 val, i;
+
+ usbd_writel(udc, USBD_CSR_SETUPADDR_DEF, USBD_CSR_SETUPADDR_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[i];
+ int max_pkt = udc->gadget.speed == USB_SPEED_HIGH ?
+ cfg->max_pkt_hs : cfg->max_pkt_fs;
+ int idx = cfg->ep_num;
+
+ udc->iudma[i].max_pkt = max_pkt;
+
+ if (idx < 0)
+ continue;
+ udc->bep[idx].ep.maxpacket = max_pkt;
+
+ val = (idx << USBD_CSR_EP_LOG_SHIFT) |
+ (cfg->dir << USBD_CSR_EP_DIR_SHIFT) |
+ (cfg->ep_type << USBD_CSR_EP_TYPE_SHIFT) |
+ (udc->cfg << USBD_CSR_EP_CFG_SHIFT) |
+ (udc->iface << USBD_CSR_EP_IFACE_SHIFT) |
+ (udc->alt_iface << USBD_CSR_EP_ALTIFACE_SHIFT) |
+ (max_pkt << USBD_CSR_EP_MAXPKT_SHIFT);
+ usbd_writel(udc, val, USBD_CSR_EP_REG(idx));
+ }
+}
+
+/**
+ * iudma_write - Queue a single IUDMA transaction.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ * @breq: Request containing the transaction data.
+ *
+ * For RX IUDMA, this will queue a single buffer descriptor, as RX IUDMA
+ * does not honor SOP/EOP so the handling of multiple buffers is ambiguous.
+ * So iudma_write() may be called several times to fulfill a single
+ * usb_request.
+ *
+ * For TX IUDMA, this can queue multiple buffer descriptors if needed.
+ */
+static void iudma_write(struct bcm63xx_udc *udc, struct iudma_ch *iudma,
+ struct bcm63xx_req *breq)
+{
+ int first_bd = 1, last_bd = 0, extra_zero_pkt = 0;
+ unsigned int bytes_left = breq->req.length - breq->offset;
+ const int max_bd_bytes = !irq_coalesce && !iudma->is_tx ?
+ iudma->max_pkt : IUDMA_MAX_FRAGMENT;
+
+ iudma->n_bds_used = 0;
+ breq->bd_bytes = 0;
+ breq->iudma = iudma;
+
+ if ((bytes_left % iudma->max_pkt == 0) && bytes_left && breq->req.zero)
+ extra_zero_pkt = 1;
+
+ do {
+ struct bcm_enet_desc *d = iudma->write_bd;
+ u32 dmaflags = 0;
+ unsigned int n_bytes;
+
+ if (d == iudma->end_bd) {
+ dmaflags |= DMADESC_WRAP_MASK;
+ iudma->write_bd = iudma->bd_ring;
+ } else {
+ iudma->write_bd++;
+ }
+ iudma->n_bds_used++;
+
+ n_bytes = min_t(int, bytes_left, max_bd_bytes);
+ if (n_bytes)
+ dmaflags |= n_bytes << DMADESC_LENGTH_SHIFT;
+ else
+ dmaflags |= (1 << DMADESC_LENGTH_SHIFT) |
+ DMADESC_USB_ZERO_MASK;
+
+ dmaflags |= DMADESC_OWNER_MASK;
+ if (first_bd) {
+ dmaflags |= DMADESC_SOP_MASK;
+ first_bd = 0;
+ }
+
+ /*
+ * extra_zero_pkt forces one more iteration through the loop
+ * after all data is queued up, to send the zero packet
+ */
+ if (extra_zero_pkt && !bytes_left)
+ extra_zero_pkt = 0;
+
+ if (!iudma->is_tx || iudma->n_bds_used == iudma->n_bds ||
+ (n_bytes == bytes_left && !extra_zero_pkt)) {
+ last_bd = 1;
+ dmaflags |= DMADESC_EOP_MASK;
+ }
+
+ d->address = breq->req.dma + breq->offset;
+ mb();
+ d->len_stat = dmaflags;
+
+ breq->offset += n_bytes;
+ breq->bd_bytes += n_bytes;
+ bytes_left -= n_bytes;
+ } while (!last_bd);
+
+ usb_dmac_writel(udc, ENETDMAC_CHANCFG_EN_MASK,
+ ENETDMAC_CHANCFG_REG(iudma->ch_idx));
+}
+
+/**
+ * iudma_read - Check for IUDMA buffer completion.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to use.
+ *
+ * This checks to see if ALL of the outstanding BDs on the DMA channel
+ * have been filled. If so, it returns the actual transfer length;
+ * otherwise it returns -EBUSY.
+ */
+static int iudma_read(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+ int i, actual_len = 0;
+ struct bcm_enet_desc *d = iudma->read_bd;
+
+ if (!iudma->n_bds_used)
+ return -EINVAL;
+
+ for (i = 0; i < iudma->n_bds_used; i++) {
+ u32 dmaflags;
+
+ dmaflags = d->len_stat;
+
+ if (dmaflags & DMADESC_OWNER_MASK)
+ return -EBUSY;
+
+ actual_len += (dmaflags & DMADESC_LENGTH_MASK) >>
+ DMADESC_LENGTH_SHIFT;
+ if (d == iudma->end_bd)
+ d = iudma->bd_ring;
+ else
+ d++;
+ }
+
+ iudma->read_bd = d;
+ iudma->n_bds_used = 0;
+ return actual_len;
+}
+
+/**
+ * iudma_reset_channel - Stop DMA on a single channel.
+ * @udc: Reference to the device controller.
+ * @iudma: IUDMA channel to reset.
+ */
+static void iudma_reset_channel(struct bcm63xx_udc *udc, struct iudma_ch *iudma)
+{
+ int timeout = IUDMA_RESET_TIMEOUT_US;
+ struct bcm_enet_desc *d;
+ int ch_idx = iudma->ch_idx;
+
+ if (!iudma->is_tx)
+ bcm63xx_fifo_reset_ep(udc, max(0, iudma->ep_num));
+
+ /* stop DMA, then wait for the hardware to wrap up */
+ usb_dmac_writel(udc, 0, ENETDMAC_CHANCFG_REG(ch_idx));
+
+ while (usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)) &
+ ENETDMAC_CHANCFG_EN_MASK) {
+ udelay(1);
+
+ /* repeatedly flush the FIFO data until the BD completes */
+ if (iudma->is_tx && iudma->ep_num >= 0)
+ bcm63xx_fifo_reset_ep(udc, iudma->ep_num);
+
+ if (!timeout--) {
+ dev_err(udc->dev, "can't reset IUDMA channel %d\n",
+ ch_idx);
+ break;
+ }
+ if (timeout == IUDMA_RESET_TIMEOUT_US / 2) {
+ dev_warn(udc->dev, "forcibly halting IUDMA channel %d\n",
+ ch_idx);
+ usb_dmac_writel(udc, ENETDMAC_CHANCFG_BUFHALT_MASK,
+ ENETDMAC_CHANCFG_REG(ch_idx));
+ }
+ }
+ usb_dmac_writel(udc, ~0, ENETDMAC_IR_REG(ch_idx));
+
+ /* don't leave "live" HW-owned entries for the next guy to step on */
+ for (d = iudma->bd_ring; d <= iudma->end_bd; d++)
+ d->len_stat = 0;
+ mb();
+
+ iudma->read_bd = iudma->write_bd = iudma->bd_ring;
+ iudma->n_bds_used = 0;
+
+ /* set up IRQs, UBUS burst size, and BD base for this channel */
+ usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+ ENETDMAC_IRMASK_REG(ch_idx));
+ usb_dmac_writel(udc, 8, ENETDMAC_MAXBURST_REG(ch_idx));
+
+ usb_dmas_writel(udc, iudma->bd_ring_dma, ENETDMAS_RSTART_REG(ch_idx));
+ usb_dmas_writel(udc, 0, ENETDMAS_SRAM2_REG(ch_idx));
+}
+
+/**
+ * iudma_init_channel - One-time IUDMA channel initialization.
+ * @udc: Reference to the device controller.
+ * @ch_idx: Channel to initialize.
+ */
+static int iudma_init_channel(struct bcm63xx_udc *udc, unsigned int ch_idx)
+{
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+ const struct iudma_ch_cfg *cfg = &iudma_defaults[ch_idx];
+ unsigned int n_bds = cfg->n_bds;
+ struct bcm63xx_ep *bep = NULL;
+
+ iudma->ep_num = cfg->ep_num;
+ iudma->ch_idx = ch_idx;
+ iudma->is_tx = !!(ch_idx & 0x01);
+ if (iudma->ep_num >= 0) {
+ bep = &udc->bep[iudma->ep_num];
+ bep->iudma = iudma;
+ INIT_LIST_HEAD(&bep->queue);
+ }
+
+ iudma->bep = bep;
+ iudma->udc = udc;
+
+ /* ep0 is always active; others are controlled by the gadget driver */
+ if (iudma->ep_num <= 0)
+ iudma->enabled = true;
+
+ iudma->n_bds = n_bds;
+ iudma->bd_ring = dmam_alloc_coherent(udc->dev,
+ n_bds * sizeof(struct bcm_enet_desc),
+ &iudma->bd_ring_dma, GFP_KERNEL);
+ if (!iudma->bd_ring)
+ return -ENOMEM;
+ iudma->end_bd = &iudma->bd_ring[n_bds - 1];
+
+ return 0;
+}
+
+/**
+ * iudma_init - One-time initialization of all IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Enable DMA, flush channels, and enable global IUDMA IRQs.
+ */
+static int iudma_init(struct bcm63xx_udc *udc)
+{
+ int i, rc;
+
+ usb_dma_writel(udc, ENETDMA_CFG_EN_MASK, ENETDMA_CFG_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ rc = iudma_init_channel(udc, i);
+ if (rc)
+ return rc;
+ iudma_reset_channel(udc, &udc->iudma[i]);
+ }
+
+ usb_dma_writel(udc, BIT(BCM63XX_NUM_IUDMA)-1, ENETDMA_GLB_IRQMASK_REG);
+ return 0;
+}
+
+/**
+ * iudma_uninit - Uninitialize IUDMA channels.
+ * @udc: Reference to the device controller.
+ *
+ * Kill global IUDMA IRQs, flush channels, and kill DMA.
+ */
+static void iudma_uninit(struct bcm63xx_udc *udc)
+{
+ int i;
+
+ usb_dma_writel(udc, 0, ENETDMA_GLB_IRQMASK_REG);
+
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++)
+ iudma_reset_channel(udc, &udc->iudma[i]);
+
+ usb_dma_writel(udc, 0, ENETDMA_CFG_REG);
+}
+
+/***********************************************************************
+ * Other low-level USBD operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_set_ctrl_irqs - Mask/unmask control path interrupts.
+ * @udc: Reference to the device controller.
+ * @enable_irqs: true to enable, false to disable.
+ */
+static void bcm63xx_set_ctrl_irqs(struct bcm63xx_udc *udc, bool enable_irqs)
+{
+ u32 val;
+
+ usbd_writel(udc, 0, USBD_STATUS_REG);
+
+ val = BIT(USBD_EVENT_IRQ_USB_RESET) |
+ BIT(USBD_EVENT_IRQ_SETUP) |
+ BIT(USBD_EVENT_IRQ_SETCFG) |
+ BIT(USBD_EVENT_IRQ_SETINTF) |
+ BIT(USBD_EVENT_IRQ_USB_LINK);
+ usbd_writel(udc, enable_irqs ? val : 0, USBD_EVENT_IRQ_MASK_REG);
+ usbd_writel(udc, val, USBD_EVENT_IRQ_STATUS_REG);
+}
+
+/**
+ * bcm63xx_select_phy_mode - Select between USB device and host mode.
+ * @udc: Reference to the device controller.
+ * @is_device: true for device, false for host.
+ *
+ * This should probably be reworked to use the drivers/usb/otg
+ * infrastructure.
+ *
+ * By default, the AFE/pullups are disabled in device mode, until
+ * bcm63xx_select_pullup() is called.
+ */
+static void bcm63xx_select_phy_mode(struct bcm63xx_udc *udc, bool is_device)
+{
+ u32 val, portmask = BIT(udc->pd->port_no);
+
+ if (BCMCPU_IS_6328()) {
+ /* configure pinmux to sense VBUS signal */
+ val = bcm_gpio_readl(GPIO_PINMUX_OTHR_REG);
+ val &= ~GPIO_PINMUX_OTHR_6328_USB_MASK;
+ val |= is_device ? GPIO_PINMUX_OTHR_6328_USB_DEV :
+ GPIO_PINMUX_OTHR_6328_USB_HOST;
+ bcm_gpio_writel(val, GPIO_PINMUX_OTHR_REG);
+ }
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+ if (is_device) {
+ val |= (portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+ val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ } else {
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_HOSTB_SHIFT);
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ }
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_SWAP_6368_REG);
+ if (is_device)
+ val |= USBH_PRIV_SWAP_USBD_MASK;
+ else
+ val &= ~USBH_PRIV_SWAP_USBD_MASK;
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_SWAP_6368_REG);
+}
+
+/**
+ * bcm63xx_select_pullup - Enable/disable the pullup on D+
+ * @udc: Reference to the device controller.
+ * @is_on: true to enable the pullup, false to disable.
+ *
+ * If the pullup is active, the host will sense a FS/HS device connected to
+ * the port. If the pullup is inactive, the host will think the USB
+ * device has been disconnected.
+ */
+static void bcm63xx_select_pullup(struct bcm63xx_udc *udc, bool is_on)
+{
+ u32 val, portmask = BIT(udc->pd->port_no);
+
+ val = bcm_rset_readl(RSET_USBH_PRIV, USBH_PRIV_UTMI_CTL_6368_REG);
+ if (is_on)
+ val &= ~(portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ else
+ val |= (portmask << USBH_PRIV_UTMI_CTL_NODRIV_SHIFT);
+ bcm_rset_writel(RSET_USBH_PRIV, val, USBH_PRIV_UTMI_CTL_6368_REG);
+}
+
+/**
+ * bcm63xx_uninit_udc_hw - Shut down the hardware prior to driver removal.
+ * @udc: Reference to the device controller.
+ *
+ * This just masks the IUDMA IRQs and releases the clocks. It is assumed
+ * that bcm63xx_udc_stop() has already run, and the clocks are stopped.
+ */
+static void bcm63xx_uninit_udc_hw(struct bcm63xx_udc *udc)
+{
+ set_clocks(udc, true);
+ iudma_uninit(udc);
+ set_clocks(udc, false);
+
+ clk_put(udc->usbd_clk);
+ clk_put(udc->usbh_clk);
+}
+
+/**
+ * bcm63xx_init_udc_hw - Initialize the controller hardware and data structures.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc)
+{
+ int i, rc = 0;
+ u32 val;
+
+ udc->ep0_ctrl_buf = devm_kzalloc(udc->dev, BCM63XX_MAX_CTRL_PKT,
+ GFP_KERNEL);
+ if (!udc->ep0_ctrl_buf)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&udc->gadget.ep_list);
+ for (i = 0; i < BCM63XX_NUM_EP; i++) {
+ struct bcm63xx_ep *bep = &udc->bep[i];
+
+ bep->ep.name = bcm63xx_ep_name[i];
+ bep->ep_num = i;
+ bep->ep.ops = &bcm63xx_udc_ep_ops;
+ list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list);
+ bep->halted = 0;
+ bep->ep.maxpacket = BCM63XX_MAX_CTRL_PKT;
+ bep->udc = udc;
+ bep->ep.desc = NULL;
+ INIT_LIST_HEAD(&bep->queue);
+ }
+
+ udc->gadget.ep0 = &udc->bep[0].ep;
+ list_del(&udc->bep[0].ep.ep_list);
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->ep0state = EP0_SHUTDOWN;
+
+ udc->usbh_clk = clk_get(udc->dev, "usbh");
+ if (IS_ERR(udc->usbh_clk))
+ return -EIO;
+
+ udc->usbd_clk = clk_get(udc->dev, "usbd");
+ if (IS_ERR(udc->usbd_clk)) {
+ clk_put(udc->usbh_clk);
+ return -EIO;
+ }
+
+ set_clocks(udc, true);
+
+ val = USBD_CONTROL_AUTO_CSRS_MASK |
+ USBD_CONTROL_DONE_CSRS_MASK |
+ (irq_coalesce ? USBD_CONTROL_RXZSCFG_MASK : 0);
+ usbd_writel(udc, val, USBD_CONTROL_REG);
+
+ val = USBD_STRAPS_APP_SELF_PWR_MASK |
+ USBD_STRAPS_APP_RAM_IF_MASK |
+ USBD_STRAPS_APP_CSRPRGSUP_MASK |
+ USBD_STRAPS_APP_8BITPHY_MASK |
+ USBD_STRAPS_APP_RMTWKUP_MASK;
+
+ if (udc->gadget.max_speed == USB_SPEED_HIGH)
+ val |= (BCM63XX_SPD_HIGH << USBD_STRAPS_SPEED_SHIFT);
+ else
+ val |= (BCM63XX_SPD_FULL << USBD_STRAPS_SPEED_SHIFT);
+ usbd_writel(udc, val, USBD_STRAPS_REG);
+
+ bcm63xx_set_ctrl_irqs(udc, false);
+
+ usbd_writel(udc, 0, USBD_EVENT_IRQ_CFG_LO_REG);
+
+ val = USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_ENUM_ON) |
+ USBD_EVENT_IRQ_CFG_FALLING(USBD_EVENT_IRQ_SET_CSRS);
+ usbd_writel(udc, val, USBD_EVENT_IRQ_CFG_HI_REG);
+
+ rc = iudma_init(udc);
+ set_clocks(udc, false);
+ if (rc)
+ bcm63xx_uninit_udc_hw(udc);
+
+ return 0;
+}
+
+/***********************************************************************
+ * Standard EP gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep_enable - Enable one endpoint.
+ * @ep: Endpoint to enable.
+ * @desc: Contains max packet, direction, etc.
+ *
+ * Most of the endpoint parameters are fixed in this controller, so there
+ * isn't much for this function to do.
+ */
+static int bcm63xx_ep_enable(struct usb_ep *ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct iudma_ch *iudma = bep->iudma;
+ unsigned long flags;
+
+ if (!ep || !desc || ep->name == bcm63xx_ep0name)
+ return -EINVAL;
+
+ if (!udc->driver)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (iudma->enabled) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EINVAL;
+ }
+
+ iudma->enabled = true;
+ BUG_ON(!list_empty(&bep->queue));
+
+ iudma_reset_channel(udc, iudma);
+
+ bep->halted = 0;
+ bcm63xx_set_stall(udc, bep, false);
+ clear_bit(bep->ep_num, &udc->wedgemap);
+
+ ep->desc = desc;
+ ep->maxpacket = usb_endpoint_maxp(desc);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * bcm63xx_ep_disable - Disable one endpoint.
+ * @ep: Endpoint to disable.
+ */
+static int bcm63xx_ep_disable(struct usb_ep *ep)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct iudma_ch *iudma = bep->iudma;
+ struct list_head *pos, *n;
+ unsigned long flags;
+
+ if (!ep || !ep->desc)
+ return -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (!iudma->enabled) {
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return -EINVAL;
+ }
+ iudma->enabled = false;
+
+ iudma_reset_channel(udc, iudma);
+
+ if (!list_empty(&bep->queue)) {
+ list_for_each_safe(pos, n, &bep->queue) {
+ struct bcm63xx_req *breq =
+ list_entry(pos, struct bcm63xx_req, queue);
+
+ usb_gadget_unmap_request(&udc->gadget, &breq->req,
+ iudma->is_tx);
+ list_del(&breq->queue);
+ breq->req.status = -ESHUTDOWN;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ breq->req.complete(&iudma->bep->ep, &breq->req);
+ spin_lock_irqsave(&udc->lock, flags);
+ }
+ }
+ ep->desc = NULL;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_alloc_request - Allocate a new request.
+ * @ep: Endpoint associated with the request.
+ * @mem_flags: Flags to pass to kzalloc().
+ */
+static struct usb_request *bcm63xx_udc_alloc_request(struct usb_ep *ep,
+ gfp_t mem_flags)
+{
+ struct bcm63xx_req *breq;
+
+ breq = kzalloc(sizeof(*breq), mem_flags);
+ if (!breq)
+ return NULL;
+ return &breq->req;
+}
+
+/**
+ * bcm63xx_udc_free_request - Free a request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to free.
+ */
+static void bcm63xx_udc_free_request(struct usb_ep *ep,
+ struct usb_request *req)
+{
+ struct bcm63xx_req *breq = our_req(req);
+ kfree(breq);
+}
+
+/**
+ * bcm63xx_udc_queue - Queue up a new request.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to add.
+ * @mem_flags: Unused.
+ *
+ * If the queue is empty, start this request immediately. Otherwise, add
+ * it to the list.
+ *
+ * ep0 replies are sent through this function from the gadget driver, but
+ * they are treated differently because they need to be handled by the ep0
+ * state machine. (Sometimes they are replies to control requests that
+ * were spoofed by this driver, and so they shouldn't be transmitted at all.)
+ */
+static int bcm63xx_udc_queue(struct usb_ep *ep, struct usb_request *req,
+ gfp_t mem_flags)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct bcm63xx_req *breq = our_req(req);
+ unsigned long flags;
+ int rc = 0;
+
+ if (unlikely(!req || !req->complete || !req->buf || !ep))
+ return -EINVAL;
+
+ req->actual = 0;
+ req->status = 0;
+ breq->offset = 0;
+
+ if (bep == &udc->bep[0]) {
+ /* only one reply per request, please */
+ if (udc->ep0_reply)
+ return -EINVAL;
+
+ udc->ep0_reply = req;
+ schedule_work(&udc->ep0_wq);
+ return 0;
+ }
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (!bep->iudma->enabled) {
+ rc = -ESHUTDOWN;
+ goto out;
+ }
+
+ rc = usb_gadget_map_request(&udc->gadget, req, bep->iudma->is_tx);
+ if (rc == 0) {
+ list_add_tail(&breq->queue, &bep->queue);
+ if (list_is_singular(&bep->queue))
+ iudma_write(udc, bep->iudma, breq);
+ }
+
+out:
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_dequeue - Remove a pending request from the queue.
+ * @ep: Endpoint associated with the request.
+ * @req: Request to remove.
+ *
+ * If the request is not at the head of the queue, this is easy - just nuke
+ * it. If the request is at the head of the queue, we'll need to stop the
+ * DMA transaction and then queue up the successor.
+ */
+static int bcm63xx_udc_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ struct bcm63xx_req *breq = our_req(req), *cur;
+ unsigned long flags;
+ int rc = 0;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (list_empty(&bep->queue)) {
+ rc = -EINVAL;
+ goto out;
+ }
+
+ cur = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+ usb_gadget_unmap_request(&udc->gadget, &breq->req, bep->iudma->is_tx);
+
+ if (breq == cur) {
+ iudma_reset_channel(udc, bep->iudma);
+ list_del(&breq->queue);
+
+ if (!list_empty(&bep->queue)) {
+ struct bcm63xx_req *next;
+
+ next = list_first_entry(&bep->queue,
+ struct bcm63xx_req, queue);
+ iudma_write(udc, bep->iudma, next);
+ }
+ } else {
+ list_del(&breq->queue);
+ }
+
+out:
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ req->status = -ESHUTDOWN;
+ req->complete(ep, req);
+
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_set_halt - Enable/disable STALL flag in the hardware.
+ * @ep: Endpoint to halt.
+ * @value: Zero to clear halt; nonzero to set halt.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_halt(struct usb_ep *ep, int value)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ bcm63xx_set_stall(udc, bep, !!value);
+ bep->halted = value;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_set_wedge - Stall the endpoint until the next reset.
+ * @ep: Endpoint to wedge.
+ *
+ * See comments in bcm63xx_update_wedge().
+ */
+static int bcm63xx_udc_set_wedge(struct usb_ep *ep)
+{
+ struct bcm63xx_ep *bep = our_ep(ep);
+ struct bcm63xx_udc *udc = bep->udc;
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ set_bit(bep->ep_num, &udc->wedgemap);
+ bcm63xx_set_stall(udc, bep, true);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_ep_ops bcm63xx_udc_ep_ops = {
+ .enable = bcm63xx_ep_enable,
+ .disable = bcm63xx_ep_disable,
+
+ .alloc_request = bcm63xx_udc_alloc_request,
+ .free_request = bcm63xx_udc_free_request,
+
+ .queue = bcm63xx_udc_queue,
+ .dequeue = bcm63xx_udc_dequeue,
+
+ .set_halt = bcm63xx_udc_set_halt,
+ .set_wedge = bcm63xx_udc_set_wedge,
+};
+
+/***********************************************************************
+ * EP0 handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_ep0_setup_callback - Drop spinlock to invoke ->setup callback.
+ * @udc: Reference to the device controller.
+ * @ctrl: 8-byte SETUP request.
+ */
+static int bcm63xx_ep0_setup_callback(struct bcm63xx_udc *udc,
+ struct usb_ctrlrequest *ctrl)
+{
+ int rc;
+
+ spin_unlock_irq(&udc->lock);
+ rc = udc->driver->setup(&udc->gadget, ctrl);
+ spin_lock_irq(&udc->lock);
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_cfg - Synthesize a SET_CONFIGURATION request.
+ * @udc: Reference to the device controller.
+ *
+ * Many standard requests are handled automatically in the hardware, but
+ * we still need to pass them to the gadget driver so that it can
+ * reconfigure the interfaces/endpoints if necessary.
+ *
+ * Unfortunately we are not able to send a STALL response if the host
+ * requests an invalid configuration. If this happens, we'll have to be
+ * content with printing a warning.
+ */
+static int bcm63xx_ep0_spoof_set_cfg(struct bcm63xx_udc *udc)
+{
+ struct usb_ctrlrequest ctrl;
+ int rc;
+
+ ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_DEVICE;
+ ctrl.bRequest = USB_REQ_SET_CONFIGURATION;
+ ctrl.wValue = cpu_to_le16(udc->cfg);
+ ctrl.wIndex = 0;
+ ctrl.wLength = 0;
+
+ rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+ if (rc < 0) {
+ dev_warn_ratelimited(udc->dev,
+ "hardware auto-acked bad SET_CONFIGURATION(%d) request\n",
+ udc->cfg);
+ }
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_spoof_set_iface - Synthesize a SET_INTERFACE request.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_spoof_set_iface(struct bcm63xx_udc *udc)
+{
+ struct usb_ctrlrequest ctrl;
+ int rc;
+
+ ctrl.bRequestType = USB_DIR_OUT | USB_RECIP_INTERFACE;
+ ctrl.bRequest = USB_REQ_SET_INTERFACE;
+ ctrl.wValue = cpu_to_le16(udc->alt_iface);
+ ctrl.wIndex = cpu_to_le16(udc->iface);
+ ctrl.wLength = 0;
+
+ rc = bcm63xx_ep0_setup_callback(udc, &ctrl);
+ if (rc < 0) {
+ dev_warn_ratelimited(udc->dev,
+ "hardware auto-acked bad SET_INTERFACE(%d,%d) request\n",
+ udc->iface, udc->alt_iface);
+ }
+ return rc;
+}
+
+/**
+ * bcm63xx_ep0_map_write - dma_map and iudma_write a single request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @req: USB gadget layer representation of the request.
+ */
+static void bcm63xx_ep0_map_write(struct bcm63xx_udc *udc, int ch_idx,
+ struct usb_request *req)
+{
+ struct bcm63xx_req *breq = our_req(req);
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+
+ BUG_ON(udc->ep0_request);
+ udc->ep0_request = req;
+
+ req->actual = 0;
+ breq->offset = 0;
+ usb_gadget_map_request(&udc->gadget, req, iudma->is_tx);
+ iudma_write(udc, iudma, breq);
+}
+
+/**
+ * bcm63xx_ep0_complete - Set completion status and "stage" the callback.
+ * @udc: Reference to the device controller.
+ * @req: USB gadget layer representation of the request.
+ * @status: Status to return to the gadget driver.
+ */
+static void bcm63xx_ep0_complete(struct bcm63xx_udc *udc,
+ struct usb_request *req, int status)
+{
+ req->status = status;
+ if (status)
+ req->actual = 0;
+ if (req->complete) {
+ spin_unlock_irq(&udc->lock);
+ req->complete(&udc->bep[0].ep, req);
+ spin_lock_irq(&udc->lock);
+ }
+}
+
+/**
+ * bcm63xx_ep0_nuke_reply - Abort request from the gadget driver due to
+ * reset/shutdown.
+ * @udc: Reference to the device controller.
+ * @is_tx: Nonzero for TX (IN), zero for RX (OUT).
+ */
+static void bcm63xx_ep0_nuke_reply(struct bcm63xx_udc *udc, int is_tx)
+{
+ struct usb_request *req = udc->ep0_reply;
+
+ udc->ep0_reply = NULL;
+ usb_gadget_unmap_request(&udc->gadget, req, is_tx);
+ if (udc->ep0_request == req) {
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+ }
+ bcm63xx_ep0_complete(udc, req, -ESHUTDOWN);
+}
+
+/**
+ * bcm63xx_ep0_read_complete - Close out the pending ep0 request; return
+ * transfer len.
+ * @udc: Reference to the device controller.
+ */
+static int bcm63xx_ep0_read_complete(struct bcm63xx_udc *udc)
+{
+ struct usb_request *req = udc->ep0_request;
+
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+
+ return req->actual;
+}
+
+/**
+ * bcm63xx_ep0_internal_request - Helper function to submit an ep0 request.
+ * @udc: Reference to the device controller.
+ * @ch_idx: IUDMA channel number.
+ * @length: Number of bytes to TX/RX.
+ *
+ * Used for simple transfers performed by the ep0 worker. This will always
+ * use ep0_ctrl_req / ep0_ctrl_buf.
+ */
+static void bcm63xx_ep0_internal_request(struct bcm63xx_udc *udc, int ch_idx,
+ int length)
+{
+ struct usb_request *req = &udc->ep0_ctrl_req.req;
+
+ req->buf = udc->ep0_ctrl_buf;
+ req->length = length;
+ req->complete = NULL;
+
+ bcm63xx_ep0_map_write(udc, ch_idx, req);
+}
+
+/**
+ * bcm63xx_ep0_do_setup - Parse new SETUP packet and decide how to handle it.
+ * @udc: Reference to the device controller.
+ *
+ * EP0_IDLE probably shouldn't ever happen. EP0_REQUEUE means we're ready
+ * for the next packet. Anything else means the transaction requires multiple
+ * stages of handling.
+ */
+static enum bcm63xx_ep0_state bcm63xx_ep0_do_setup(struct bcm63xx_udc *udc)
+{
+ int rc;
+ struct usb_ctrlrequest *ctrl = (void *)udc->ep0_ctrl_buf;
+
+ rc = bcm63xx_ep0_read_complete(udc);
+
+ if (rc < 0) {
+ dev_err(udc->dev, "missing SETUP packet\n");
+ return EP0_IDLE;
+ }
+
+ /*
+ * Handle 0-byte IN STATUS acknowledgement. The hardware doesn't
+ * ALWAYS deliver these 100% of the time, so if we happen to see one,
+ * just throw it away.
+ */
+ if (rc == 0)
+ return EP0_REQUEUE;
+
+ /* Drop malformed SETUP packets */
+ if (rc != sizeof(*ctrl)) {
+ dev_warn_ratelimited(udc->dev,
+ "malformed SETUP packet (%d bytes)\n", rc);
+ return EP0_REQUEUE;
+ }
+
+ /* Process new SETUP packet arriving on ep0 */
+ rc = bcm63xx_ep0_setup_callback(udc, ctrl);
+ if (rc < 0) {
+ bcm63xx_set_stall(udc, &udc->bep[0], true);
+ return EP0_REQUEUE;
+ }
+
+ if (!ctrl->wLength)
+ return EP0_REQUEUE;
+ else if (ctrl->bRequestType & USB_DIR_IN)
+ return EP0_IN_DATA_PHASE_SETUP;
+ else
+ return EP0_OUT_DATA_PHASE_SETUP;
+}
+
+/**
+ * bcm63xx_ep0_do_idle - Check for outstanding requests if ep0 is idle.
+ * @udc: Reference to the device controller.
+ *
+ * In state EP0_IDLE, the RX descriptor is either pending, or has been
+ * filled with a SETUP packet from the host. This function handles new
+ * SETUP packets, control IRQ events (which can generate fake SETUP packets),
+ * and reset/shutdown events.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_do_idle(struct bcm63xx_udc *udc)
+{
+ if (udc->ep0_req_reset) {
+ udc->ep0_req_reset = 0;
+ } else if (udc->ep0_req_set_cfg) {
+ udc->ep0_req_set_cfg = 0;
+ if (bcm63xx_ep0_spoof_set_cfg(udc) >= 0)
+ udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+ } else if (udc->ep0_req_set_iface) {
+ udc->ep0_req_set_iface = 0;
+ if (bcm63xx_ep0_spoof_set_iface(udc) >= 0)
+ udc->ep0state = EP0_IN_FAKE_STATUS_PHASE;
+ } else if (udc->ep0_req_completed) {
+ udc->ep0state = bcm63xx_ep0_do_setup(udc);
+ return udc->ep0state == EP0_IDLE ? -EAGAIN : 0;
+ } else if (udc->ep0_req_shutdown) {
+ udc->ep0_req_shutdown = 0;
+ udc->ep0_req_completed = 0;
+ udc->ep0_request = NULL;
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+ usb_gadget_unmap_request(&udc->gadget,
+ &udc->ep0_ctrl_req.req, 0);
+
+ /* bcm63xx_udc_pullup() is waiting for this */
+ mb();
+ udc->ep0state = EP0_SHUTDOWN;
+ } else if (udc->ep0_reply) {
+ /*
+ * This could happen if a USB RESET shows up during an ep0
+ * transaction (especially if a laggy driver like gadgetfs
+ * is in use).
+ */
+ dev_warn(udc->dev, "nuking unexpected reply\n");
+ bcm63xx_ep0_nuke_reply(udc, 0);
+ } else {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+/**
+ * bcm63xx_ep0_one_round - Handle the current ep0 state.
+ * @udc: Reference to the device controller.
+ *
+ * Returns 0 if work was done; -EAGAIN if nothing to do.
+ */
+static int bcm63xx_ep0_one_round(struct bcm63xx_udc *udc)
+{
+ enum bcm63xx_ep0_state ep0state = udc->ep0state;
+ bool shutdown = udc->ep0_req_reset || udc->ep0_req_shutdown;
+
+ switch (udc->ep0state) {
+ case EP0_REQUEUE:
+ /* set up descriptor to receive SETUP packet */
+ bcm63xx_ep0_internal_request(udc, IUDMA_EP0_RXCHAN,
+ BCM63XX_MAX_CTRL_PKT);
+ ep0state = EP0_IDLE;
+ break;
+ case EP0_IDLE:
+ return bcm63xx_ep0_do_idle(udc);
+ case EP0_IN_DATA_PHASE_SETUP:
+ /*
+ * Normal case: TX request is in ep0_reply (queued by the
+ * callback), or will be queued shortly. When it's here,
+ * send it to the HW and go to EP0_IN_DATA_PHASE_COMPLETE.
+ *
+ * Shutdown case: Stop waiting for the reply. Just
+ * REQUEUE->IDLE. The gadget driver is NOT expected to
+ * queue anything else now.
+ */
+ if (udc->ep0_reply) {
+ bcm63xx_ep0_map_write(udc, IUDMA_EP0_TXCHAN,
+ udc->ep0_reply);
+ ep0state = EP0_IN_DATA_PHASE_COMPLETE;
+ } else if (shutdown) {
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_IN_DATA_PHASE_COMPLETE: {
+ /*
+ * Normal case: TX packet (ep0_reply) is in flight; wait for
+ * it to finish, then go back to REQUEUE->IDLE.
+ *
+ * Shutdown case: Reset the TX channel, send -ESHUTDOWN
+ * completion to the gadget driver, then REQUEUE->IDLE.
+ */
+ if (udc->ep0_req_completed) {
+ udc->ep0_reply = NULL;
+ bcm63xx_ep0_read_complete(udc);
+ /*
+ * the "ack" sometimes gets eaten (see
+ * bcm63xx_ep0_do_idle)
+ */
+ ep0state = EP0_REQUEUE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+ bcm63xx_ep0_nuke_reply(udc, 1);
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ }
+ case EP0_OUT_DATA_PHASE_SETUP:
+ /* Similar behavior to EP0_IN_DATA_PHASE_SETUP */
+ if (udc->ep0_reply) {
+ bcm63xx_ep0_map_write(udc, IUDMA_EP0_RXCHAN,
+ udc->ep0_reply);
+ ep0state = EP0_OUT_DATA_PHASE_COMPLETE;
+ } else if (shutdown) {
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_OUT_DATA_PHASE_COMPLETE: {
+ /* Similar behavior to EP0_IN_DATA_PHASE_COMPLETE */
+ if (udc->ep0_req_completed) {
+ udc->ep0_reply = NULL;
+ bcm63xx_ep0_read_complete(udc);
+
+ /* send 0-byte ack to host */
+ bcm63xx_ep0_internal_request(udc, IUDMA_EP0_TXCHAN, 0);
+ ep0state = EP0_OUT_STATUS_PHASE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_RXCHAN]);
+ bcm63xx_ep0_nuke_reply(udc, 0);
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ }
+ case EP0_OUT_STATUS_PHASE:
+ /*
+ * Normal case: 0-byte OUT ack packet is in flight; wait
+ * for it to finish, then go back to REQUEUE->IDLE.
+ *
+ * Shutdown case: just cancel the transmission. Don't bother
+ * calling the completion, because it originated from this
+ * function anyway. Then go back to REQUEUE->IDLE.
+ */
+ if (udc->ep0_req_completed) {
+ bcm63xx_ep0_read_complete(udc);
+ ep0state = EP0_REQUEUE;
+ } else if (shutdown) {
+ iudma_reset_channel(udc, &udc->iudma[IUDMA_EP0_TXCHAN]);
+ udc->ep0_request = NULL;
+ ep0state = EP0_REQUEUE;
+ }
+ break;
+ case EP0_IN_FAKE_STATUS_PHASE: {
+ /*
+ * Normal case: we spoofed a SETUP packet and are now
+ * waiting for the gadget driver to send a 0-byte reply.
+ * This doesn't actually get sent to the HW because the
+ * HW has already sent its own reply. Once we get the
+ * response, return to IDLE.
+ *
+ * Shutdown case: return to IDLE immediately.
+ *
+ * Note that the ep0 RX descriptor has remained queued
+ * (and possibly unfilled) during this entire transaction.
+ * The HW datapath (IUDMA) never even sees SET_CONFIGURATION
+ * or SET_INTERFACE transactions.
+ */
+ struct usb_request *r = udc->ep0_reply;
+
+ if (!r) {
+ if (shutdown)
+ ep0state = EP0_IDLE;
+ break;
+ }
+
+ bcm63xx_ep0_complete(udc, r, 0);
+ udc->ep0_reply = NULL;
+ ep0state = EP0_IDLE;
+ break;
+ }
+ case EP0_SHUTDOWN:
+ break;
+ }
+
+ if (udc->ep0state == ep0state)
+ return -EAGAIN;
+
+ udc->ep0state = ep0state;
+ return 0;
+}
+
+/**
+ * bcm63xx_ep0_process - ep0 worker thread / state machine.
+ * @w: Workqueue struct.
+ *
+ * bcm63xx_ep0_process is triggered any time an event occurs on ep0. It
+ * is used to synchronize ep0 events and ensure that both HW and SW events
+ * occur in a well-defined order. When the ep0 IUDMA queues are idle, it may
+ * synthesize SET_CONFIGURATION / SET_INTERFACE requests that were consumed
+ * by the USBD hardware.
+ *
+ * The worker function will continue iterating around the state machine
+ * until there is nothing left to do. Usually "nothing left to do" means
+ * that we're waiting for a new event from the hardware.
+ */
+static void bcm63xx_ep0_process(struct work_struct *w)
+{
+ struct bcm63xx_udc *udc = container_of(w, struct bcm63xx_udc, ep0_wq);
+ spin_lock_irq(&udc->lock);
+ while (bcm63xx_ep0_one_round(udc) == 0)
+ ;
+ spin_unlock_irq(&udc->lock);
+}
+
+/***********************************************************************
+ * Standard UDC gadget operations
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_get_frame - Read current SOF frame number from the HW.
+ * @gadget: USB slave device.
+ */
+static int bcm63xx_udc_get_frame(struct usb_gadget *gadget)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+
+ return (usbd_readl(udc, USBD_STATUS_REG) &
+ USBD_STATUS_SOF_MASK) >> USBD_STATUS_SOF_SHIFT;
+}
+
+/**
+ * bcm63xx_udc_pullup - Enable/disable pullup on D+ line.
+ * @gadget: USB slave device.
+ * @is_on: 0 to disable pullup, 1 to enable.
+ *
+ * See notes in bcm63xx_select_pullup().
+ */
+static int bcm63xx_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+ int i, rc = -EINVAL;
+
+ spin_lock_irqsave(&udc->lock, flags);
+ if (is_on && udc->ep0state == EP0_SHUTDOWN) {
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->ep0state = EP0_REQUEUE;
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_ep_setup(udc);
+
+ bitmap_zero(&udc->wedgemap, BCM63XX_NUM_EP);
+ for (i = 0; i < BCM63XX_NUM_EP; i++)
+ bcm63xx_set_stall(udc, &udc->bep[i], false);
+
+ bcm63xx_set_ctrl_irqs(udc, true);
+ bcm63xx_select_pullup(gadget_to_udc(gadget), true);
+ rc = 0;
+ } else if (!is_on && udc->ep0state != EP0_SHUTDOWN) {
+ bcm63xx_select_pullup(gadget_to_udc(gadget), false);
+
+ udc->ep0_req_shutdown = 1;
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ while (1) {
+ schedule_work(&udc->ep0_wq);
+ if (udc->ep0state == EP0_SHUTDOWN)
+ break;
+ msleep(50);
+ }
+ bcm63xx_set_ctrl_irqs(udc, false);
+ cancel_work_sync(&udc->ep0_wq);
+ return 0;
+ }
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_start - Start the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+
+ if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+ !driver->setup)
+ return -EINVAL;
+ if (!udc)
+ return -ENODEV;
+ if (udc->driver)
+ return -EBUSY;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ set_clocks(udc, true);
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_ep_init(udc);
+ bcm63xx_ep_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_select_phy_mode(udc, true);
+
+ udc->driver = driver;
+ driver->driver.bus = NULL;
+ udc->gadget.dev.driver = &driver->driver;
+ udc->gadget.dev.of_node = udc->dev->of_node;
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+/**
+ * bcm63xx_udc_stop - Shut down the controller.
+ * @gadget: USB slave device.
+ * @driver: Driver for USB slave devices.
+ */
+static int bcm63xx_udc_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct bcm63xx_udc *udc = gadget_to_udc(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+
+ udc->driver = NULL;
+ udc->gadget.dev.driver = NULL;
+
+ /*
+ * If we switch the PHY too abruptly after dropping D+, the host
+ * will often complain:
+ *
+ * hub 1-0:1.0: port 1 disabled by hub (EMI?), re-enabling...
+ */
+ msleep(100);
+
+ bcm63xx_select_phy_mode(udc, false);
+ set_clocks(udc, false);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+}
+
+static const struct usb_gadget_ops bcm63xx_udc_ops = {
+ .get_frame = bcm63xx_udc_get_frame,
+ .pullup = bcm63xx_udc_pullup,
+ .udc_start = bcm63xx_udc_start,
+ .udc_stop = bcm63xx_udc_stop,
+};
+
+/***********************************************************************
+ * IRQ handling
+ ***********************************************************************/
+
+/**
+ * bcm63xx_update_cfg_iface - Read current configuration/interface settings.
+ * @udc: Reference to the device controller.
+ *
+ * This controller intercepts SET_CONFIGURATION and SET_INTERFACE messages.
+ * The driver never sees the raw control packets coming in on the ep0
+ * IUDMA channel, but at least we get an interrupt event to tell us that
+ * new values are waiting in the USBD_STATUS register.
+ */
+static void bcm63xx_update_cfg_iface(struct bcm63xx_udc *udc)
+{
+ u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+
+ udc->cfg = (reg & USBD_STATUS_CFG_MASK) >> USBD_STATUS_CFG_SHIFT;
+ udc->iface = (reg & USBD_STATUS_INTF_MASK) >> USBD_STATUS_INTF_SHIFT;
+ udc->alt_iface = (reg & USBD_STATUS_ALTINTF_MASK) >>
+ USBD_STATUS_ALTINTF_SHIFT;
+ bcm63xx_ep_setup(udc);
+}
+
+/**
+ * bcm63xx_update_link_speed - Check to see if the link speed has changed.
+ * @udc: Reference to the device controller.
+ *
+ * The link speed update coincides with a SETUP IRQ. Returns 1 if the
+ * speed has changed, so that the caller can update the endpoint settings.
+ */
+static int bcm63xx_update_link_speed(struct bcm63xx_udc *udc)
+{
+ u32 reg = usbd_readl(udc, USBD_STATUS_REG);
+ enum usb_device_speed oldspeed = udc->gadget.speed;
+
+ switch ((reg & USBD_STATUS_SPD_MASK) >> USBD_STATUS_SPD_SHIFT) {
+ case BCM63XX_SPD_HIGH:
+ udc->gadget.speed = USB_SPEED_HIGH;
+ break;
+ case BCM63XX_SPD_FULL:
+ udc->gadget.speed = USB_SPEED_FULL;
+ break;
+ default:
+ /* this should never happen */
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ dev_err(udc->dev,
+ "received SETUP packet with invalid link speed\n");
+ return 0;
+ }
+
+ if (udc->gadget.speed != oldspeed) {
+ dev_info(udc->dev, "link up, %s-speed mode\n",
+ udc->gadget.speed == USB_SPEED_HIGH ? "high" : "full");
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * bcm63xx_update_wedge - Iterate through wedged endpoints.
+ * @udc: Reference to the device controller.
+ * @new_status: true to "refresh" wedge status; false to clear it.
+ *
+ * On a SETUP interrupt, we need to manually "refresh" the wedge status
+ * because the controller hardware is designed to automatically clear
+ * stalls in response to a CLEAR_FEATURE request from the host.
+ *
+ * On a RESET interrupt, we do want to restore all wedged endpoints.
+ */
+static void bcm63xx_update_wedge(struct bcm63xx_udc *udc, bool new_status)
+{
+ int i;
+
+ for_each_set_bit(i, &udc->wedgemap, BCM63XX_NUM_EP) {
+ bcm63xx_set_stall(udc, &udc->bep[i], new_status);
+ if (!new_status)
+ clear_bit(i, &udc->wedgemap);
+ }
+}
+
+/**
+ * bcm63xx_udc_ctrl_isr - ISR for control path events (USBD).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the device controller.
+ *
+ * This is where we handle link (VBUS) down, USB reset, speed changes,
+ * SET_CONFIGURATION, and SET_INTERFACE events.
+ */
+static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
+{
+ struct bcm63xx_udc *udc = dev_id;
+ u32 stat;
+ bool disconnected = false;
+
+ stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
+ usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
+
+ usbd_writel(udc, stat, USBD_EVENT_IRQ_STATUS_REG);
+
+ spin_lock(&udc->lock);
+ if (stat & BIT(USBD_EVENT_IRQ_USB_LINK)) {
+ /* VBUS toggled */
+
+ if (!(usbd_readl(udc, USBD_EVENTS_REG) &
+ USBD_EVENTS_USB_LINK_MASK) &&
+ udc->gadget.speed != USB_SPEED_UNKNOWN)
+ dev_info(udc->dev, "link down\n");
+
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ disconnected = true;
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_USB_RESET)) {
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_fifo_reset(udc);
+ bcm63xx_ep_setup(udc);
+
+ bcm63xx_update_wedge(udc, false);
+
+ udc->ep0_req_reset = 1;
+ schedule_work(&udc->ep0_wq);
+ disconnected = true;
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
+ if (bcm63xx_update_link_speed(udc)) {
+ bcm63xx_fifo_setup(udc);
+ bcm63xx_ep_setup(udc);
+ }
+ bcm63xx_update_wedge(udc, true);
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETCFG)) {
+ bcm63xx_update_cfg_iface(udc);
+ udc->ep0_req_set_cfg = 1;
+ schedule_work(&udc->ep0_wq);
+ }
+ if (stat & BIT(USBD_EVENT_IRQ_SETINTF)) {
+ bcm63xx_update_cfg_iface(udc);
+ udc->ep0_req_set_iface = 1;
+ schedule_work(&udc->ep0_wq);
+ }
+ spin_unlock(&udc->lock);
+
+ if (disconnected && udc->driver)
+ udc->driver->disconnect(&udc->gadget);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * bcm63xx_udc_data_isr - ISR for data path events (IUDMA).
+ * @irq: IRQ number (unused).
+ * @dev_id: Reference to the IUDMA channel that generated the interrupt.
+ *
+ * For the two ep0 channels, we have special handling that triggers the
+ * ep0 worker thread. For normal bulk/intr channels, either queue up
+ * the next buffer descriptor for the transaction (incomplete transaction),
+ * or invoke the completion callback (complete transactions).
+ */
+static irqreturn_t bcm63xx_udc_data_isr(int irq, void *dev_id)
+{
+ struct iudma_ch *iudma = dev_id;
+ struct bcm63xx_udc *udc = iudma->udc;
+ struct bcm63xx_ep *bep;
+ struct usb_request *req = NULL;
+ struct bcm63xx_req *breq = NULL;
+ int rc;
+ bool is_done = false;
+
+ spin_lock(&udc->lock);
+
+ usb_dmac_writel(udc, ENETDMAC_IR_BUFDONE_MASK,
+ ENETDMAC_IR_REG(iudma->ch_idx));
+ bep = iudma->bep;
+ rc = iudma_read(udc, iudma);
+
+ /* special handling for EP0 RX (0) and TX (1) */
+ if (iudma->ch_idx == IUDMA_EP0_RXCHAN ||
+ iudma->ch_idx == IUDMA_EP0_TXCHAN) {
+ req = udc->ep0_request;
+ breq = our_req(req);
+
+ /* a single request could require multiple submissions */
+ if (rc >= 0) {
+ req->actual += rc;
+
+ if (req->actual >= req->length || breq->bd_bytes > rc) {
+ udc->ep0_req_completed = 1;
+ is_done = true;
+ schedule_work(&udc->ep0_wq);
+
+ /* "actual" on a ZLP is 1 byte */
+ req->actual = min(req->actual, req->length);
+ } else {
+ /* queue up the next BD (same request) */
+ iudma_write(udc, iudma, breq);
+ }
+ }
+ } else if (!list_empty(&bep->queue)) {
+ breq = list_first_entry(&bep->queue, struct bcm63xx_req, queue);
+ req = &breq->req;
+
+ if (rc >= 0) {
+ req->actual += rc;
+
+ if (req->actual >= req->length || breq->bd_bytes > rc) {
+ is_done = true;
+ list_del(&breq->queue);
+
+ req->actual = min(req->actual, req->length);
+
+ if (!list_empty(&bep->queue)) {
+ struct bcm63xx_req *next;
+
+ next = list_first_entry(&bep->queue,
+ struct bcm63xx_req, queue);
+ iudma_write(udc, iudma, next);
+ }
+ } else {
+ iudma_write(udc, iudma, breq);
+ }
+ }
+ }
+ spin_unlock(&udc->lock);
+
+ if (is_done) {
+ usb_gadget_unmap_request(&udc->gadget, req, iudma->is_tx);
+ if (req->complete)
+ req->complete(&bep->ep, req);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/***********************************************************************
+ * Debug filesystem
+ ***********************************************************************/
+
+/*
+ * bcm63xx_usbd_dbg_show - Show USBD controller state.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/usbd
+ */
+static int bcm63xx_usbd_dbg_show(struct seq_file *s, void *p)
+{
+ struct bcm63xx_udc *udc = s->private;
+
+ if (!udc->driver)
+ return -ENODEV;
+
+ seq_printf(s, "ep0 state: %s\n",
+ bcm63xx_ep0_state_names[udc->ep0state]);
+ seq_printf(s, " pending requests: %s%s%s%s%s%s%s\n",
+ udc->ep0_req_reset ? "reset " : "",
+ udc->ep0_req_set_cfg ? "set_cfg " : "",
+ udc->ep0_req_set_iface ? "set_iface " : "",
+ udc->ep0_req_shutdown ? "shutdown " : "",
+ udc->ep0_request ? "pending " : "",
+ udc->ep0_req_completed ? "completed " : "",
+ udc->ep0_reply ? "reply " : "");
+ seq_printf(s, "cfg: %d; iface: %d; alt_iface: %d\n",
+ udc->cfg, udc->iface, udc->alt_iface);
+ seq_printf(s, "regs:\n");
+ seq_printf(s, " control: %08x; straps: %08x; status: %08x\n",
+ usbd_readl(udc, USBD_CONTROL_REG),
+ usbd_readl(udc, USBD_STRAPS_REG),
+ usbd_readl(udc, USBD_STATUS_REG));
+ seq_printf(s, " events: %08x; stall: %08x\n",
+ usbd_readl(udc, USBD_EVENTS_REG),
+ usbd_readl(udc, USBD_STALL_REG));
+
+ return 0;
+}
+
+/*
+ * bcm63xx_iudma_dbg_show - Show IUDMA status and descriptors.
+ * @s: seq_file to which the information will be written.
+ * @p: Unused.
+ *
+ * This file nominally shows up as /sys/kernel/debug/bcm63xx_udc/iudma
+ */
+static int bcm63xx_iudma_dbg_show(struct seq_file *s, void *p)
+{
+ struct bcm63xx_udc *udc = s->private;
+ int ch_idx, i;
+ u32 sram2, sram3;
+
+ if (!udc->driver)
+ return -ENODEV;
+
+ for (ch_idx = 0; ch_idx < BCM63XX_NUM_IUDMA; ch_idx++) {
+ struct iudma_ch *iudma = &udc->iudma[ch_idx];
+ struct list_head *pos;
+
+ seq_printf(s, "IUDMA channel %d -- ", ch_idx);
+ switch (iudma_defaults[ch_idx].ep_type) {
+ case BCMEP_CTRL:
+ seq_printf(s, "control");
+ break;
+ case BCMEP_BULK:
+ seq_printf(s, "bulk");
+ break;
+ case BCMEP_INTR:
+ seq_printf(s, "interrupt");
+ break;
+ }
+ seq_printf(s, ch_idx & 0x01 ? " tx" : " rx");
+ seq_printf(s, " [ep%d]:\n",
+ max_t(int, iudma_defaults[ch_idx].ep_num, 0));
+ seq_printf(s, " cfg: %08x; irqstat: %08x; irqmask: %08x; maxburst: %08x\n",
+ usb_dmac_readl(udc, ENETDMAC_CHANCFG_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_IR_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_IRMASK_REG(ch_idx)),
+ usb_dmac_readl(udc, ENETDMAC_MAXBURST_REG(ch_idx)));
+
+ sram2 = usb_dmas_readl(udc, ENETDMAS_SRAM2_REG(ch_idx));
+ sram3 = usb_dmas_readl(udc, ENETDMAS_SRAM3_REG(ch_idx));
+ seq_printf(s, " base: %08x; index: %04x_%04x; desc: %04x_%04x %08x\n",
+ usb_dmas_readl(udc, ENETDMAS_RSTART_REG(ch_idx)),
+ sram2 >> 16, sram2 & 0xffff,
+ sram3 >> 16, sram3 & 0xffff,
+ usb_dmas_readl(udc, ENETDMAS_SRAM4_REG(ch_idx)));
+ seq_printf(s, " desc: %d/%d used", iudma->n_bds_used,
+ iudma->n_bds);
+
+ if (iudma->bep) {
+ i = 0;
+ list_for_each(pos, &iudma->bep->queue)
+ i++;
+ seq_printf(s, "; %d queued\n", i);
+ } else {
+ seq_printf(s, "\n");
+ }
+
+ for (i = 0; i < iudma->n_bds; i++) {
+ struct bcm_enet_desc *d = &iudma->bd_ring[i];
+
+ seq_printf(s, " %03x (%02x): len_stat: %04x_%04x; pa %08x",
+ i * sizeof(*d), i,
+ d->len_stat >> 16, d->len_stat & 0xffff,
+ d->address);
+ if (d == iudma->read_bd)
+ seq_printf(s, " <<RD");
+ if (d == iudma->write_bd)
+ seq_printf(s, " <<WR");
+ seq_printf(s, "\n");
+ }
+
+ seq_printf(s, "\n");
+ }
+
+ return 0;
+}
+
+static int bcm63xx_usbd_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm63xx_usbd_dbg_show, inode->i_private);
+}
+
+static int bcm63xx_iudma_dbg_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, bcm63xx_iudma_dbg_show, inode->i_private);
+}
+
+static const struct file_operations usbd_dbg_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm63xx_usbd_dbg_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static const struct file_operations iudma_dbg_fops = {
+ .owner = THIS_MODULE,
+ .open = bcm63xx_iudma_dbg_open,
+ .llseek = seq_lseek,
+ .read = seq_read,
+ .release = single_release,
+};
+
+
+/**
+ * bcm63xx_udc_init_debugfs - Create debugfs entries.
+ * @udc: Reference to the device controller.
+ */
+static void bcm63xx_udc_init_debugfs(struct bcm63xx_udc *udc)
+{
+ struct dentry *root, *usbd, *iudma;
+
+ if (!IS_ENABLED(CONFIG_USB_GADGET_DEBUG_FS))
+ return;
+
+ root = debugfs_create_dir(udc->gadget.name, NULL);
+ if (IS_ERR(root) || !root)
+ goto err_root;
+
+ usbd = debugfs_create_file("usbd", 0400, root, udc,
+ &usbd_dbg_fops);
+ if (!usbd)
+ goto err_usbd;
+ iudma = debugfs_create_file("iudma", 0400, root, udc,
+ &iudma_dbg_fops);
+ if (!iudma)
+ goto err_iudma;
+
+ udc->debugfs_root = root;
+ udc->debugfs_usbd = usbd;
+ udc->debugfs_iudma = iudma;
+ return;
+err_iudma:
+ debugfs_remove(usbd);
+err_usbd:
+ debugfs_remove(root);
+err_root:
+ dev_err(udc->dev, "debugfs is not available\n");
+}
+
+/**
+ * bcm63xx_udc_cleanup_debugfs - Remove debugfs entries.
+ * @udc: Reference to the device controller.
+ *
+ * debugfs_remove() is safe to call with a NULL argument.
+ */
+static void bcm63xx_udc_cleanup_debugfs(struct bcm63xx_udc *udc)
+{
+ debugfs_remove(udc->debugfs_iudma);
+ debugfs_remove(udc->debugfs_usbd);
+ debugfs_remove(udc->debugfs_root);
+ udc->debugfs_iudma = NULL;
+ udc->debugfs_usbd = NULL;
+ udc->debugfs_root = NULL;
+}
+
+/***********************************************************************
+ * Driver init/exit
+ ***********************************************************************/
+
+/**
+ * bcm63xx_udc_gadget_release - Called from device_release().
+ * @dev: Unused.
+ *
+ * We get a warning if this function doesn't exist, but it's empty because
+ * we don't have to free any of the memory allocated with the devm_* APIs.
+ */
+static void bcm63xx_udc_gadget_release(struct device *dev)
+{
+}
+
+/**
+ * bcm63xx_udc_probe - Initialize a new instance of the UDC.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ *
+ * Note that platform data is required, because pd.port_no varies from chip
+ * to chip and is used to switch the correct USB port to device mode.
+ */
+static int __devinit bcm63xx_udc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct bcm63xx_usbd_platform_data *pd = dev->platform_data;
+ struct bcm63xx_udc *udc;
+ struct resource *res;
+ int rc = -ENOMEM, i, irq;
+
+ udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
+ if (!udc) {
+ dev_err(dev, "cannot allocate memory\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, udc);
+ udc->dev = dev;
+ udc->pd = pd;
+
+ if (!pd) {
+ dev_err(dev, "missing platform data\n");
+ return -EINVAL;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "error finding USBD resource\n");
+ return -ENXIO;
+ }
+ udc->usbd_regs = devm_request_and_ioremap(dev, res);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res) {
+ dev_err(dev, "error finding IUDMA resource\n");
+ return -ENXIO;
+ }
+ udc->iudma_regs = devm_request_and_ioremap(dev, res);
+
+ if (!udc->usbd_regs || !udc->iudma_regs) {
+ dev_err(dev, "error requesting resources\n");
+ return -ENXIO;
+ }
+
+ spin_lock_init(&udc->lock);
+ INIT_WORK(&udc->ep0_wq, bcm63xx_ep0_process);
+ dev_set_name(&udc->gadget.dev, "gadget");
+
+ udc->gadget.ops = &bcm63xx_udc_ops;
+ udc->gadget.name = dev_name(dev);
+ udc->gadget.dev.parent = dev;
+ udc->gadget.dev.release = bcm63xx_udc_gadget_release;
+ udc->gadget.dev.dma_mask = dev->dma_mask;
+
+ if (!pd->use_fullspeed && !use_fullspeed)
+ udc->gadget.max_speed = USB_SPEED_HIGH;
+ else
+ udc->gadget.max_speed = USB_SPEED_FULL;
+
+ /* request clocks, allocate buffers, and clear any pending IRQs */
+ rc = bcm63xx_init_udc_hw(udc);
+ if (rc)
+ return rc;
+
+ rc = -ENXIO;
+
+ /* IRQ resource #0: control interrupt (VBUS, speed, etc.) */
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "missing IRQ resource #0\n");
+ goto out_uninit;
+ }
+ if (devm_request_irq(dev, irq, &bcm63xx_udc_ctrl_isr, 0,
+ dev_name(dev), udc) < 0) {
+ dev_err(dev, "error requesting IRQ #%d\n", irq);
+ goto out_uninit;
+ }
+
+ /* IRQ resources #1-6: data interrupts for IUDMA channels 0-5 */
+ for (i = 0; i < BCM63XX_NUM_IUDMA; i++) {
+ irq = platform_get_irq(pdev, i + 1);
+ if (irq < 0) {
+ dev_err(dev, "missing IRQ resource #%d\n", i + 1);
+ goto out_uninit;
+ }
+ if (devm_request_irq(dev, irq, &bcm63xx_udc_data_isr, 0,
+ dev_name(dev), &udc->iudma[i]) < 0) {
+ dev_err(dev, "error requesting IRQ #%d\n", irq);
+ goto out_uninit;
+ }
+ }
+
+ rc = device_register(&udc->gadget.dev);
+ if (rc)
+ goto out_uninit;
+
+ bcm63xx_udc_init_debugfs(udc);
+ rc = usb_add_gadget_udc(dev, &udc->gadget);
+ if (!rc)
+ return 0;
+
+ bcm63xx_udc_cleanup_debugfs(udc);
+ device_unregister(&udc->gadget.dev);
+out_uninit:
+ bcm63xx_uninit_udc_hw(udc);
+ return rc;
+}
+
+/**
+ * bcm63xx_udc_remove - Remove the device from the system.
+ * @pdev: Platform device struct from the bcm63xx BSP code.
+ */
+static int __devexit bcm63xx_udc_remove(struct platform_device *pdev)
+{
+ struct bcm63xx_udc *udc = platform_get_drvdata(pdev);
+
+ bcm63xx_udc_cleanup_debugfs(udc);
+ usb_del_gadget_udc(&udc->gadget);
+ device_unregister(&udc->gadget.dev);
+ BUG_ON(udc->driver);
+
+ platform_set_drvdata(pdev, NULL);
+ bcm63xx_uninit_udc_hw(udc);
+
+ return 0;
+}
+
+static struct platform_driver bcm63xx_udc_driver = {
+ .probe = bcm63xx_udc_probe,
+ .remove = __devexit_p(bcm63xx_udc_remove),
+ .driver = {
+ .name = DRV_MODULE_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(bcm63xx_udc_driver);
+
+MODULE_DESCRIPTION("BCM63xx USB Peripheral Controller");
+MODULE_AUTHOR("Kevin Cernekee <cernekee@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_MODULE_NAME);
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
index 725550f..1e4bb77 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/cdc2.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include "u_ether.h"
@@ -34,6 +33,7 @@
#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/*
* Kbuild is not very cooperative with respect to linking separately
@@ -43,10 +43,6 @@
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "u_serial.c"
#include "f_acm.c"
#include "f_ecm.c"
@@ -92,15 +88,10 @@
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -152,7 +143,6 @@
static int __init cdc_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -172,47 +162,22 @@
if (status < 0)
goto fail0;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- WARNING(cdev, "controller '%s' not recognized; trying %s\n",
- gadget->name,
- cdc_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail1;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail1;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &cdc_config_driver, cdc_do_config);
if (status < 0)
goto fail1;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
@@ -232,11 +197,12 @@
return 0;
}
-static struct usb_composite_driver cdc_driver = {
+static __refdata struct usb_composite_driver cdc_driver = {
.name = "g_cdc",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = cdc_bind,
.unbind = __exit_p(cdc_unbind),
};
@@ -246,7 +212,7 @@
static int __init init(void)
{
- return usb_composite_probe(&cdc_driver, cdc_bind);
+ return usb_composite_probe(&cdc_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 3f72110..957f973 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -28,44 +28,6 @@
* with the relevant device-wide data.
*/
-/* big enough to hold our biggest descriptor */
-#define USB_BUFSIZ 1024
-
-static struct usb_composite_driver *composite;
-static int (*composite_gadget_bind)(struct usb_composite_dev *cdev);
-
-/* Some systems will need runtime overrides for the product identifiers
- * published in the device descriptor, either numbers or strings or both.
- * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
- */
-
-static ushort idVendor;
-module_param(idVendor, ushort, 0644);
-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
-
-static ushort idProduct;
-module_param(idProduct, ushort, 0644);
-MODULE_PARM_DESC(idProduct, "USB Product ID");
-
-static ushort bcdDevice;
-module_param(bcdDevice, ushort, 0644);
-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
-
-static char *iManufacturer;
-module_param(iManufacturer, charp, 0644);
-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
-
-static char *iProduct;
-module_param(iProduct, charp, 0644);
-MODULE_PARM_DESC(iProduct, "USB Product string");
-
-static char *iSerialNumber;
-module_param(iSerialNumber, charp, 0644);
-MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
-
-static char composite_manufacturer[50];
-
-/*-------------------------------------------------------------------------*/
/**
* next_ep_desc() - advance to the next EP descriptor
* @t: currect pointer within descriptor array
@@ -192,6 +154,7 @@
}
return 0;
}
+EXPORT_SYMBOL_GPL(config_ep_by_speed);
/**
* usb_add_function() - add a function to a configuration
@@ -250,6 +213,7 @@
function->name, function, value);
return value;
}
+EXPORT_SYMBOL_GPL(usb_add_function);
/**
* usb_function_deactivate - prevent function and gadget enumeration
@@ -286,6 +250,7 @@
spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
+EXPORT_SYMBOL_GPL(usb_function_deactivate);
/**
* usb_function_activate - allow function and gadget enumeration
@@ -300,9 +265,10 @@
int usb_function_activate(struct usb_function *function)
{
struct usb_composite_dev *cdev = function->config->cdev;
+ unsigned long flags;
int status = 0;
- spin_lock(&cdev->lock);
+ spin_lock_irqsave(&cdev->lock, flags);
if (WARN_ON(cdev->deactivations == 0))
status = -EINVAL;
@@ -312,9 +278,10 @@
status = usb_gadget_connect(cdev->gadget);
}
- spin_unlock(&cdev->lock);
+ spin_unlock_irqrestore(&cdev->lock, flags);
return status;
}
+EXPORT_SYMBOL_GPL(usb_function_activate);
/**
* usb_interface_id() - allocate an unused interface ID
@@ -351,16 +318,18 @@
}
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(usb_interface_id);
static int config_buf(struct usb_configuration *config,
enum usb_device_speed speed, void *buf, u8 type)
{
struct usb_config_descriptor *c = buf;
void *next = buf + USB_DT_CONFIG_SIZE;
- int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
+ int len;
struct usb_function *f;
int status;
+ len = USB_COMP_EP0_BUFSIZ - USB_DT_CONFIG_SIZE;
/* write the config descriptor */
c = buf;
c->bLength = USB_DT_CONFIG_SIZE;
@@ -790,6 +759,7 @@
config->bConfigurationValue, status);
return status;
}
+EXPORT_SYMBOL_GPL(usb_add_config);
static void remove_config(struct usb_composite_dev *cdev,
struct usb_configuration *config)
@@ -889,10 +859,10 @@
static int get_string(struct usb_composite_dev *cdev,
void *buf, u16 language, int id)
{
+ struct usb_composite_driver *composite = cdev->driver;
struct usb_configuration *c;
struct usb_function *f;
int len;
- const char *str;
/* Yes, not only is USB's I18N support probably more than most
* folk will ever care about ... also, it's all supported here.
@@ -932,26 +902,6 @@
return s->bLength;
}
- /* Otherwise, look up and return a specified string. First
- * check if the string has not been overridden.
- */
- if (cdev->manufacturer_override == id)
- str = iManufacturer ?: composite->iManufacturer ?:
- composite_manufacturer;
- else if (cdev->product_override == id)
- str = iProduct ?: composite->iProduct;
- else if (cdev->serial_override == id)
- str = iSerialNumber ?: composite->iSerialNumber;
- else
- str = NULL;
- if (str) {
- struct usb_gadget_strings strings = {
- .language = language,
- .strings = &(struct usb_string) { 0xff, str }
- };
- return usb_gadget_get_string(&strings, 0xff, buf);
- }
-
/* String IDs are device-scoped, so we look up each string
* table we're told about. These lookups are infrequent;
* simpler-is-better here.
@@ -1003,6 +953,7 @@
}
return -ENODEV;
}
+EXPORT_SYMBOL_GPL(usb_string_id);
/**
* usb_string_ids() - allocate unused string IDs in batch
@@ -1034,6 +985,7 @@
return 0;
}
+EXPORT_SYMBOL_GPL(usb_string_ids_tab);
/**
* usb_string_ids_n() - allocate unused string IDs in batch
@@ -1062,7 +1014,7 @@
c->next_string_id += n;
return next + 1;
}
-
+EXPORT_SYMBOL_GPL(usb_string_ids_n);
/*-------------------------------------------------------------------------*/
@@ -1359,8 +1311,8 @@
spin_lock_irqsave(&cdev->lock, flags);
if (cdev->config)
reset_config(cdev);
- if (composite->disconnect)
- composite->disconnect(cdev);
+ if (cdev->driver->disconnect)
+ cdev->driver->disconnect(cdev);
spin_unlock_irqrestore(&cdev->lock, flags);
}
@@ -1396,35 +1348,67 @@
struct usb_configuration, list);
remove_config(cdev, c);
}
- if (composite->unbind)
- composite->unbind(cdev);
+ if (cdev->driver->unbind)
+ cdev->driver->unbind(cdev);
if (cdev->req) {
kfree(cdev->req->buf);
usb_ep_free_request(gadget->ep0, cdev->req);
}
device_remove_file(&gadget->dev, &dev_attr_suspended);
+ kfree(cdev->def_manufacturer);
kfree(cdev);
set_gadget_data(gadget, NULL);
- composite = NULL;
}
-static u8 override_id(struct usb_composite_dev *cdev, u8 *desc)
+static void update_unchanged_dev_desc(struct usb_device_descriptor *new,
+ const struct usb_device_descriptor *old)
{
- if (!*desc) {
- int ret = usb_string_id(cdev);
- if (unlikely(ret < 0))
- WARNING(cdev, "failed to override string ID\n");
- else
- *desc = ret;
- }
+ __le16 idVendor;
+ __le16 idProduct;
+ __le16 bcdDevice;
+ u8 iSerialNumber;
+ u8 iManufacturer;
+ u8 iProduct;
- return *desc;
+ /*
+ * these variables may have been set in
+ * usb_composite_overwrite_options()
+ */
+ idVendor = new->idVendor;
+ idProduct = new->idProduct;
+ bcdDevice = new->bcdDevice;
+ iSerialNumber = new->iSerialNumber;
+ iManufacturer = new->iManufacturer;
+ iProduct = new->iProduct;
+
+ *new = *old;
+ if (idVendor)
+ new->idVendor = idVendor;
+ if (idProduct)
+ new->idProduct = idProduct;
+ if (bcdDevice)
+ new->bcdDevice = bcdDevice;
+ else
+ new->bcdDevice = cpu_to_le16(get_default_bcdDevice());
+ if (iSerialNumber)
+ new->iSerialNumber = iSerialNumber;
+ if (iManufacturer)
+ new->iManufacturer = iManufacturer;
+ if (iProduct)
+ new->iProduct = iProduct;
}
-static int composite_bind(struct usb_gadget *gadget)
+static struct usb_composite_driver *to_cdriver(struct usb_gadget_driver *gdrv)
+{
+ return container_of(gdrv, struct usb_composite_driver, gadget_driver);
+}
+
+static int composite_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *gdriver)
{
struct usb_composite_dev *cdev;
+ struct usb_composite_driver *composite = to_cdriver(gdriver);
int status = -ENOMEM;
cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
@@ -1440,13 +1424,12 @@
cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
if (!cdev->req)
goto fail;
- cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+ cdev->req->buf = kmalloc(USB_COMP_EP0_BUFSIZ, GFP_KERNEL);
if (!cdev->req->buf)
goto fail;
cdev->req->complete = composite_setup_complete;
gadget->ep0->driver_data = cdev;
- cdev->bufsiz = USB_BUFSIZ;
cdev->driver = composite;
/*
@@ -1467,49 +1450,11 @@
* serial number), register function drivers, potentially update
* power state and consumption, etc
*/
- status = composite_gadget_bind(cdev);
+ status = composite->bind(cdev);
if (status < 0)
goto fail;
- cdev->desc = *composite->dev;
-
- /* standardized runtime overrides for device ID data */
- if (idVendor)
- cdev->desc.idVendor = cpu_to_le16(idVendor);
- else
- idVendor = le16_to_cpu(cdev->desc.idVendor);
- if (idProduct)
- cdev->desc.idProduct = cpu_to_le16(idProduct);
- else
- idProduct = le16_to_cpu(cdev->desc.idProduct);
- if (bcdDevice)
- cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
- else
- bcdDevice = le16_to_cpu(cdev->desc.bcdDevice);
-
- /* string overrides */
- if (iManufacturer || !cdev->desc.iManufacturer) {
- if (!iManufacturer && !composite->iManufacturer &&
- !*composite_manufacturer)
- snprintf(composite_manufacturer,
- sizeof composite_manufacturer,
- "%s %s with %s",
- init_utsname()->sysname,
- init_utsname()->release,
- gadget->name);
-
- cdev->manufacturer_override =
- override_id(cdev, &cdev->desc.iManufacturer);
- }
-
- if (iProduct || (!cdev->desc.iProduct && composite->iProduct))
- cdev->product_override =
- override_id(cdev, &cdev->desc.iProduct);
-
- if (iSerialNumber ||
- (!cdev->desc.iSerialNumber && composite->iSerialNumber))
- cdev->serial_override =
- override_id(cdev, &cdev->desc.iSerialNumber);
+ update_unchanged_dev_desc(&cdev->desc, composite->dev);
/* has userspace failed to provide a serial number? */
if (composite->needs_serial && !cdev->desc.iSerialNumber)
@@ -1546,8 +1491,8 @@
f->suspend(f);
}
}
- if (composite->suspend)
- composite->suspend(cdev);
+ if (cdev->driver->suspend)
+ cdev->driver->suspend(cdev);
cdev->suspended = 1;
@@ -1565,8 +1510,8 @@
* suspend/resume callbacks?
*/
DBG(cdev, "resume\n");
- if (composite->resume)
- composite->resume(cdev);
+ if (cdev->driver->resume)
+ cdev->driver->resume(cdev);
if (cdev->config) {
list_for_each_entry(f, &cdev->config->functions, list) {
if (f->resume)
@@ -1584,13 +1529,8 @@
/*-------------------------------------------------------------------------*/
-static struct usb_gadget_driver composite_driver = {
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- .max_speed = USB_SPEED_SUPER,
-#else
- .max_speed = USB_SPEED_HIGH,
-#endif
-
+static const struct usb_gadget_driver composite_driver_template = {
+ .bind = composite_bind,
.unbind = composite_unbind,
.setup = composite_setup,
@@ -1623,25 +1563,26 @@
* while it was binding. That would usually be done in order to wait for
* some userspace participation.
*/
-int usb_composite_probe(struct usb_composite_driver *driver,
- int (*bind)(struct usb_composite_dev *cdev))
+int usb_composite_probe(struct usb_composite_driver *driver)
{
- if (!driver || !driver->dev || !bind || composite)
+ struct usb_gadget_driver *gadget_driver;
+
+ if (!driver || !driver->dev || !driver->bind)
return -EINVAL;
if (!driver->name)
driver->name = "composite";
- if (!driver->iProduct)
- driver->iProduct = driver->name;
- composite_driver.function = (char *) driver->name;
- composite_driver.driver.name = driver->name;
- composite_driver.max_speed =
- min_t(u8, composite_driver.max_speed, driver->max_speed);
- composite = driver;
- composite_gadget_bind = bind;
- return usb_gadget_probe_driver(&composite_driver, composite_bind);
+ driver->gadget_driver = composite_driver_template;
+ gadget_driver = &driver->gadget_driver;
+
+ gadget_driver->function = (char *) driver->name;
+ gadget_driver->driver.name = driver->name;
+ gadget_driver->max_speed = driver->max_speed;
+
+ return usb_gadget_probe_driver(gadget_driver);
}
+EXPORT_SYMBOL_GPL(usb_composite_probe);
/**
* usb_composite_unregister() - unregister a composite driver
@@ -1652,10 +1593,9 @@
*/
void usb_composite_unregister(struct usb_composite_driver *driver)
{
- if (composite != driver)
- return;
- usb_gadget_unregister_driver(&composite_driver);
+ usb_gadget_unregister_driver(&driver->gadget_driver);
}
+EXPORT_SYMBOL_GPL(usb_composite_unregister);
/**
* usb_composite_setup_continue() - Continue with the control transfer
@@ -1692,4 +1632,60 @@
spin_unlock_irqrestore(&cdev->lock, flags);
}
+EXPORT_SYMBOL_GPL(usb_composite_setup_continue);
+static char *composite_default_mfr(struct usb_gadget *gadget)
+{
+ char *mfr;
+ int len;
+
+ len = snprintf(NULL, 0, "%s %s with %s", init_utsname()->sysname,
+ init_utsname()->release, gadget->name);
+ len++;
+ mfr = kmalloc(len, GFP_KERNEL);
+ if (!mfr)
+ return NULL;
+ snprintf(mfr, len, "%s %s with %s", init_utsname()->sysname,
+ init_utsname()->release, gadget->name);
+ return mfr;
+}
+
+void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
+ struct usb_composite_overwrite *covr)
+{
+ struct usb_device_descriptor *desc = &cdev->desc;
+ struct usb_gadget_strings *gstr = cdev->driver->strings[0];
+ struct usb_string *dev_str = gstr->strings;
+
+ if (covr->idVendor)
+ desc->idVendor = cpu_to_le16(covr->idVendor);
+
+ if (covr->idProduct)
+ desc->idProduct = cpu_to_le16(covr->idProduct);
+
+ if (covr->bcdDevice)
+ desc->bcdDevice = cpu_to_le16(covr->bcdDevice);
+
+ if (covr->serial_number) {
+ desc->iSerialNumber = dev_str[USB_GADGET_SERIAL_IDX].id;
+ dev_str[USB_GADGET_SERIAL_IDX].s = covr->serial_number;
+ }
+ if (covr->manufacturer) {
+ desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = covr->manufacturer;
+
+ } else if (!strlen(dev_str[USB_GADGET_MANUFACTURER_IDX].s)) {
+ desc->iManufacturer = dev_str[USB_GADGET_MANUFACTURER_IDX].id;
+ cdev->def_manufacturer = composite_default_mfr(cdev->gadget);
+ dev_str[USB_GADGET_MANUFACTURER_IDX].s = cdev->def_manufacturer;
+ }
+
+ if (covr->product) {
+ desc->iProduct = dev_str[USB_GADGET_PRODUCT_IDX].id;
+ dev_str[USB_GADGET_PRODUCT_IDX].s = covr->product;
+ }
+}
+EXPORT_SYMBOL_GPL(usb_composite_overwrite_options);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Brownell");
diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
index 7542a72c..e3a9892 100644
--- a/drivers/usb/gadget/config.c
+++ b/drivers/usb/gadget/config.c
@@ -12,6 +12,7 @@
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
@@ -53,7 +54,7 @@
}
return dest - (u8 *)buf;
}
-
+EXPORT_SYMBOL_GPL(usb_descriptor_fillbuf);
/**
* usb_gadget_config_buf - builts a complete configuration descriptor
@@ -106,6 +107,7 @@
cp->bmAttributes |= USB_CONFIG_ATT_ONE;
return len;
}
+EXPORT_SYMBOL_GPL(usb_gadget_config_buf);
/**
* usb_copy_descriptors - copy a vector of USB descriptors
@@ -155,4 +157,4 @@
return ret;
}
-
+EXPORT_SYMBOL_GPL(usb_copy_descriptors);
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/dbgp.c
index 19d7bb0..87d1650 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/dbgp.c
@@ -13,9 +13,6 @@
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
-/* See comments in "zero.c" */
-#include "epautoconf.c"
-
#ifdef CONFIG_USB_G_DBGP_SERIAL
#include "u_serial.c"
#endif
@@ -292,7 +289,8 @@
return -ENODEV;
}
-static int __init dbgp_bind(struct usb_gadget *gadget)
+static int __init dbgp_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
int err, stp;
@@ -402,9 +400,10 @@
return err;
}
-static struct usb_gadget_driver dbgp_driver = {
+static __refdata struct usb_gadget_driver dbgp_driver = {
.function = "dbgp",
.max_speed = USB_SPEED_HIGH,
+ .bind = dbgp_bind,
.unbind = dbgp_unbind,
.setup = dbgp_setup,
.disconnect = dbgp_disconnect,
@@ -416,7 +415,7 @@
static int __init dbgp_init(void)
{
- return usb_gadget_probe_driver(&dbgp_driver, dbgp_bind);
+ return usb_gadget_probe_driver(&dbgp_driver);
}
static void __exit dbgp_exit(void)
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index b799106..91916f6 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -909,6 +909,7 @@
dum->devstatus = 0;
dum->driver = driver;
+ dum->gadget.dev.driver = &driver->driver;
dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
return 0;
@@ -923,6 +924,7 @@
dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
driver->driver.name);
+ dum->gadget.dev.driver = NULL;
dum->driver = NULL;
return 0;
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index 51f3d42..a777f7b 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -10,6 +10,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
@@ -22,17 +23,6 @@
#include "gadget_chips.h"
-
-/* we must assign addresses for configurable endpoints (like net2280) */
-static unsigned epnum;
-
-// #define MANY_ENDPOINTS
-#ifdef MANY_ENDPOINTS
-/* more than 15 configurable endpoints */
-static unsigned in_epnum;
-#endif
-
-
/*
* This should work with endpoints from controller drivers sharing the
* same endpoint naming convention. By example:
@@ -176,16 +166,14 @@
if (isdigit (ep->name [2])) {
u8 num = simple_strtoul (&ep->name [2], NULL, 10);
desc->bEndpointAddress |= num;
-#ifdef MANY_ENDPOINTS
} else if (desc->bEndpointAddress & USB_DIR_IN) {
- if (++in_epnum > 15)
+ if (++gadget->in_epnum > 15)
return 0;
- desc->bEndpointAddress = USB_DIR_IN | in_epnum;
-#endif
+ desc->bEndpointAddress = USB_DIR_IN | gadget->in_epnum;
} else {
- if (++epnum > 15)
+ if (++gadget->out_epnum > 15)
return 0;
- desc->bEndpointAddress |= epnum;
+ desc->bEndpointAddress |= gadget->out_epnum;
}
/* report (variable) full speed bulk maxpacket */
@@ -328,6 +316,7 @@
ep->comp_desc = NULL;
return ep;
}
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_ss);
/**
* usb_ep_autoconfig() - choose an endpoint matching the
@@ -367,7 +356,7 @@
{
return usb_ep_autoconfig_ss(gadget, desc, NULL);
}
-
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig);
/**
* usb_ep_autoconfig_reset - reset endpoint autoconfig state
@@ -385,9 +374,7 @@
list_for_each_entry (ep, &gadget->ep_list, ep_list) {
ep->driver_data = NULL;
}
-#ifdef MANY_ENDPOINTS
- in_epnum = 0;
-#endif
- epnum = 0;
+ gadget->in_epnum = 0;
+ gadget->out_epnum = 0;
}
-
+EXPORT_SYMBOL_GPL(usb_ep_autoconfig_reset);
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index a28f6ff..18c3f42 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -14,8 +14,6 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
@@ -102,11 +100,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_ecm.c"
#include "f_subset.c"
#ifdef USB_ETH_RNDIS
@@ -117,6 +110,7 @@
#include "u_ether.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
* Instead: allocate your own, using normal USB-IF procedures.
@@ -195,17 +189,10 @@
NULL,
};
-
-/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -288,7 +275,6 @@
static int __init eth_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -323,42 +309,15 @@
device_desc.bNumConfigurations = 2;
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- dev_warn(&gadget->dev,
- "controller '%s' not recognized; trying %s\n",
- gadget->name,
- eth_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration(s); RNDIS first, if it's used */
if (has_rndis()) {
@@ -372,6 +331,7 @@
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s, version: " DRIVER_VERSION "\n",
DRIVER_DESC);
@@ -388,11 +348,12 @@
return 0;
}
-static struct usb_composite_driver eth_driver = {
+static __refdata struct usb_composite_driver eth_driver = {
.name = "g_ether",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = eth_bind,
.unbind = __exit_p(eth_unbind),
};
@@ -402,7 +363,7 @@
static int __init init(void)
{
- return usb_composite_probe(ð_driver, eth_bind);
+ return usb_composite_probe(ð_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 30b908f..95bc94f 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -897,10 +897,7 @@
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr);
ecm_string_defs[1].s = ecm->ethaddr;
ecm->port.cdc_filter = DEFAULT_FILTER;
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 16a8b1c..511e527 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -10,7 +10,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/hid.h>
#include <linux/cdev.h>
@@ -18,6 +17,7 @@
#include <linux/poll.h>
#include <linux/uaccess.h>
#include <linux/wait.h>
+#include <linux/sched.h>
#include <linux/usb/g_hid.h>
static int major, minors;
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/f_mass_storage.c
index 4f1142e..3a7668b 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/f_mass_storage.c
@@ -213,7 +213,6 @@
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/freezer.h>
-#include <linux/utsname.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -349,7 +348,6 @@
const char *vendor_name; /* 8 characters or less */
const char *product_name; /* 16 characters or less */
- u16 release;
char can_stall;
};
@@ -2773,18 +2771,7 @@
bh->next = common->buffhds;
/* Prepare inquiryString */
- if (cfg->release != 0xffff) {
- i = cfg->release;
- } else {
- i = usb_gadget_controller_number(gadget);
- if (i >= 0) {
- i = 0x0300 + i;
- } else {
- WARNING(common, "controller '%s' not recognized\n",
- gadget->name);
- i = 0x0399;
- }
- }
+ i = get_default_bcdDevice();
snprintf(common->inquiry_string, sizeof common->inquiry_string,
"%-8s%-16s%04x", cfg->vendor_name ?: "Linux",
/* Assume product name dependent on the first LUN */
@@ -3110,7 +3097,6 @@
/* Let MSF use defaults */
cfg->vendor_name = 0;
cfg->product_name = 0;
- cfg->release = 0xffff;
cfg->ops = NULL;
cfg->private_data = NULL;
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c
index 2f7e8f2..8ed1259 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/f_midi.c
@@ -21,7 +21,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <sound/core.h>
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index aab8ede..b651b52 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -1346,10 +1346,7 @@
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(ncm->ethaddr, sizeof ncm->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(ncm->ethaddr, sizeof ncm->ethaddr, "%pm", ethaddr);
ncm_string_defs[1].s = ncm->ethaddr;
spin_lock_init(&ncm->lock);
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index 5c1b68b..3c126fd 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -795,7 +795,7 @@
u16 w_value = le16_to_cpu(ctrl->wValue);
u16 w_length = le16_to_cpu(ctrl->wLength);
- req->length = USB_BUFSIZ;
+ req->length = USB_COMP_EP0_BUFSIZ;
/* composite driver infrastructure handles everything except
* the two control test requests.
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
index 21ab474..4060c0b 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -436,10 +436,7 @@
return -ENOMEM;
/* export host's Ethernet address in CDC format */
- snprintf(geth->ethaddr, sizeof geth->ethaddr,
- "%02X%02X%02X%02X%02X%02X",
- ethaddr[0], ethaddr[1], ethaddr[2],
- ethaddr[3], ethaddr[4], ethaddr[5]);
+ snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr);
geth_string_defs[1].s = geth->ethaddr;
geth->port.cdc_filter = DEFAULT_FILTER;
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/f_uac2.c
index e7cc4de..d3c6cff 100644
--- a/drivers/usb/gadget/f_uac2.c
+++ b/drivers/usb/gadget/f_uac2.c
@@ -463,7 +463,7 @@
return err;
}
-static int __devexit snd_uac2_remove(struct platform_device *pdev)
+static int snd_uac2_remove(struct platform_device *pdev)
{
struct snd_card *card = platform_get_drvdata(pdev);
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index a896d73..3f7d640 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -251,26 +251,12 @@
#include <linux/freezer.h>
#include <linux/utsname.h>
+#include <linux/usb/composite.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
#include "gadget_chips.h"
-
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
-/*-------------------------------------------------------------------------*/
-
#define DRIVER_DESC "File-backed Storage Gadget"
#define DRIVER_NAME "g_file_storage"
#define DRIVER_VERSION "1 September 2010"
@@ -3213,7 +3199,6 @@
static int __init check_parameters(struct fsg_dev *fsg)
{
int prot;
- int gcnum;
/* Store the default values */
mod_data.transport_type = USB_PR_BULK;
@@ -3228,16 +3213,8 @@
if (gadget_is_at91(fsg->gadget))
mod_data.can_stall = 0;
- if (mod_data.release == 0xffff) { // Parameter wasn't set
- gcnum = usb_gadget_controller_number(fsg->gadget);
- if (gcnum >= 0)
- mod_data.release = 0x0300 + gcnum;
- else {
- WARNING(fsg, "controller '%s' not recognized\n",
- fsg->gadget->name);
- mod_data.release = 0x0399;
- }
- }
+ if (mod_data.release == 0xffff)
+ mod_data.release = get_default_bcdDevice();
prot = simple_strtol(mod_data.protocol_parm, NULL, 0);
@@ -3331,7 +3308,8 @@
}
-static int __init fsg_bind(struct usb_gadget *gadget)
+static int __init fsg_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
struct fsg_dev *fsg = the_fsg;
int rc;
@@ -3603,9 +3581,10 @@
/*-------------------------------------------------------------------------*/
-static struct usb_gadget_driver fsg_driver = {
+static __refdata struct usb_gadget_driver fsg_driver = {
.max_speed = USB_SPEED_SUPER,
.function = (char *) fsg_string_product,
+ .bind = fsg_bind,
.unbind = fsg_unbind,
.disconnect = fsg_disconnect,
.setup = fsg_setup,
@@ -3653,7 +3632,8 @@
if ((rc = fsg_alloc()) != 0)
return rc;
fsg = the_fsg;
- if ((rc = usb_gadget_probe_driver(&fsg_driver, fsg_bind)) != 0)
+ rc = usb_gadget_probe_driver(&fsg_driver);
+ if (rc != 0)
kref_put(&fsg->ref, fsg_release);
return rc;
}
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 3def828..6ae70cb 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -1255,7 +1255,7 @@
}
static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int fsl_stop(struct usb_gadget_driver *driver);
/* defined in gadget.h */
static struct usb_gadget_ops fsl_gadget_ops = {
@@ -1951,7 +1951,7 @@
* Called by initialization code of gadget drivers
*----------------------------------------------------------------*/
static int fsl_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
int retval = -ENODEV;
unsigned long flags = 0;
@@ -1976,7 +1976,7 @@
spin_unlock_irqrestore(&udc_controller->lock, flags);
/* bind udc driver to gadget driver */
- retval = bind(&udc_controller->gadget);
+ retval = bind(&udc_controller->gadget, driver);
if (retval) {
VDBG("bind to %s --> %d", driver->driver.name, retval);
udc_controller->gadget.dev.driver = NULL;
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index cdd9454..72cd5e6 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -1311,7 +1311,7 @@
static struct fusb300 *the_controller;
static int fusb300_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct fusb300 *fusb300 = the_controller;
int retval;
@@ -1339,7 +1339,7 @@
goto error;
}
- retval = bind(&fusb300->gadget);
+ retval = bind(&fusb300->gadget, driver);
if (retval) {
pr_err("bind to driver error (%d)\n", retval);
device_del(&fusb300->gadget.dev);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index d3ace90..3953dd4 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -13,7 +13,6 @@
#define pr_fmt(fmt) "g_ffs: " fmt
#include <linux/module.h>
-#include <linux/utsname.h>
/*
* kbuild is not very cooperative with respect to linking separately
@@ -22,12 +21,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
# if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
@@ -76,6 +69,8 @@
struct ffs_data *ffs_data;
};
+USB_GADGET_COMPOSITE_OPTIONS();
+
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -117,6 +112,9 @@
/* String IDs are assigned dynamically */
static struct usb_string gfs_strings[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{ .s = "FunctionFS + RNDIS" },
#endif
@@ -163,13 +161,13 @@
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
-static struct usb_composite_driver gfs_driver = {
+static __refdata struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
.strings = gfs_dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = gfs_bind,
.unbind = gfs_unbind,
- .iProduct = DRIVER_DESC,
};
static DEFINE_MUTEX(gfs_lock);
@@ -268,7 +266,7 @@
}
gfs_registered = true;
- ret = usb_composite_probe(&gfs_driver, gfs_bind);
+ ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
gfs_registered = false;
@@ -357,6 +355,7 @@
ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
goto error;
+ gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
for (i = func_num; --i; ) {
ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);
@@ -369,9 +368,10 @@
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
struct gfs_configuration *c = gfs_configurations + i;
+ int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
- c->c.label = gfs_strings[i].s;
- c->c.iConfiguration = gfs_strings[i].id;
+ c->c.label = gfs_strings[sid].s;
+ c->c.iConfiguration = gfs_strings[sid].id;
c->c.bConfigurationValue = 1 + i;
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
@@ -379,7 +379,7 @@
if (unlikely(ret < 0))
goto error_unbind;
}
-
+ usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
error_unbind:
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index b8b3a34..bcd04bc 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -15,6 +15,8 @@
#ifndef __GADGET_CHIPS_H
#define __GADGET_CHIPS_H
+#include <linux/usb/gadget.h>
+
/*
* NOTICE: the entries below are alphabetical and should be kept
* that way.
@@ -25,106 +27,12 @@
* If you have forgotten the alphabetical order let VIM/EMACS
* do that for you.
*/
-#define gadget_is_amd5536udc(g) (!strcmp("amd5536udc", (g)->name))
#define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name))
-#define gadget_is_atmel_usba(g) (!strcmp("atmel_usba_udc", (g)->name))
-#define gadget_is_ci13xxx_msm(g) (!strcmp("ci13xxx_msm", (g)->name))
-#define gadget_is_ci13xxx_pci(g) (!strcmp("ci13xxx_pci", (g)->name))
-#define gadget_is_dummy(g) (!strcmp("dummy_udc", (g)->name))
-#define gadget_is_dwc3(g) (!strcmp("dwc3-gadget", (g)->name))
-#define gadget_is_fsl_qe(g) (!strcmp("fsl_qe_udc", (g)->name))
-#define gadget_is_fsl_usb2(g) (!strcmp("fsl-usb2-udc", (g)->name))
#define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name))
-#define gadget_is_imx(g) (!strcmp("imx_udc", (g)->name))
-#define gadget_is_langwell(g) (!strcmp("langwell_udc", (g)->name))
-#define gadget_is_lpc32xx(g) (!strcmp("lpc32xx_udc", (g)->name))
-#define gadget_is_m66592(g) (!strcmp("m66592_udc", (g)->name))
#define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name))
-#define gadget_is_net2272(g) (!strcmp("net2272", (g)->name))
#define gadget_is_net2280(g) (!strcmp("net2280", (g)->name))
-#define gadget_is_omap(g) (!strcmp("omap_udc", (g)->name))
-#define gadget_is_pch(g) (!strcmp("pch_udc", (g)->name))
#define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name))
#define gadget_is_pxa27x(g) (!strcmp("pxa27x_udc", (g)->name))
-#define gadget_is_r8a66597(g) (!strcmp("r8a66597_udc", (g)->name))
-#define gadget_is_renesas_usbhs(g) (!strcmp("renesas_usbhs_udc", (g)->name))
-#define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name))
-#define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name))
-#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
-
-/**
- * usb_gadget_controller_number - support bcdDevice id convention
- * @gadget: the controller being driven
- *
- * Return a 2-digit BCD value associated with the peripheral controller,
- * suitable for use as part of a bcdDevice value, or a negative error code.
- *
- * NOTE: this convention is purely optional, and has no meaning in terms of
- * any USB specification. If you want to use a different convention in your
- * gadget driver firmware -- maybe a more formal revision ID -- feel free.
- *
- * Hosts see these bcdDevice numbers, and are allowed (but not encouraged!)
- * to change their behavior accordingly. For example it might help avoiding
- * some chip bug.
- */
-static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
-{
- if (gadget_is_net2280(gadget))
- return 0x01;
- else if (gadget_is_dummy(gadget))
- return 0x02;
- else if (gadget_is_pxa(gadget))
- return 0x03;
- else if (gadget_is_goku(gadget))
- return 0x06;
- else if (gadget_is_omap(gadget))
- return 0x08;
- else if (gadget_is_pxa27x(gadget))
- return 0x11;
- else if (gadget_is_s3c2410(gadget))
- return 0x12;
- else if (gadget_is_at91(gadget))
- return 0x13;
- else if (gadget_is_imx(gadget))
- return 0x14;
- else if (gadget_is_musbhdrc(gadget))
- return 0x16;
- else if (gadget_is_atmel_usba(gadget))
- return 0x18;
- else if (gadget_is_fsl_usb2(gadget))
- return 0x19;
- else if (gadget_is_amd5536udc(gadget))
- return 0x20;
- else if (gadget_is_m66592(gadget))
- return 0x21;
- else if (gadget_is_fsl_qe(gadget))
- return 0x22;
- else if (gadget_is_ci13xxx_pci(gadget))
- return 0x23;
- else if (gadget_is_langwell(gadget))
- return 0x24;
- else if (gadget_is_r8a66597(gadget))
- return 0x25;
- else if (gadget_is_s3c_hsotg(gadget))
- return 0x26;
- else if (gadget_is_pch(gadget))
- return 0x27;
- else if (gadget_is_ci13xxx_msm(gadget))
- return 0x28;
- else if (gadget_is_renesas_usbhs(gadget))
- return 0x29;
- else if (gadget_is_s3c_hsudc(gadget))
- return 0x30;
- else if (gadget_is_net2272(gadget))
- return 0x31;
- else if (gadget_is_dwc3(gadget))
- return 0x32;
- else if (gadget_is_lpc32xx(gadget))
- return 0x33;
-
- return -ENOENT;
-}
-
/**
* gadget_supports_altsettings - return true if altsettings work
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
index 681bd03..881aab8 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/gmidi.c
@@ -22,7 +22,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/module.h>
#include <linux/device.h>
@@ -31,16 +30,13 @@
#include <sound/rawmidi.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
#include "gadget_chips.h"
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "f_midi.c"
/*-------------------------------------------------------------------------*/
@@ -51,6 +47,8 @@
static const char shortname[] = "g_midi";
static const char longname[] = "MIDI Gadget";
+USB_GADGET_COMPOSITE_OPTIONS();
+
static int index = SNDRV_DEFAULT_IDX1;
module_param(index, int, S_IRUGO);
MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
@@ -85,9 +83,7 @@
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_device_descriptor device_desc = {
.bLength = USB_DT_DEVICE_SIZE,
@@ -102,8 +98,9 @@
};
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = "Grey Innovation",
- [STRING_PRODUCT_IDX].s = "MIDI Gadget",
+ [USB_GADGET_MANUFACTURER_IDX].s = "Grey Innovation",
+ [USB_GADGET_PRODUCT_IDX].s = "MIDI Gadget",
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = "MIDI",
{ } /* end of list */
};
@@ -140,61 +137,35 @@
static int __init midi_bind(struct usb_composite_dev *cdev)
{
- struct usb_gadget *gadget = cdev->gadget;
- int gcnum, status;
+ int status;
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
- midi_config.iConfiguration = status;
-
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum < 0) {
- /* gmidi is so simple (no altsettings) that
- * it SHOULD NOT have problems with bulk-capable hardware.
- * so warn about unrecognized controllers, don't panic.
- */
- pr_warning("%s: controller '%s' not recognized\n",
- __func__, gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x9999);
- } else {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- }
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
return status;
-
+ usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
}
-static struct usb_composite_driver midi_driver = {
+static __refdata struct usb_composite_driver midi_driver = {
.name = (char *) longname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = midi_bind,
.unbind = __exit_p(midi_unbind),
};
static int __init midi_init(void)
{
- return usb_composite_probe(&midi_driver, midi_bind);
+ return usb_composite_probe(&midi_driver);
}
module_init(midi_init);
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index 9fd7886..51037cb 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -994,7 +994,7 @@
}
static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int goku_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops goku_ops = {
@@ -1348,7 +1348,7 @@
* the driver might get unbound.
*/
static int goku_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct goku_udc *dev = the_controller;
int retval;
@@ -1368,7 +1368,7 @@
driver->driver.bus = NULL;
dev->driver = driver;
dev->gadget.dev.driver = &driver->driver;
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
DBG(dev, "bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/hid.c
index 3493adf..74130f6 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/hid.c
@@ -15,7 +15,10 @@
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/usb/composite.h>
+#include "gadget_chips.h"
#define DRIVER_DESC "HID Gadget"
#define DRIVER_VERSION "2010/03/16"
@@ -33,12 +36,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_hid.c"
@@ -50,6 +47,7 @@
static LIST_HEAD(hidg_func_list);
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -92,15 +90,10 @@
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -150,7 +143,7 @@
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
- int status, gcnum, funcs = 0;
+ int status, funcs = 0;
list_for_each(tmp, &hidg_func_list)
funcs++;
@@ -163,38 +156,22 @@
if (status < 0)
return status;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
return status;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- return status;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &config_driver, do_config);
if (status < 0)
return status;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
return 0;
@@ -242,11 +219,12 @@
/****************************** Some noise ******************************/
-static struct usb_composite_driver hidg_driver = {
+static __refdata struct usb_composite_driver hidg_driver = {
.name = "g_hid",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = hid_bind,
.unbind = __exit_p(hid_unbind),
};
@@ -272,7 +250,7 @@
if (status < 0)
return status;
- status = usb_composite_probe(&hidg_driver, hid_bind);
+ status = usb_composite_probe(&hidg_driver);
if (status < 0)
platform_driver_unregister(&hidg_plat_driver);
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index e58b164..4bb6d53 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -828,7 +828,6 @@
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_SPEED_HIGH:
/* fails if caller didn't provide that descriptor... */
ep->desc = &data->hs_desc;
@@ -836,7 +835,6 @@
if (value == 0)
data->state = STATE_EP_ENABLED;
break;
-#endif
default:
DBG(data->dev, "unconnected, %s init abandoned\n",
data->name);
@@ -1324,7 +1322,6 @@
* Unrecognized ep0 requests may be handled in user space.
*/
-#ifdef CONFIG_USB_GADGET_DUALSPEED
static void make_qualifier (struct dev_data *dev)
{
struct usb_qualifier_descriptor qual;
@@ -1347,7 +1344,6 @@
memcpy (dev->rbuf, &qual, sizeof qual);
}
-#endif
static int
config_buf (struct dev_data *dev, u8 type, unsigned index)
@@ -1427,7 +1423,6 @@
dev->dev->bMaxPacketSize0 = dev->gadget->ep0->maxpacket;
req->buf = dev->dev;
break;
-#ifdef CONFIG_USB_GADGET_DUALSPEED
case USB_DT_DEVICE_QUALIFIER:
if (!dev->hs_config)
break;
@@ -1437,7 +1432,6 @@
break;
case USB_DT_OTHER_SPEED_CONFIG:
// FALLTHROUGH
-#endif
case USB_DT_CONFIG:
value = config_buf (dev,
w_value >> 8,
@@ -1685,8 +1679,8 @@
static struct dev_data *the_device;
-static int
-gadgetfs_bind (struct usb_gadget *gadget)
+static int gadgetfs_bind(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
struct dev_data *dev = the_device;
@@ -1763,12 +1757,8 @@
}
static struct usb_gadget_driver gadgetfs_driver = {
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- .max_speed = USB_SPEED_HIGH,
-#else
- .max_speed = USB_SPEED_FULL,
-#endif
.function = (char *) driver_desc,
+ .bind = gadgetfs_bind,
.unbind = gadgetfs_unbind,
.setup = gadgetfs_setup,
.disconnect = gadgetfs_disconnect,
@@ -1783,7 +1773,8 @@
static void gadgetfs_nop(struct usb_gadget *arg) { }
-static int gadgetfs_probe (struct usb_gadget *gadget)
+static int gadgetfs_probe(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
CHIP = gadget->name;
return -EISNAM;
@@ -1791,6 +1782,7 @@
static struct usb_gadget_driver probe_driver = {
.max_speed = USB_SPEED_HIGH,
+ .bind = gadgetfs_probe,
.unbind = gadgetfs_nop,
.setup = (void *)gadgetfs_nop,
.disconnect = gadgetfs_nop,
@@ -1900,7 +1892,12 @@
/* triggers gadgetfs_bind(); then we can enumerate. */
spin_unlock_irq (&dev->lock);
- value = usb_gadget_probe_driver(&gadgetfs_driver, gadgetfs_bind);
+ if (dev->hs_config)
+ gadgetfs_driver.max_speed = USB_SPEED_HIGH;
+ else
+ gadgetfs_driver.max_speed = USB_SPEED_FULL;
+
+ value = usb_gadget_probe_driver(&gadgetfs_driver);
if (value != 0) {
kfree (dev->buf);
dev->buf = NULL;
@@ -2039,7 +2036,7 @@
return -ESRCH;
/* fake probe to determine $CHIP */
- (void) usb_gadget_probe_driver(&probe_driver, gadgetfs_probe);
+ usb_gadget_probe_driver(&probe_driver);
if (!CHIP)
return -ENODEV;
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c
index f1ec99e..f696fb9 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/lpc32xx_udc.c
@@ -141,8 +141,6 @@
u32 totalints;
bool wedge;
-
- const struct usb_endpoint_descriptor *desc;
};
/*
@@ -556,10 +554,8 @@
if (udc->enabled && udc->vbus) {
proc_ep_show(s, &udc->ep[0]);
- list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
- if (ep->desc)
- proc_ep_show(s, ep);
- }
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list)
+ proc_ep_show(s, ep);
}
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1453,7 +1449,6 @@
if (i != 0)
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- ep->desc = NULL;
ep->ep.maxpacket = ep->maxpacket;
INIT_LIST_HEAD(&ep->queue);
ep->req_pending = 0;
@@ -1515,7 +1510,7 @@
done(ep, req, status);
}
- if (ep->desc && status == -ESHUTDOWN) {
+ if (status == -ESHUTDOWN) {
uda_disable_hwepint(ep->udc, ep->hwep_num);
udc_disable_hwep(ep->udc, ep->hwep_num);
}
@@ -1658,9 +1653,6 @@
nuke(ep, -ESHUTDOWN);
- /* restore the endpoint's pristine config */
- ep->desc = NULL;
-
/* Clear all DMA statuses for this EP */
udc_ep_dma_disable(udc, ep->hwep_num);
writel(1 << ep->hwep_num, USBD_EOTINTCLR(udc->udp_baseaddr));
@@ -1696,7 +1688,7 @@
unsigned long flags;
/* Verify EP data */
- if ((!_ep) || (!ep) || (!desc) || (ep->desc) ||
+ if ((!_ep) || (!ep) || (!desc) ||
(desc->bDescriptorType != USB_DT_ENDPOINT)) {
dev_dbg(udc->dev, "bad ep or descriptor\n");
return -EINVAL;
@@ -1754,7 +1746,6 @@
/* Initialize endpoint to match the selected descriptor */
ep->is_in = (desc->bEndpointAddress & USB_DIR_IN) != 0;
- ep->desc = desc;
ep->ep.maxpacket = maxpacket;
/* Map hardware endpoint from base and direction */
@@ -1837,7 +1828,7 @@
udc = ep->udc;
- if (!_ep || (!ep->desc && ep->hwep_num_base != 0)) {
+ if (!_ep) {
dev_dbg(udc->dev, "invalid ep\n");
return -EINVAL;
}
@@ -1976,7 +1967,7 @@
struct lpc32xx_udc *udc = ep->udc;
unsigned long flags;
- if ((!ep) || (ep->desc == NULL) || (ep->hwep_num <= 1))
+ if ((!ep) || (ep->hwep_num <= 1))
return -EINVAL;
/* Don't halt an IN EP */
@@ -2262,7 +2253,7 @@
case USB_RECIP_ENDPOINT:
tmp = wIndex & USB_ENDPOINT_NUMBER_MASK;
ep = &udc->ep[tmp];
- if ((tmp == 0) || (tmp >= NUM_ENDPOINTS) || (tmp && !ep->desc))
+ if ((tmp == 0) || (tmp >= NUM_ENDPOINTS))
return -EOPNOTSUPP;
if (wIndex & USB_DIR_IN) {
@@ -2599,9 +2590,8 @@
return 0;
}
-static int lpc32xx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
-static int lpc32xx_stop(struct usb_gadget_driver *driver);
+static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *);
+static int lpc32xx_stop(struct usb_gadget *, struct usb_gadget_driver *);
static const struct usb_gadget_ops lpc32xx_udc_ops = {
.get_frame = lpc32xx_get_frame,
@@ -2609,8 +2599,8 @@
.set_selfpowered = lpc32xx_set_selfpowered,
.vbus_session = lpc32xx_vbus_session,
.pullup = lpc32xx_pullup,
- .start = lpc32xx_start,
- .stop = lpc32xx_stop,
+ .udc_start = lpc32xx_start,
+ .udc_stop = lpc32xx_stop,
};
static void nop_release(struct device *dev)
@@ -2618,10 +2608,9 @@
/* nothing to free */
}
-static struct lpc32xx_udc controller = {
+static const struct lpc32xx_udc controller_template = {
.gadget = {
.ops = &lpc32xx_udc_ops,
- .ep0 = &controller.ep[0].ep,
.name = driver_name,
.dev = {
.init_name = "gadget",
@@ -2633,7 +2622,6 @@
.name = "ep0",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 0,
.hwep_num = 0, /* Can be 0 or 1, has special handling */
@@ -2645,7 +2633,6 @@
.name = "ep1-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 2,
.hwep_num = 0, /* 2 or 3, will be set later */
@@ -2657,7 +2644,6 @@
.name = "ep2-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 4,
.hwep_num = 0, /* 4 or 5, will be set later */
@@ -2669,7 +2655,6 @@
.name = "ep3-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 6,
.hwep_num = 0, /* 6 or 7, will be set later */
@@ -2681,7 +2666,6 @@
.name = "ep4-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 8,
.hwep_num = 0, /* 8 or 9, will be set later */
@@ -2693,7 +2677,6 @@
.name = "ep5-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 10,
.hwep_num = 0, /* 10 or 11, will be set later */
@@ -2705,7 +2688,6 @@
.name = "ep6-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 12,
.hwep_num = 0, /* 12 or 13, will be set later */
@@ -2717,7 +2699,6 @@
.name = "ep7-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 14,
.hwep_num = 0,
@@ -2729,7 +2710,6 @@
.name = "ep8-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 16,
.hwep_num = 0,
@@ -2741,7 +2721,6 @@
.name = "ep9-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 18,
.hwep_num = 0,
@@ -2753,7 +2732,6 @@
.name = "ep10-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 20,
.hwep_num = 0,
@@ -2765,7 +2743,6 @@
.name = "ep11-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 22,
.hwep_num = 0,
@@ -2777,7 +2754,6 @@
.name = "ep12-iso",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 24,
.hwep_num = 0,
@@ -2789,7 +2765,6 @@
.name = "ep13-int",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 26,
.hwep_num = 0,
@@ -2801,7 +2776,6 @@
.name = "ep14-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 64,
.hwep_num_base = 28,
.hwep_num = 0,
@@ -2813,7 +2787,6 @@
.name = "ep15-bulk",
.ops = &lpc32xx_ep_ops,
},
- .udc = &controller,
.maxpacket = 1023,
.hwep_num_base = 30,
.hwep_num = 0,
@@ -2987,14 +2960,13 @@
return IRQ_HANDLED;
}
-static int lpc32xx_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+static int lpc32xx_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
- struct lpc32xx_udc *udc = &controller;
- int retval, i;
+ struct lpc32xx_udc *udc = to_udc(gadget);
+ int i;
- if (!driver || driver->max_speed < USB_SPEED_FULL ||
- !bind || !driver->setup) {
+ if (!driver || driver->max_speed < USB_SPEED_FULL || !driver->setup) {
dev_err(udc->dev, "bad parameter.\n");
return -EINVAL;
}
@@ -3011,18 +2983,6 @@
udc->selfpowered = 1;
udc->vbus = 0;
- retval = bind(&udc->gadget);
- if (retval) {
- dev_err(udc->dev, "bind() returned %d\n", retval);
- udc->enabled = 0;
- udc->selfpowered = 0;
- udc->driver = NULL;
- udc->gadget.dev.driver = NULL;
- return retval;
- }
-
- dev_dbg(udc->dev, "bound to %s\n", driver->driver.name);
-
/* Force VBUS process once to check for cable insertion */
udc->last_vbus = udc->vbus = 0;
schedule_work(&udc->vbus_job);
@@ -3034,22 +2994,19 @@
return 0;
}
-static int lpc32xx_stop(struct usb_gadget_driver *driver)
+static int lpc32xx_stop(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
{
int i;
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc = to_udc(gadget);
- if (!driver || driver != udc->driver || !driver->unbind)
+ if (!driver || driver != udc->driver)
return -EINVAL;
- /* Disable USB pullup */
- isp1301_pullup_enable(udc, 0, 1);
-
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
disable_irq(udc->udp_irq[i]);
if (udc->clocked) {
-
spin_lock(&udc->lock);
stop_activity(udc);
spin_unlock(&udc->lock);
@@ -3069,20 +3026,16 @@
}
udc->enabled = 0;
- pullup(udc, 0);
-
- driver->unbind(&udc->gadget);
udc->gadget.dev.driver = NULL;
udc->driver = NULL;
- dev_dbg(udc->dev, "unbound from %s\n", driver->driver.name);
return 0;
}
static void lpc32xx_udc_shutdown(struct platform_device *dev)
{
/* Force disconnect on reboot */
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc = platform_get_drvdata(dev);
pullup(udc, 0);
}
@@ -3120,12 +3073,21 @@
static int __init lpc32xx_udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct lpc32xx_udc *udc = &controller;
+ struct lpc32xx_udc *udc;
int retval, i;
struct resource *res;
dma_addr_t dma_handle;
struct device_node *isp1301_node;
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+ return -ENOMEM;
+
+ memcpy(udc, &controller_template, sizeof(*udc));
+ for (i = 0; i <= 15; i++)
+ udc->ep[i].udc = udc;
+ udc->gadget.ep0 = &udc->ep[0].ep;
+
/* init software state */
udc->gadget.dev.parent = dev;
udc->pdev = pdev;
@@ -3140,8 +3102,10 @@
}
udc->isp1301_i2c_client = isp1301_get_client(isp1301_node);
- if (!udc->isp1301_i2c_client)
- return -EPROBE_DEFER;
+ if (!udc->isp1301_i2c_client) {
+ retval = -EPROBE_DEFER;
+ goto phy_fail;
+ }
dev_info(udc->dev, "ISP1301 I2C device at address 0x%x\n",
udc->isp1301_i2c_client->addr);
@@ -3160,8 +3124,10 @@
* IORESOURCE_IRQ, USB transceiver interrupt number
*/
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -ENXIO;
+ if (!res) {
+ retval = -ENXIO;
+ goto resource_fail;
+ }
spin_lock_init(&udc->lock);
@@ -3171,7 +3137,8 @@
if (udc->udp_irq[i] < 0) {
dev_err(udc->dev,
"irq resource %d not available!\n", i);
- return udc->udp_irq[i];
+ retval = udc->udp_irq[i];
+ goto irq_fail;
}
}
@@ -3179,7 +3146,8 @@
udc->io_p_size = resource_size(res);
if (!request_mem_region(udc->io_p_start, udc->io_p_size, driver_name)) {
dev_err(udc->dev, "someone's using UDC memory\n");
- return -EBUSY;
+ retval = -EBUSY;
+ goto request_mem_region_fail;
}
udc->udp_baseaddr = ioremap(udc->io_p_start, udc->io_p_size);
@@ -3208,7 +3176,7 @@
udc->usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
if (IS_ERR(udc->usb_otg_clk)) {
dev_err(udc->dev, "failed to acquire USB otg clock\n");
- retval = PTR_ERR(udc->usb_slv_clk);
+ retval = PTR_ERR(udc->usb_otg_clk);
goto usb_otg_clk_get_fail;
}
@@ -3376,7 +3344,11 @@
io_map_fail:
release_mem_region(udc->io_p_start, udc->io_p_size);
dev_err(udc->dev, "%s probe failed, %d\n", driver_name, retval);
-
+request_mem_region_fail:
+irq_fail:
+resource_fail:
+phy_fail:
+ kfree(udc);
return retval;
}
@@ -3414,6 +3386,7 @@
clk_put(udc->usb_pll_clk);
iounmap(udc->udp_baseaddr);
release_mem_region(udc->io_p_start, udc->io_p_size);
+ kfree(udc);
return 0;
}
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index cf6bd62..b6401f1 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -1466,7 +1466,7 @@
static struct m66592 *the_controller;
static int m66592_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct m66592 *m66592 = the_controller;
int retval;
@@ -1492,7 +1492,7 @@
goto error;
}
- retval = bind(&m66592->gadget);
+ retval = bind(&m66592->gadget, driver);
if (retval) {
pr_err("bind to driver error (%d)\n", retval);
device_del(&m66592->gadget.dev);
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/mass_storage.c
index 1f376eb..080e577 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/mass_storage.c
@@ -29,9 +29,8 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/usb/ch9.h>
-
+#include <linux/module.h>
/*-------------------------------------------------------------------------*/
@@ -47,14 +46,10 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
#include "f_mass_storage.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor msg_device_desc = {
.bLength = sizeof msg_device_desc,
@@ -85,6 +80,22 @@
NULL,
};
+static struct usb_string strings_dev[] = {
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+ .strings = strings_dev,
+};
+
+static struct usb_gadget_strings *dev_strings[] = {
+ &stringtab_dev,
+ NULL,
+};
/****************************** Configurations ******************************/
@@ -143,10 +154,15 @@
{
int status;
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ return status;
+ msg_device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+
status = usb_add_config(cdev, &msg_config_driver, msg_do_config);
if (status < 0)
return status;
-
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&cdev->gadget->dev,
DRIVER_DESC ", version: " DRIVER_VERSION "\n");
set_bit(0, &msg_registered);
@@ -156,12 +172,13 @@
/****************************** Some noise ******************************/
-static struct usb_composite_driver msg_driver = {
+static __refdata struct usb_composite_driver msg_driver = {
.name = "g_mass_storage",
.dev = &msg_device_desc,
- .iProduct = DRIVER_DESC,
.max_speed = USB_SPEED_SUPER,
.needs_serial = 1,
+ .strings = dev_strings,
+ .bind = msg_bind,
};
MODULE_DESCRIPTION(DRIVER_DESC);
@@ -170,7 +187,7 @@
static int __init msg_init(void)
{
- return usb_composite_probe(&msg_driver, msg_bind);
+ return usb_composite_probe(&msg_driver);
}
module_init(msg_init);
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index c37fb33..88472bf 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -14,10 +14,8 @@
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/module.h>
-
#if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
#endif
@@ -42,12 +40,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_mass_storage.c"
#include "u_serial.c"
@@ -61,7 +53,7 @@
#endif
#include "u_ether.c"
-
+USB_GADGET_COMPOSITE_OPTIONS();
/***************************** Device Descriptor ****************************/
@@ -112,21 +104,16 @@
enum {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
- MULTI_STRING_RNDIS_CONFIG_IDX,
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
+ MULTI_STRING_RNDIS_CONFIG_IDX = USB_GADGET_FIRST_AVAIL_IDX,
MULTI_STRING_CDC_CONFIG_IDX,
-#endif
};
static struct usb_string strings_dev[] = {
-#ifdef CONFIG_USB_G_MULTI_RNDIS
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
[MULTI_STRING_RNDIS_CONFIG_IDX].s = "Multifunction with RNDIS",
-#endif
-#ifdef CONFIG_USB_G_MULTI_CDC
[MULTI_STRING_CDC_CONFIG_IDX].s = "Multifunction with CDC ECM",
-#endif
{ } /* end of list */
};
@@ -260,7 +247,7 @@
static int __ref multi_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
- int status, gcnum;
+ int status;
if (!can_support_ecm(cdev->gadget)) {
dev_err(&gadget->dev, "controller '%s' not usable\n",
@@ -288,19 +275,11 @@
}
}
- /* set bcdDevice */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- } else {
- WARNING(cdev, "controller '%s' not recognized\n", gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x0300 | 0x0099);
- }
-
/* allocate string IDs */
status = usb_string_ids_tab(cdev, strings_dev);
if (unlikely(status < 0))
goto fail2;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register configurations */
status = rndis_config_register(cdev);
@@ -310,6 +289,7 @@
status = cdc_config_register(cdev);
if (unlikely(status < 0))
goto fail2;
+ usb_composite_overwrite_options(cdev, &coverwrite);
/* we're done */
dev_info(&gadget->dev, DRIVER_DESC "\n");
@@ -338,20 +318,20 @@
/****************************** Some noise ******************************/
-static struct usb_composite_driver multi_driver = {
+static __refdata struct usb_composite_driver multi_driver = {
.name = "g_multi",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = multi_bind,
.unbind = __exit_p(multi_unbind),
- .iProduct = DRIVER_DESC,
.needs_serial = 1,
};
static int __init multi_init(void)
{
- return usb_composite_probe(&multi_driver, multi_bind);
+ return usb_composite_probe(&multi_driver);
}
module_init(multi_init);
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index 75db2c3..ea45224 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -51,9 +51,8 @@
#define EPSTATUS_TIMEOUT 10000
#define PRIME_TIMEOUT 10000
#define READSAFE_TIMEOUT 1000
-#define DTD_TIMEOUT 1000
-#define LOOPS_USEC_SHIFT 4
+#define LOOPS_USEC_SHIFT 1
#define LOOPS_USEC (1 << LOOPS_USEC_SHIFT)
#define LOOPS(timeout) ((timeout) >> LOOPS_USEC_SHIFT)
@@ -64,7 +63,6 @@
/* controller device global variable */
static struct mv_udc *the_controller;
-int mv_usb_otgsc;
static void nuke(struct mv_ep *ep, int status);
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver);
@@ -357,17 +355,24 @@
return retval;
}
-
static struct mv_dtd *build_dtd(struct mv_req *req, unsigned *length,
dma_addr_t *dma, int *is_last)
{
- u32 temp;
struct mv_dtd *dtd;
struct mv_udc *udc;
+ struct mv_dqh *dqh;
+ u32 temp, mult = 0;
/* how big will this transfer be? */
- *length = min(req->req.length - req->req.actual,
- (unsigned)EP_MAX_LENGTH_TRANSFER);
+ if (usb_endpoint_xfer_isoc(req->ep->ep.desc)) {
+ dqh = req->ep->dqh;
+ mult = (dqh->max_packet_length >> EP_QUEUE_HEAD_MULT_POS)
+ & 0x3;
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)(mult * req->ep->ep.maxpacket));
+ } else
+ *length = min(req->req.length - req->req.actual,
+ (unsigned)EP_MAX_LENGTH_TRANSFER);
udc = req->ep->udc;
@@ -375,7 +380,7 @@
* Be careful that no _GFP_HIGHMEM is set,
* or we can not use dma_to_virt
*/
- dtd = dma_pool_alloc(udc->dtd_pool, GFP_KERNEL, dma);
+ dtd = dma_pool_alloc(udc->dtd_pool, GFP_ATOMIC, dma);
if (dtd == NULL)
return dtd;
@@ -409,6 +414,8 @@
if (*is_last && !req->req.no_interrupt)
temp |= DTD_IOC;
+ temp |= mult << 10;
+
dtd->size_ioc_sts = temp;
mb();
@@ -708,6 +715,7 @@
struct mv_req *req = container_of(_req, struct mv_req, req);
struct mv_udc *udc = ep->udc;
unsigned long flags;
+ int retval;
/* catch various bogus parameters */
if (!_req || !req->req.complete || !req->req.buf
@@ -719,10 +727,6 @@
dev_err(&udc->dev->dev, "%s, bad ep", __func__);
return -EINVAL;
}
- if (ep->ep.desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
- if (req->req.length > ep->ep.maxpacket)
- return -EMSGSIZE;
- }
udc = ep->udc;
if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
@@ -755,15 +759,17 @@
/* build dtds and push them to device queue */
if (!req_to_dtd(req)) {
- int retval;
retval = queue_dtd(ep, req);
if (retval) {
spin_unlock_irqrestore(&udc->lock, flags);
- return retval;
+ dev_err(&udc->dev->dev, "Failed to queue dtd\n");
+ goto err_unmap_dma;
}
} else {
spin_unlock_irqrestore(&udc->lock, flags);
- return -ENOMEM;
+ dev_err(&udc->dev->dev, "Failed to dma_pool_alloc\n");
+ retval = -ENOMEM;
+ goto err_unmap_dma;
}
/* Update ep0 state */
@@ -775,6 +781,22 @@
spin_unlock_irqrestore(&udc->lock, flags);
return 0;
+
+err_unmap_dma:
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ } else
+ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+
+ return retval;
}
static void mv_prime_ep(struct mv_ep *ep, struct mv_req *req)
@@ -1065,7 +1087,7 @@
tmp |= USBMODE_CTRL_MODE_DEVICE;
/* turn setup lockout off, require setup tripwire in usbcmd */
- tmp |= USBMODE_SETUP_LOCK_OFF | USBMODE_STREAM_DISABLE;
+ tmp |= USBMODE_SETUP_LOCK_OFF;
writel(tmp, &udc->op_regs->usbmode);
@@ -1199,12 +1221,16 @@
udc_start(udc);
}
} else if (udc->driver && udc->softconnect) {
+ if (!udc->active)
+ goto out;
+
/* stop all the transfer in queue*/
stop_activity(udc, udc->driver);
udc_stop(udc);
mv_udc_disable(udc);
}
+out:
spin_unlock_irqrestore(&udc->lock, flags);
return retval;
}
@@ -1243,7 +1269,7 @@
}
static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int mv_udc_stop(struct usb_gadget_driver *driver);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops mv_ops = {
@@ -1348,7 +1374,7 @@
}
static int mv_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct mv_udc *udc = the_controller;
int retval = 0;
@@ -1373,7 +1399,7 @@
spin_unlock_irqrestore(&udc->lock, flags);
- retval = bind(&udc->gadget);
+ retval = bind(&udc->gadget, driver);
if (retval) {
dev_err(&udc->dev->dev, "bind to driver %s --> %d\n",
driver->driver.name, retval);
@@ -1499,15 +1525,17 @@
}
/* prime the data phase */
- if (!req_to_dtd(req))
+ if (!req_to_dtd(req)) {
retval = queue_dtd(ep, req);
- else{ /* no mem */
+ if (retval) {
+ dev_err(&udc->dev->dev,
+ "Failed to queue dtd when prime status\n");
+ goto out;
+ }
+ } else{ /* no mem */
retval = -ENOMEM;
- goto out;
- }
-
- if (retval) {
- dev_err(&udc->dev->dev, "response error on GET_STATUS request\n");
+ dev_err(&udc->dev->dev,
+ "Failed to dma_pool_alloc when prime status\n");
goto out;
}
@@ -1515,6 +1543,15 @@
return 0;
out:
+ if (req->mapped) {
+ dma_unmap_single(ep->udc->gadget.dev.parent,
+ req->req.dma, req->req.length,
+ ((ep_dir(ep) == EP_DIR_IN) ?
+ DMA_TO_DEVICE : DMA_FROM_DEVICE));
+ req->req.dma = DMA_ADDR_INVALID;
+ req->mapped = 0;
+ }
+
return retval;
}
@@ -2468,9 +2505,11 @@
u32 mode;
/* reset controller mode to IDLE */
+ mv_udc_enable(udc);
mode = readl(&udc->op_regs->usbmode);
mode &= ~3;
writel(mode, &udc->op_regs->usbmode);
+ mv_udc_disable(udc);
}
static struct platform_driver udc_driver = {
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/ncm.c
index 8953003..a22ad9a 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/ncm.c
@@ -20,8 +20,8 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
-#include <linux/utsname.h>
-
+#include <linux/module.h>
+#include <linux/usb/composite.h>
#include "u_ether.h"
@@ -36,11 +36,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_ncm.c"
#include "u_ether.c"
@@ -57,6 +52,7 @@
#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
@@ -97,17 +93,11 @@
NULL,
};
-
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-
-static char manufacturer[50];
-
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = DRIVER_DESC,
+ [USB_GADGET_SERIAL_IDX].s = "",
{ } /* end of list */
};
@@ -149,7 +139,6 @@
static int __init gncm_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -158,48 +147,22 @@
if (status < 0)
return status;
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
- else {
- /* We assume that can_support_ecm() tells the truth;
- * but if the controller isn't recognized at all then
- * that assumption is a bit more likely to be wrong.
- */
- dev_warn(&gadget->dev,
- "controller '%s' not recognized; trying %s\n",
- gadget->name,
- ncm_config_driver.label);
- device_desc.bcdDevice =
- cpu_to_le16(0x0300 | 0x0099);
- }
-
-
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- /* device descriptor strings: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
- device_desc.iProduct = status;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
status = usb_add_config(cdev, &ncm_config_driver,
ncm_do_config);
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", DRIVER_DESC);
return 0;
@@ -215,11 +178,12 @@
return 0;
}
-static struct usb_composite_driver ncm_driver = {
+static __refdata struct usb_composite_driver ncm_driver = {
.name = "g_ncm",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = gncm_bind,
.unbind = __exit_p(gncm_unbind),
};
@@ -229,7 +193,7 @@
static int __init init(void)
{
- return usb_composite_probe(&ncm_driver, gncm_bind);
+ return usb_composite_probe(&ncm_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c
index c7fb772..661600a 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/nokia.c
@@ -16,7 +16,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include "u_serial.h"
@@ -38,11 +37,6 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "u_serial.c"
#include "f_acm.c"
#include "f_ecm.c"
@@ -52,23 +46,23 @@
#include "u_ether.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define NOKIA_VENDOR_ID 0x0421 /* Nokia */
#define NOKIA_PRODUCT_ID 0x01c8 /* Nokia Gadget */
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static char manufacturer_nokia[] = "Nokia";
static const char product_nokia[] = NOKIA_LONG_NAME;
static const char description_nokia[] = "PC-Suite Configuration";
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer_nokia,
- [STRING_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [USB_GADGET_MANUFACTURER_IDX].s = manufacturer_nokia,
+ [USB_GADGET_PRODUCT_IDX].s = NOKIA_LONG_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = description_nokia,
{ } /* end of list */
};
@@ -90,6 +84,7 @@
.bDeviceClass = USB_CLASS_COMM,
.idVendor = __constant_cpu_to_le16(NOKIA_VENDOR_ID),
.idProduct = __constant_cpu_to_le16(NOKIA_PRODUCT_ID),
+ .bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
@@ -151,7 +146,6 @@
static int __init nokia_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
struct usb_gadget *gadget = cdev->gadget;
int status;
@@ -167,41 +161,17 @@
if (status < 0)
goto err_ether;
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto err_usb;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
-
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_PRODUCT_IDX].id = status;
-
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- goto err_usb;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
nokia_config_500ma_driver.iConfiguration = status;
nokia_config_100ma_driver.iConfiguration = status;
- /* set up other descriptors */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(NOKIA_VERSION_NUM);
- else {
- /* this should only work with hw that supports altsettings
- * and several endpoints, anything else, panic.
- */
- pr_err("nokia_bind: controller '%s' not recognized\n",
- gadget->name);
+ if (!gadget_supports_altsettings(gadget))
goto err_usb;
- }
/* finally register the configuration */
status = usb_add_config(cdev, &nokia_config_500ma_driver,
@@ -214,6 +184,7 @@
if (status < 0)
goto err_usb;
+ usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, "%s\n", NOKIA_LONG_NAME);
return 0;
@@ -237,17 +208,18 @@
return 0;
}
-static struct usb_composite_driver nokia_driver = {
+static __refdata struct usb_composite_driver nokia_driver = {
.name = "g_nokia",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = nokia_bind,
.unbind = __exit_p(nokia_unbind),
};
static int __init nokia_init(void)
{
- return usb_composite_probe(&nokia_driver, nokia_bind);
+ return usb_composite_probe(&nokia_driver);
}
module_init(nokia_init);
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index f9132ad..2a4749c 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -1308,7 +1308,7 @@
}
static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int omap_udc_stop(struct usb_gadget_driver *driver);
static struct usb_gadget_ops omap_gadget_ops = {
@@ -2040,7 +2040,7 @@
}
static int omap_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
int status = -ENODEV;
struct omap_ep *ep;
@@ -2082,7 +2082,7 @@
if (udc->dc_clk != NULL)
omap_udc_enable_clock(1);
- status = bind(&udc->gadget);
+ status = bind(&udc->gadget, driver);
if (status) {
DBG("bind to %s --> %d\n", driver->driver.name, status);
udc->gadget.dev.driver = NULL;
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index f4fb71c..6490c00 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -1236,7 +1236,7 @@
}
static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pch_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pch_udc_ops = {
.get_frame = pch_udc_pcd_get_frame,
@@ -2982,7 +2982,7 @@
}
static int pch_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pch_udc_dev *dev = pch_udc;
int retval;
@@ -3006,7 +3006,7 @@
dev->gadget.dev.driver = &driver->driver;
/* Invoke the bind routine of the gadget driver */
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
dev_err(&dev->pdev->dev, "%s: binding to %s returning %d\n",
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
index f1f9290..e156e3f 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/printer.c
@@ -22,7 +22,6 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/interrupt.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
#include <linux/fs.h>
@@ -38,25 +37,13 @@
#include <asm/unaligned.h>
#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
#include <linux/usb/gadget.h>
#include <linux/usb/g_printer.h>
#include "gadget_chips.h"
-
-/*
- * Kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
-/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_DESC "Printer Gadget"
#define DRIVER_VERSION "2007 OCT 06"
@@ -120,8 +107,7 @@
* parameters are in UTF-8 (superset of ASCII's 7 bit characters).
*/
-static char *iSerialNum;
-module_param(iSerialNum, charp, S_IRUGO);
+module_param_named(iSerialNum, coverwrite.serial_number, charp, S_IRUGO);
MODULE_PARM_DESC(iSerialNum, "1");
static char *iPNPstring;
@@ -141,18 +127,10 @@
* descriptors are built on demand.
*/
-#define STRING_MANUFACTURER 1
-#define STRING_PRODUCT 2
-#define STRING_SERIALNUM 3
-
/* holds our biggest descriptor */
#define USB_DESC_BUFSIZE 256
#define USB_BUFSIZE 8192
-/* This device advertises one configuration. */
-#define DEV_CONFIG_VALUE 1
-#define PRINTER_INTERFACE 0
-
static struct usb_device_descriptor device_desc = {
.bLength = sizeof device_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -162,16 +140,12 @@
.bDeviceProtocol = 0,
.idVendor = cpu_to_le16(PRINTER_VENDOR_NUM),
.idProduct = cpu_to_le16(PRINTER_PRODUCT_NUM),
- .iManufacturer = STRING_MANUFACTURER,
- .iProduct = STRING_PRODUCT,
- .iSerialNumber = STRING_SERIALNUM,
.bNumConfigurations = 1
};
static struct usb_interface_descriptor intf_desc = {
.bLength = sizeof intf_desc,
.bDescriptorType = USB_DT_INTERFACE,
- .bInterfaceNumber = PRINTER_INTERFACE,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_PRINTER,
.bInterfaceSubClass = 1, /* Printer Sub-Class */
@@ -252,7 +226,6 @@
/* descriptors that are built on-demand */
-static char manufacturer [50];
static char product_desc [40] = DRIVER_DESC;
static char serial_num [40] = "1";
static char pnp_string [1024] =
@@ -260,9 +233,9 @@
/* static strings, in UTF-8 */
static struct usb_string strings [] = {
- { STRING_MANUFACTURER, manufacturer, },
- { STRING_PRODUCT, product_desc, },
- { STRING_SERIALNUM, serial_num, },
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = product_desc,
+ [USB_GADGET_SERIAL_IDX].s = serial_num,
{ } /* end of list */
};
@@ -871,25 +844,13 @@
int result = 0;
/* Free the current interface */
- switch (dev->interface) {
- case PRINTER_INTERFACE:
- printer_reset_interface(dev);
- break;
- }
+ printer_reset_interface(dev);
- switch (number) {
- case PRINTER_INTERFACE:
- result = set_printer_interface(dev);
- if (result) {
- printer_reset_interface(dev);
- } else {
- dev->interface = PRINTER_INTERFACE;
- }
- break;
- default:
- result = -EINVAL;
- /* FALL THROUGH */
- }
+ result = set_printer_interface(dev);
+ if (result)
+ printer_reset_interface(dev);
+ else
+ dev->interface = number;
if (!result)
INFO(dev, "Using interface %x\n", number);
@@ -972,7 +933,7 @@
switch (ctrl->bRequest) {
case 0: /* Get the IEEE-1284 PNP String */
/* Only one printer interface is supported. */
- if ((wIndex>>8) != PRINTER_INTERFACE)
+ if ((wIndex>>8) != dev->interface)
break;
value = (pnp_string[0]<<8)|pnp_string[1];
@@ -983,7 +944,7 @@
case 1: /* Get Port Status */
/* Only one printer interface is supported. */
- if (wIndex != PRINTER_INTERFACE)
+ if (wIndex != dev->interface)
break;
*(u8 *)req->buf = dev->printer_status;
@@ -992,7 +953,7 @@
case 2: /* Soft Reset */
/* Only one printer interface is supported. */
- if (wIndex != PRINTER_INTERFACE)
+ if (wIndex != dev->interface)
break;
printer_soft_reset(dev);
@@ -1020,6 +981,37 @@
static int __init printer_func_bind(struct usb_configuration *c,
struct usb_function *f)
{
+ struct printer_dev *dev = container_of(f, struct printer_dev, function);
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usb_ep *in_ep, *out_ep;
+ int id;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ intf_desc.bInterfaceNumber = id;
+
+ /* all we really need is bulk IN/OUT */
+ in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
+ if (!in_ep) {
+autoconf_fail:
+ dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
+ cdev->gadget->name);
+ return -ENODEV;
+ }
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
+ if (!out_ep)
+ goto autoconf_fail;
+ out_ep->driver_data = out_ep; /* claim */
+
+ /* assumes that all endpoints are dual-speed */
+ hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+ hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
return 0;
}
@@ -1035,7 +1027,8 @@
int ret = -ENOTSUPP;
if (!alt)
- ret = set_interface(dev, PRINTER_INTERFACE);
+ ret = set_interface(dev, intf);
+
return ret;
}
@@ -1107,13 +1100,13 @@
{
struct usb_gadget *gadget = c->cdev->gadget;
struct printer_dev *dev;
- struct usb_ep *in_ep, *out_ep;
int status = -ENOMEM;
- int gcnum;
size_t len;
u32 i;
struct usb_request *req;
+ usb_ep_autoconfig_reset(gadget);
+
dev = &usb_printer_gadget;
dev->function.name = shortname;
@@ -1125,6 +1118,10 @@
dev->function.set_alt = printer_func_set_alt;
dev->function.disable = printer_func_disable;
+ status = usb_add_function(c, &dev->function);
+ if (status)
+ return status;
+
/* Setup the sysfs files for the printer gadget. */
dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
NULL, "g_printer");
@@ -1145,23 +1142,6 @@
goto fail;
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0) {
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- } else {
- dev_warn(&gadget->dev, "controller '%s' not recognized\n",
- gadget->name);
- /* unrecognized, but safe unless bulk is REALLY quirky */
- device_desc.bcdDevice =
- cpu_to_le16(0xFFFF);
- }
- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
- if (iSerialNum)
- strlcpy(serial_num, iSerialNum, sizeof serial_num);
-
if (iPNPstring)
strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2);
@@ -1169,26 +1149,6 @@
pnp_string[0] = (len >> 8) & 0xFF;
pnp_string[1] = len & 0xFF;
- /* all we really need is bulk IN/OUT */
- usb_ep_autoconfig_reset(gadget);
- in_ep = usb_ep_autoconfig(gadget, &fs_ep_in_desc);
- if (!in_ep) {
-autoconf_fail:
- dev_err(&gadget->dev, "can't autoconfigure on %s\n",
- gadget->name);
- return -ENODEV;
- }
- in_ep->driver_data = in_ep; /* claim */
-
- out_ep = usb_ep_autoconfig(gadget, &fs_ep_out_desc);
- if (!out_ep)
- goto autoconf_fail;
- out_ep->driver_data = out_ep; /* claim */
-
- /* assumes that all endpoints are dual-speed */
- hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
- hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
-
usb_gadget_set_selfpowered(gadget);
if (gadget->is_otg) {
@@ -1215,9 +1175,6 @@
dev->current_rx_bytes = 0;
dev->current_rx_buf = NULL;
- dev->in_ep = in_ep;
- dev->out_ep = out_ep;
-
for (i = 0; i < QLEN; i++) {
req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
if (!req) {
@@ -1250,8 +1207,6 @@
dev->gadget = gadget;
INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name, out_ep->name,
- in_ep->name);
return 0;
fail:
@@ -1266,14 +1221,28 @@
static int __init printer_bind(struct usb_composite_dev *cdev)
{
- return usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
+ int ret;
+
+ ret = usb_string_ids_tab(cdev, strings);
+ if (ret < 0)
+ return ret;
+ device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
+
+ ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
+ if (ret)
+ return ret;
+ usb_composite_overwrite_options(cdev, &coverwrite);
+ return ret;
}
-static struct usb_composite_driver printer_driver = {
+static __refdata struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_HIGH,
+ .bind = printer_bind,
.unbind = printer_unbind,
};
@@ -1297,7 +1266,7 @@
return status;
}
- status = usb_composite_probe(&printer_driver, printer_bind);
+ status = usb_composite_probe(&printer_driver);
if (status) {
class_destroy(usb_gadget_class);
unregister_chrdev_region(g_printer_devno, 1);
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 907ad3e..8efbf08 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -33,7 +33,6 @@
#include <linux/dma-mapping.h>
#include <linux/irq.h>
#include <linux/clk.h>
-#include <linux/err.h>
#include <linux/seq_file.h>
#include <linux/debugfs.h>
#include <linux/io.h>
@@ -1000,7 +999,7 @@
}
static int pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pxa25x_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa25x_udc_ops = {
@@ -1258,7 +1257,7 @@
* the driver might get unbound.
*/
static int pxa25x_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pxa25x_udc *dev = the_controller;
int retval;
@@ -1286,7 +1285,7 @@
dev->gadget.dev.driver = NULL;
return retval;
}
- retval = bind(&dev->gadget);
+ retval = bind(&dev->gadget, driver);
if (retval) {
DMSG("bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
index 861f4df..2eca1e7 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/pxa25x_udc.h
@@ -225,7 +225,7 @@
dev->stats.read.bytes, dev->stats.read.ops);
for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
- if (dev->ep [i].desc == NULL)
+ if (dev->ep[i].ep.desc == NULL)
continue;
DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
}
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 644b430..979ddad 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1672,7 +1672,7 @@
}
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int pxa27x_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops pxa_udc_ops = {
@@ -1803,7 +1803,7 @@
* Returns 0 if no error, -EINVAL, -ENODEV, -EBUSY otherwise
*/
static int pxa27x_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct pxa_udc *udc = the_controller;
int retval;
@@ -1826,7 +1826,7 @@
dev_err(udc->dev, "device_add error %d\n", retval);
goto add_fail;
}
- retval = bind(&udc->gadget);
+ retval = bind(&udc->gadget, driver);
if (retval) {
dev_err(udc->dev, "bind to driver %s --> error %d\n",
driver->driver.name, retval);
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index b35babe..e4192b8 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -863,26 +863,8 @@
*/
pr_warning("%s: unknown RNDIS message 0x%08X len %d\n",
__func__, MsgType, MsgLength);
- {
- unsigned i;
- for (i = 0; i < MsgLength; i += 16) {
- pr_debug("%03d: "
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- " %02x %02x %02x %02x"
- "\n",
- i,
- buf[i], buf [i+1],
- buf[i+2], buf[i+3],
- buf[i+4], buf [i+5],
- buf[i+6], buf[i+7],
- buf[i+8], buf [i+9],
- buf[i+10], buf[i+11],
- buf[i+12], buf [i+13],
- buf[i+14], buf[i+15]);
- }
- }
+ print_hex_dump_bytes(__func__, DUMP_PREFIX_OFFSET,
+ buf, MsgLength);
break;
}
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index b13e0bb..6f696ee 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2197,7 +2197,7 @@
/* issue soft reset */
writel(GRSTCTL_CSftRst, hsotg->regs + GRSTCTL);
- timeout = 1000;
+ timeout = 10000;
do {
grstctl = readl(hsotg->regs + GRSTCTL);
} while ((grstctl & GRSTCTL_CSftRst) && timeout-- > 0);
@@ -2207,7 +2207,7 @@
return -EINVAL;
}
- timeout = 1000;
+ timeout = 10000;
while (1) {
u32 grstctl = readl(hsotg->regs + GRSTCTL);
@@ -3516,7 +3516,7 @@
hsotg->dev = dev;
hsotg->plat = plat;
- hsotg->clk = clk_get(&pdev->dev, "otg");
+ hsotg->clk = devm_clk_get(&pdev->dev, "otg");
if (IS_ERR(hsotg->clk)) {
dev_err(dev, "cannot get otg clock\n");
return PTR_ERR(hsotg->clk);
@@ -3599,6 +3599,7 @@
if (hsotg->num_of_eps == 0) {
dev_err(dev, "wrong number of EPs (zero)\n");
+ ret = -EINVAL;
goto err_supplies;
}
@@ -3606,6 +3607,7 @@
GFP_KERNEL);
if (!eps) {
dev_err(dev, "cannot get memory\n");
+ ret = -ENOMEM;
goto err_supplies;
}
@@ -3622,6 +3624,7 @@
GFP_KERNEL);
if (!hsotg->ctrl_req) {
dev_err(dev, "failed to allocate ctrl req\n");
+ ret = -ENOMEM;
goto err_ep_mem;
}
@@ -3664,7 +3667,6 @@
err_clk:
clk_disable_unprepare(hsotg->clk);
- clk_put(hsotg->clk);
return ret;
}
@@ -3690,7 +3692,6 @@
regulator_bulk_free(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
clk_disable_unprepare(hsotg->clk);
- clk_put(hsotg->clk);
device_unregister(&hsotg->gadget.dev);
return 0;
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index e26a4e7..d8e785d 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -135,7 +135,6 @@
* @dev: The device reference used by probe function.
* @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
* @regs: Remapped base address of controller's register space.
- * @mem_rsrc: Device memory resource used for remapping device register space.
* irq: IRQ number used by the controller.
* uclk: Reference to the controller clock.
* ep0state: Current state of EP0.
@@ -150,7 +149,6 @@
struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsudc_supply_names)];
spinlock_t lock;
void __iomem *regs;
- struct resource *mem_rsrc;
int irq;
struct clk *uclk;
int ep0state;
@@ -835,9 +833,9 @@
{
struct s3c_hsudc_req *hsreq;
- hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+ hsreq = kzalloc(sizeof(*hsreq), gfp_flags);
if (!hsreq)
- return 0;
+ return NULL;
INIT_LIST_HEAD(&hsreq->queue);
return &hsreq->req;
@@ -906,16 +904,16 @@
csr = readl((u32)hsudc->regs + offset);
if (!(csr & S3C_ESR_TX_SUCCESS) &&
(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
} else {
csr = readl((u32)hsudc->regs + offset);
if ((csr & S3C_ESR_RX_SUCCESS)
&& (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
- hsreq = 0;
+ hsreq = NULL;
}
}
- if (hsreq != 0)
+ if (hsreq)
list_add_tail(&hsreq->queue, &hsep->queue);
spin_unlock_irqrestore(&hsudc->lock, flags);
@@ -1271,7 +1269,7 @@
struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
int ret, i;
- hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+ hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
sizeof(struct s3c_hsudc_ep) * pd->epnum,
GFP_KERNEL);
if (!hsudc) {
@@ -1296,25 +1294,12 @@
}
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- dev_err(dev, "unable to obtain driver resource data\n");
- ret = -ENODEV;
- goto err_res;
- }
- hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
- dev_name(&pdev->dev));
- if (!hsudc->mem_rsrc) {
- dev_err(dev, "failed to reserve register area\n");
- ret = -ENODEV;
- goto err_res;
- }
-
- hsudc->regs = ioremap(res->start, resource_size(res));
+ hsudc->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hsudc->regs) {
dev_err(dev, "error mapping device register area\n");
ret = -EBUSY;
- goto err_remap;
+ goto err_res;
}
spin_lock_init(&hsudc->lock);
@@ -1337,21 +1322,22 @@
ret = platform_get_irq(pdev, 0);
if (ret < 0) {
dev_err(dev, "unable to obtain IRQ number\n");
- goto err_irq;
+ goto err_res;
}
hsudc->irq = ret;
- ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+ ret = devm_request_irq(&pdev->dev, hsudc->irq, s3c_hsudc_irq, 0,
+ driver_name, hsudc);
if (ret < 0) {
dev_err(dev, "irq request failed\n");
- goto err_irq;
+ goto err_res;
}
- hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+ hsudc->uclk = devm_clk_get(&pdev->dev, "usb-device");
if (IS_ERR(hsudc->uclk)) {
dev_err(dev, "failed to find usb-device clock source\n");
ret = PTR_ERR(hsudc->uclk);
- goto err_clk;
+ goto err_res;
}
clk_enable(hsudc->uclk);
@@ -1377,21 +1363,12 @@
device_unregister(&hsudc->gadget.dev);
err_add_device:
clk_disable(hsudc->uclk);
- clk_put(hsudc->uclk);
-err_clk:
- free_irq(hsudc->irq, hsudc);
-err_irq:
- iounmap(hsudc->regs);
-
-err_remap:
- release_mem_region(res->start, resource_size(res));
err_res:
if (!IS_ERR_OR_NULL(hsudc->transceiver))
usb_put_phy(hsudc->transceiver);
regulator_bulk_free(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
err_supplies:
- kfree(hsudc);
return ret;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index f2e51f5..c33e942 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -12,6 +12,8 @@
* (at your option) any later version.
*/
+#define pr_fmt(fmt) "s3c2410_udc: " fmt
+
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
@@ -27,6 +29,7 @@
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/prefetch.h>
+#include <linux/io.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
@@ -35,7 +38,6 @@
#include <linux/usb/gadget.h>
#include <asm/byteorder.h>
-#include <asm/io.h>
#include <asm/irq.h>
#include <asm/unaligned.h>
#include <mach/irqs.h>
@@ -115,7 +117,7 @@
sizeof(printk_buf)-len, fmt, args);
va_end(args);
- return printk(KERN_DEBUG "%s", printk_buf);
+ return pr_debug("%s", printk_buf);
}
#else
static int dprintk(int level, const char *fmt, ...)
@@ -125,10 +127,10 @@
#endif
static int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p)
{
- u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;
+ u32 addr_reg, pwr_reg, ep_int_reg, usb_int_reg;
u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;
- u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;
- u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;
+ u32 ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2;
+ u32 ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2;
addr_reg = udc_read(S3C2410_UDC_FUNC_ADDR_REG);
pwr_reg = udc_read(S3C2410_UDC_PWR_REG);
@@ -164,10 +166,10 @@
"EP2_I_CSR2 : 0x%04X\n"
"EP2_O_CSR1 : 0x%04X\n"
"EP2_O_CSR2 : 0x%04X\n",
- addr_reg,pwr_reg,ep_int_reg,usb_int_reg,
+ addr_reg, pwr_reg, ep_int_reg, usb_int_reg,
ep_int_en_reg, usb_int_en_reg, ep0_csr,
- ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,
- ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2
+ ep1_i_csr1, ep1_i_csr2, ep1_o_csr1, ep1_o_csr2,
+ ep2_i_csr1, ep2_i_csr2, ep2_o_csr1, ep2_o_csr2
);
return 0;
@@ -230,7 +232,7 @@
{
udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
- udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY
+ udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
| S3C2410_UDC_EP0_CSR_DE),
S3C2410_UDC_EP0_CSR_REG);
}
@@ -263,7 +265,7 @@
list_del_init(&req->queue);
- if (likely (req->req.status == -EINPROGRESS))
+ if (likely(req->req.status == -EINPROGRESS))
req->req.status = status;
else
status = req->req.status;
@@ -280,9 +282,9 @@
if (&ep->queue == NULL)
return;
- while (!list_empty (&ep->queue)) {
+ while (!list_empty(&ep->queue)) {
struct s3c2410_request *req;
- req = list_entry (ep->queue.next, struct s3c2410_request,
+ req = list_entry(ep->queue.next, struct s3c2410_request,
queue);
s3c2410_udc_done(ep, req, status);
}
@@ -389,10 +391,10 @@
if (idx == 0) {
/* Reset signal => no need to say 'data sent' */
- if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+ if (!(udc_read(S3C2410_UDC_USB_INT_REG)
& S3C2410_UDC_USBINT_RESET))
s3c2410_udc_set_ep0_de_in(base_addr);
- ep->dev->ep0state=EP0_IDLE;
+ ep->dev->ep0state = EP0_IDLE;
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);
@@ -406,7 +408,7 @@
} else {
if (idx == 0) {
/* Reset signal => no need to say 'data sent' */
- if (! (udc_read(S3C2410_UDC_USB_INT_REG)
+ if (!(udc_read(S3C2410_UDC_USB_INT_REG)
& S3C2410_UDC_USBINT_RESET))
s3c2410_udc_set_ep0_ipr(base_addr);
} else {
@@ -442,7 +444,7 @@
u8 *buf;
u32 ep_csr;
unsigned bufferspace;
- int is_last=1;
+ int is_last = 1;
unsigned avail;
int fifo_count = 0;
u32 idx;
@@ -510,7 +512,7 @@
/* Only ep0 debug messages are interesting */
if (idx == 0)
dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n",
- __func__, fifo_count,is_last);
+ __func__, fifo_count, is_last);
if (is_last) {
if (idx == 0) {
@@ -542,7 +544,7 @@
static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq)
{
- unsigned char *outbuf = (unsigned char*)crq;
+ unsigned char *outbuf = (unsigned char *)crq;
int bytes_read = 0;
udc_write(0, S3C2410_UDC_INDEX_REG);
@@ -648,7 +650,7 @@
switch (crq->bRequest) {
case USB_REQ_SET_CONFIGURATION:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ...\n");
if (crq->bRequestType == USB_RECIP_DEVICE) {
dev->req_config = 1;
@@ -657,7 +659,7 @@
break;
case USB_REQ_SET_INTERFACE:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ...\n");
if (crq->bRequestType == USB_RECIP_INTERFACE) {
dev->req_config = 1;
@@ -666,7 +668,7 @@
break;
case USB_REQ_SET_ADDRESS:
- dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ...\n");
if (crq->bRequestType == USB_RECIP_DEVICE) {
tmp = crq->wValue & 0x7F;
@@ -679,13 +681,12 @@
break;
case USB_REQ_GET_STATUS:
- dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n");
+ dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ...\n");
s3c2410_udc_clear_ep0_opr(base_addr);
if (dev->req_std) {
- if (!s3c2410_udc_get_status(dev, crq)) {
+ if (!s3c2410_udc_get_status(dev, crq))
return;
- }
}
break;
@@ -750,7 +751,7 @@
/* deferred i/o == no response yet */
} else if (dev->req_pending) {
dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n");
- dev->req_pending=0;
+ dev->req_pending = 0;
}
dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]);
@@ -801,16 +802,14 @@
case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n");
- if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) {
+ if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req)
s3c2410_udc_write_fifo(ep, req);
- }
break;
case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n");
- if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) {
- s3c2410_udc_read_fifo(ep,req);
- }
+ if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req)
+ s3c2410_udc_read_fifo(ep, req);
break;
case EP0_END_XFER:
@@ -836,7 +835,7 @@
u32 ep_csr1;
u32 idx;
- if (likely (!list_empty(&ep->queue)))
+ if (likely(!list_empty(&ep->queue)))
req = list_entry(ep->queue.next,
struct s3c2410_request, queue);
else
@@ -858,9 +857,8 @@
return;
}
- if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) {
- s3c2410_udc_write_fifo(ep,req);
- }
+ if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req)
+ s3c2410_udc_write_fifo(ep, req);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG);
@@ -873,9 +871,8 @@
return;
}
- if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) {
- s3c2410_udc_read_fifo(ep,req);
- }
+ if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req)
+ s3c2410_udc_read_fifo(ep, req);
}
}
@@ -1057,7 +1054,7 @@
struct s3c2410_ep *ep;
u32 max, tmp;
unsigned long flags;
- u32 csr1,csr2;
+ u32 csr1, csr2;
u32 int_en_reg;
ep = to_s3c2410_ep(_ep);
@@ -1073,7 +1070,7 @@
max = usb_endpoint_maxp(desc) & 0x1fff;
- local_irq_save (flags);
+ local_irq_save(flags);
_ep->maxpacket = max & 0x7ff;
ep->ep.desc = desc;
ep->halted = 0;
@@ -1117,11 +1114,11 @@
/* print some debug message */
tmp = desc->bEndpointAddress;
- dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
- _ep->name,ep->num, tmp,
+ dprintk(DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n",
+ _ep->name, ep->num, tmp,
desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max);
- local_irq_restore (flags);
+ local_irq_restore(flags);
s3c2410_udc_set_halt(_ep, 0);
return 0;
@@ -1149,7 +1146,7 @@
ep->ep.desc = NULL;
ep->halted = 1;
- s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN);
+ s3c2410_udc_nuke(ep->dev, ep, -ESHUTDOWN);
/* disable irqs */
int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG);
@@ -1170,16 +1167,16 @@
{
struct s3c2410_request *req;
- dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags);
+ dprintk(DEBUG_VERBOSE, "%s(%p,%d)\n", __func__, _ep, mem_flags);
if (!_ep)
return NULL;
- req = kzalloc (sizeof(struct s3c2410_request), mem_flags);
+ req = kzalloc(sizeof(struct s3c2410_request), mem_flags);
if (!req)
return NULL;
- INIT_LIST_HEAD (&req->queue);
+ INIT_LIST_HEAD(&req->queue);
return &req->req;
}
@@ -1197,7 +1194,7 @@
if (!ep || !_req || (!ep->ep.desc && _ep->name != ep0name))
return;
- WARN_ON (!list_empty (&req->queue));
+ WARN_ON(!list_empty(&req->queue));
kfree(req);
}
@@ -1220,12 +1217,12 @@
}
dev = ep->dev;
- if (unlikely (!dev->driver
+ if (unlikely(!dev->driver
|| dev->gadget.speed == USB_SPEED_UNKNOWN)) {
return -ESHUTDOWN;
}
- local_irq_save (flags);
+ local_irq_save(flags);
if (unlikely(!_req || !_req->complete
|| !_req->buf || !list_empty(&req->queue))) {
@@ -1233,7 +1230,7 @@
dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__);
else {
dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n",
- __func__, !_req->complete,!_req->buf,
+ __func__, !_req->complete, !_req->buf,
!list_empty(&req->queue));
}
@@ -1299,7 +1296,7 @@
}
/* pio or dma irq handler advances the queue. */
- if (likely (req != 0))
+ if (likely(req))
list_add_tail(&req->queue, &ep->queue);
local_irq_restore(flags);
@@ -1329,11 +1326,11 @@
udc = to_s3c2410_udc(ep->gadget);
- local_irq_save (flags);
+ local_irq_save(flags);
- list_for_each_entry (req, &ep->queue, queue) {
+ list_for_each_entry(req, &ep->queue, queue) {
if (&req->req == _req) {
- list_del_init (&req->queue);
+ list_del_init(&req->queue);
_req->status = -ECONNRESET;
retval = 0;
break;
@@ -1348,7 +1345,7 @@
s3c2410_udc_done(ep, req, -ECONNRESET);
}
- local_irq_restore (flags);
+ local_irq_restore(flags);
return retval;
}
@@ -1367,7 +1364,7 @@
return -EINVAL;
}
- local_irq_save (flags);
+ local_irq_save(flags);
idx = ep->bEndpointAddress & 0x7F;
@@ -1376,7 +1373,7 @@
s3c2410_udc_set_ep0_de_out(base_addr);
} else {
udc_write(idx, S3C2410_UDC_INDEX_REG);
- ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN)
+ ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN)
? S3C2410_UDC_IN_CSR1_REG
: S3C2410_UDC_OUT_CSR1_REG);
@@ -1404,7 +1401,7 @@
}
ep->halted = value ? 1 : 0;
- local_irq_restore (flags);
+ local_irq_restore(flags);
return 0;
}
@@ -1484,9 +1481,9 @@
}
s3c2410_udc_disable(udc);
}
- }
- else
+ } else {
return -EOPNOTSUPP;
+ }
return 0;
}
@@ -1542,7 +1539,7 @@
}
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *));
static int s3c2410_udc_stop(struct usb_gadget_driver *driver);
static const struct usb_gadget_ops s3c2410_ops = {
@@ -1617,20 +1614,20 @@
u32 i;
/* device/ep0 records init */
- INIT_LIST_HEAD (&dev->gadget.ep_list);
- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
dev->ep0state = EP0_IDLE;
for (i = 0; i < S3C2410_ENDPOINTS; i++) {
struct s3c2410_ep *ep = &dev->ep[i];
if (i != 0)
- list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
ep->dev = dev;
ep->ep.desc = NULL;
ep->halted = 0;
- INIT_LIST_HEAD (&ep->queue);
+ INIT_LIST_HEAD(&ep->queue);
}
}
@@ -1668,7 +1665,7 @@
}
static int s3c2410_udc_start(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
struct s3c2410_udc *udc = the_controller;
int retval;
@@ -1683,13 +1680,13 @@
return -EBUSY;
if (!bind || !driver->setup || driver->max_speed < USB_SPEED_FULL) {
- printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n",
+ dev_err(&udc->gadget.dev, "Invalid driver: bind %p setup %p speed %d\n",
bind, driver->setup, driver->max_speed);
return -EINVAL;
}
#if defined(MODULE)
if (!driver->unbind) {
- printk(KERN_ERR "Invalid driver: no unbind method\n");
+ dev_err(&udc->gadget.dev, "Invalid driver: no unbind method\n");
return -EINVAL;
}
#endif
@@ -1699,15 +1696,17 @@
udc->gadget.dev.driver = &driver->driver;
/* Bind the driver */
- if ((retval = device_add(&udc->gadget.dev)) != 0) {
- printk(KERN_ERR "Error in device_add() : %d\n",retval);
+ retval = device_add(&udc->gadget.dev);
+ if (retval) {
+ dev_err(&udc->gadget.dev, "Error in device_add() : %d\n", retval);
goto register_error;
}
dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n",
driver->driver.name);
- if ((retval = bind(&udc->gadget)) != 0) {
+ retval = bind(&udc->gadget, driver);
+ if (retval) {
device_del(&udc->gadget.dev);
goto register_error;
}
@@ -1865,7 +1864,7 @@
memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE;
}
- spin_lock_init (&udc->lock);
+ spin_lock_init(&udc->lock);
udc_info = pdev->dev.platform_data;
rsrc_start = S3C2410_PA_USBDEV;
@@ -2028,7 +2027,8 @@
}
#ifdef CONFIG_PM
-static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
+static int
+s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message)
{
s3c2410_udc_command(S3C2410_UDC_P_DISABLE);
@@ -2073,7 +2073,7 @@
s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL);
if (IS_ERR(s3c2410_udc_debugfs_root)) {
- printk(KERN_ERR "%s: debugfs dir creation failed %ld\n",
+ pr_err("%s: debugfs dir creation failed %ld\n",
gadget_name, PTR_ERR(s3c2410_udc_debugfs_root));
s3c2410_udc_debugfs_root = NULL;
}
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index 665c074..44752f5 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -11,7 +11,6 @@
*/
#include <linux/kernel.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
@@ -37,17 +36,13 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_acm.c"
#include "f_obex.c"
#include "f_serial.c"
#include "u_serial.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
/* Thanks to NetChip Technologies for donating this product ID.
*
@@ -61,15 +56,12 @@
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
-
-static char manufacturer[50];
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = GS_VERSION_NAME,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
{ } /* end of list */
};
@@ -94,7 +86,7 @@
/* .bMaxPacketSize0 = f(hardware) */
.idVendor = cpu_to_le16(GS_VENDOR_ID),
/* .idProduct = f(use_acm) */
- /* .bcdDevice = f(hardware) */
+ .bcdDevice = cpu_to_le16(GS_VERSION_NUM),
/* .iManufacturer = DYNAMIC */
/* .iProduct = DYNAMIC */
.bNumConfigurations = 1,
@@ -162,8 +154,6 @@
static int __init gs_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
- struct usb_gadget *gadget = cdev->gadget;
int status;
status = gserial_setup(cdev->gadget, n_ports);
@@ -174,50 +164,14 @@
* contents can be overridden by the composite_dev glue.
*/
- /* device description: manufacturer, product */
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
- status = usb_string_id(cdev);
+ status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
goto fail;
- strings_dev[STRING_MANUFACTURER_IDX].id = status;
-
- device_desc.iManufacturer = status;
-
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_PRODUCT_IDX].id = status;
-
- device_desc.iProduct = status;
-
- /* config description */
- status = usb_string_id(cdev);
- if (status < 0)
- goto fail;
- strings_dev[STRING_DESCRIPTION_IDX].id = status;
-
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ status = strings_dev[STRING_DESCRIPTION_IDX].id;
serial_config_driver.iConfiguration = status;
- /* set up other descriptors */
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum);
- else {
- /* this is so simple (for now, no altsettings) that it
- * SHOULD NOT have problems with bulk-capable hardware.
- * so warn about unrcognized controllers -- don't panic.
- *
- * things like configuration and altsetting numbering
- * can need hardware-specific attention though.
- */
- pr_warning("gs_bind: controller '%s' not recognized\n",
- gadget->name);
- device_desc.bcdDevice =
- cpu_to_le16(GS_VERSION_NUM | 0x0099);
- }
-
if (gadget_is_otg(cdev->gadget)) {
serial_config_driver.descriptors = otg_desc;
serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
@@ -229,6 +183,7 @@
if (status < 0)
goto fail;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s\n", GS_VERSION_NAME);
return 0;
@@ -238,11 +193,12 @@
return status;
}
-static struct usb_composite_driver gserial_driver = {
+static __refdata struct usb_composite_driver gserial_driver = {
.name = "g_serial",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = gs_bind,
};
static int __init init(void)
@@ -271,7 +227,7 @@
}
strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
- return usb_composite_probe(&gserial_driver, gs_bind);
+ return usb_composite_probe(&gserial_driver);
}
module_init(init);
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c
index 5444866..eaa1005 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/tcm_usb_gadget.c
@@ -25,13 +25,10 @@
#include <target/configfs_macros.h>
#include <asm/unaligned.h>
-#include "usbstring.c"
-#include "epautoconf.c"
-#include "config.c"
-#include "composite.c"
-
#include "tcm_usb_gadget.h"
+USB_GADGET_COMPOSITE_OPTIONS();
+
static struct target_fabric_configfs *usbg_fabric_configfs;
static inline struct f_uas *to_f_uas(struct usb_function *f)
@@ -1977,7 +1974,6 @@
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI,
.bInterfaceProtocol = USB_PR_BULK,
- .iInterface = USB_G_STR_INT_UAS,
};
static struct usb_interface_descriptor uasp_intf_desc = {
@@ -1988,7 +1984,6 @@
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = USB_SC_SCSI,
.bInterfaceProtocol = USB_PR_UAS,
- .iInterface = USB_G_STR_INT_BBB,
};
static struct usb_endpoint_descriptor uasp_bi_desc = {
@@ -2209,20 +2204,16 @@
.bDeviceClass = USB_CLASS_PER_INTERFACE,
.idVendor = cpu_to_le16(UAS_VENDOR_ID),
.idProduct = cpu_to_le16(UAS_PRODUCT_ID),
- .iManufacturer = USB_G_STR_MANUFACTOR,
- .iProduct = USB_G_STR_PRODUCT,
- .iSerialNumber = USB_G_STR_SERIAL,
-
.bNumConfigurations = 1,
};
static struct usb_string usbg_us_strings[] = {
- { USB_G_STR_MANUFACTOR, "Target Manufactor"},
- { USB_G_STR_PRODUCT, "Target Product"},
- { USB_G_STR_SERIAL, "000000000001"},
- { USB_G_STR_CONFIG, "default config"},
- { USB_G_STR_INT_UAS, "USB Attached SCSI"},
- { USB_G_STR_INT_BBB, "Bulk Only Transport"},
+ [USB_GADGET_MANUFACTURER_IDX].s = "Target Manufactor",
+ [USB_GADGET_PRODUCT_IDX].s = "Target Product",
+ [USB_GADGET_SERIAL_IDX].s = "000000000001",
+ [USB_G_STR_CONFIG].s = "default config",
+ [USB_G_STR_INT_UAS].s = "USB Attached SCSI",
+ [USB_G_STR_INT_BBB].s = "Bulk Only Transport",
{ },
};
@@ -2244,7 +2235,6 @@
static struct usb_configuration usbg_config_driver = {
.label = "Linux Target",
.bConfigurationValue = 1,
- .iConfiguration = USB_G_STR_CONFIG,
.bmAttributes = USB_CONFIG_ATT_SELFPOWER,
};
@@ -2417,6 +2407,9 @@
fu->function.disable = usbg_disable;
fu->tpg = the_only_tpg_I_currently_have;
+ bot_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_BBB].id;
+ uasp_intf_desc.iInterface = usbg_us_strings[USB_G_STR_INT_UAS].id;
+
ret = usb_add_function(c, &fu->function);
if (ret)
goto err;
@@ -2431,22 +2424,38 @@
{
int ret;
+ ret = usb_string_ids_tab(cdev, usbg_us_strings);
+ if (ret)
+ return ret;
+
+ usbg_device_desc.iManufacturer =
+ usbg_us_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ usbg_device_desc.iProduct = usbg_us_strings[USB_GADGET_PRODUCT_IDX].id;
+ usbg_device_desc.iSerialNumber =
+ usbg_us_strings[USB_GADGET_SERIAL_IDX].id;
+ usbg_config_driver.iConfiguration =
+ usbg_us_strings[USB_G_STR_CONFIG].id;
+
ret = usb_add_config(cdev, &usbg_config_driver,
usbg_cfg_bind);
+ if (ret)
+ return ret;
+ usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
}
-static struct usb_composite_driver usbg_driver = {
+static __refdata struct usb_composite_driver usbg_driver = {
.name = "g_target",
.dev = &usbg_device_desc,
.strings = usbg_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = usb_target_bind,
.unbind = guas_unbind,
};
static int usbg_attach(struct usbg_tpg *tpg)
{
- return usb_composite_probe(&usbg_driver, usb_target_bind);
+ return usb_composite_probe(&usbg_driver);
}
static void usbg_detach(struct usbg_tpg *tpg)
diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/tcm_usb_gadget.h
index bb18999..8289219 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.h
+++ b/drivers/usb/gadget/tcm_usb_gadget.h
@@ -16,12 +16,11 @@
#define UASP_SS_EP_COMP_LOG_STREAMS 4
#define UASP_SS_EP_COMP_NUM_STREAMS (1 << UASP_SS_EP_COMP_LOG_STREAMS)
-#define USB_G_STR_MANUFACTOR 1
-#define USB_G_STR_PRODUCT 2
-#define USB_G_STR_SERIAL 3
-#define USB_G_STR_CONFIG 4
-#define USB_G_STR_INT_UAS 5
-#define USB_G_STR_INT_BBB 6
+enum {
+ USB_G_STR_CONFIG = USB_GADGET_FIRST_AVAIL_IDX,
+ USB_G_STR_INT_UAS,
+ USB_G_STR_INT_BBB,
+};
#define USB_G_ALT_INT_BBB 0
#define USB_G_ALT_INT_UAS 1
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 90e82e2..b9c4690 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -14,6 +14,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/gfp.h>
#include <linux/device.h>
#include <linux/ctype.h>
@@ -83,17 +84,10 @@
#define DEFAULT_QLEN 2 /* double buffering by default */
-
-#ifdef CONFIG_USB_GADGET_DUALSPEED
-
static unsigned qmult = 5;
module_param(qmult, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(qmult, "queue length multiplier at high/super speed");
-#else /* full speed (low speed doesn't do bulk) */
-#define qmult 1
-#endif
-
/* for dual-speed hardware, use deeper queues at high/super speed */
static inline int qlen(struct usb_gadget *gadget)
{
@@ -669,6 +663,8 @@
spin_lock_irqsave(&dev->lock, flags);
if (dev->port_usb) {
struct gether *link = dev->port_usb;
+ const struct usb_endpoint_descriptor *in;
+ const struct usb_endpoint_descriptor *out;
if (link->close)
link->close(link);
@@ -682,10 +678,14 @@
* their own pace; the network stack can handle old packets.
* For the moment we leave this here, since it works.
*/
+ in = link->in_ep->desc;
+ out = link->out_ep->desc;
usb_ep_disable(link->in_ep);
usb_ep_disable(link->out_ep);
if (netif_carrier_ok(net)) {
DBG(dev, "host still using in/out endpoints\n");
+ link->in_ep->desc = in;
+ link->out_ep->desc = out;
usb_ep_enable(link->in_ep);
usb_ep_enable(link->out_ep);
}
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc-core.c
index e5e44f8..f3cd969 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc-core.c
@@ -118,7 +118,7 @@
*/
static inline int usb_gadget_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+ int (*bind)(struct usb_gadget *, struct usb_gadget_driver *))
{
return gadget->ops->start(driver, bind);
}
@@ -262,8 +262,8 @@
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
if (udc_is_newstyle(udc)) {
- udc->driver->disconnect(udc->gadget);
usb_gadget_disconnect(udc->gadget);
+ udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
usb_gadget_udc_stop(udc->gadget, udc->driver);
} else {
@@ -311,13 +311,12 @@
/* ------------------------------------------------------------------------- */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *))
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret;
- if (!driver || !bind || !driver->setup)
+ if (!driver || !driver->bind || !driver->setup)
return -EINVAL;
mutex_lock(&udc_lock);
@@ -339,7 +338,7 @@
udc->dev.driver = &driver->driver;
if (udc_is_newstyle(udc)) {
- ret = bind(udc->gadget);
+ ret = driver->bind(udc->gadget, driver);
if (ret)
goto err1;
ret = usb_gadget_udc_start(udc->gadget, driver);
@@ -350,7 +349,7 @@
usb_gadget_connect(udc->gadget);
} else {
- ret = usb_gadget_start(udc->gadget, driver, bind);
+ ret = usb_gadget_start(udc->gadget, driver, driver->bind);
if (ret)
goto err1;
diff --git a/drivers/usb/gadget/usbstring.c b/drivers/usb/gadget/usbstring.c
index 4d25b90..1f49fce 100644
--- a/drivers/usb/gadget/usbstring.c
+++ b/drivers/usb/gadget/usbstring.c
@@ -9,6 +9,7 @@
#include <linux/errno.h>
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/list.h>
#include <linux/string.h>
#include <linux/device.h>
@@ -68,4 +69,4 @@
buf [1] = USB_DT_STRING;
return buf [0];
}
-
+EXPORT_SYMBOL_GPL(usb_gadget_get_string);
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/webcam.c
index 120e134..69cf5c2 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/webcam.c
@@ -23,16 +23,12 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "uvc_queue.c"
#include "uvc_video.c"
#include "uvc_v4l2.c"
#include "f_uvc.c"
+USB_GADGET_COMPOSITE_OPTIONS();
/* --------------------------------------------------------------------------
* Device descriptor
*/
@@ -47,13 +43,12 @@
/* string IDs are assigned dynamically */
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_DESCRIPTION_IDX 2
+#define STRING_DESCRIPTION_IDX USB_GADGET_FIRST_AVAIL_IDX
static struct usb_string webcam_strings[] = {
- [STRING_MANUFACTURER_IDX].s = webcam_vendor_label,
- [STRING_PRODUCT_IDX].s = webcam_product_label,
+ [USB_GADGET_MANUFACTURER_IDX].s = webcam_vendor_label,
+ [USB_GADGET_PRODUCT_IDX].s = webcam_product_label,
+ [USB_GADGET_SERIAL_IDX].s = "",
[STRING_DESCRIPTION_IDX].s = webcam_config_label,
{ }
};
@@ -358,26 +353,22 @@
/* Allocate string descriptor numbers ... note that string contents
* can be overridden by the composite_dev glue.
*/
- if ((ret = usb_string_id(cdev)) < 0)
+ ret = usb_string_ids_tab(cdev, webcam_strings);
+ if (ret < 0)
goto error;
- webcam_strings[STRING_MANUFACTURER_IDX].id = ret;
- webcam_device_descriptor.iManufacturer = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_PRODUCT_IDX].id = ret;
- webcam_device_descriptor.iProduct = ret;
-
- if ((ret = usb_string_id(cdev)) < 0)
- goto error;
- webcam_strings[STRING_DESCRIPTION_IDX].id = ret;
- webcam_config_driver.iConfiguration = ret;
+ webcam_device_descriptor.iManufacturer =
+ webcam_strings[USB_GADGET_MANUFACTURER_IDX].id;
+ webcam_device_descriptor.iProduct =
+ webcam_strings[USB_GADGET_PRODUCT_IDX].id;
+ webcam_config_driver.iConfiguration =
+ webcam_strings[STRING_DESCRIPTION_IDX].id;
/* Register our configuration. */
if ((ret = usb_add_config(cdev, &webcam_config_driver,
webcam_config_bind)) < 0)
goto error;
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "Webcam Video Gadget\n");
return 0;
@@ -390,18 +381,19 @@
* Driver
*/
-static struct usb_composite_driver webcam_driver = {
+static __refdata struct usb_composite_driver webcam_driver = {
.name = "g_webcam",
.dev = &webcam_device_descriptor,
.strings = webcam_device_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = webcam_bind,
.unbind = webcam_unbind,
};
static int __init
webcam_init(void)
{
- return usb_composite_probe(&webcam_driver, webcam_bind);
+ return usb_composite_probe(&webcam_driver);
}
static void __exit
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index 12ad516..6bf4c06 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -42,7 +42,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
-#include <linux/utsname.h>
#include <linux/device.h>
#include "g_zero.h"
@@ -58,15 +57,11 @@
* the runtime footprint, and giving us at least some parts of what
* a "gcc --combine ... part1.c part2.c part3.c ... " build would.
*/
-#include "composite.c"
-#include "usbstring.c"
-#include "config.c"
-#include "epautoconf.c"
-
#include "f_sourcesink.c"
#include "f_loopback.c"
/*-------------------------------------------------------------------------*/
+USB_GADGET_COMPOSITE_OPTIONS();
#define DRIVER_VERSION "Cinco de Mayo 2008"
@@ -141,20 +136,13 @@
#endif
/* string IDs are assigned dynamically */
-
-#define STRING_MANUFACTURER_IDX 0
-#define STRING_PRODUCT_IDX 1
-#define STRING_SERIAL_IDX 2
-
-static char manufacturer[50];
-
/* default serial number takes at least two packets */
static char serial[] = "0123456789.0123456789.0123456789";
static struct usb_string strings_dev[] = {
- [STRING_MANUFACTURER_IDX].s = manufacturer,
- [STRING_PRODUCT_IDX].s = longname,
- [STRING_SERIAL_IDX].s = serial,
+ [USB_GADGET_MANUFACTURER_IDX].s = "",
+ [USB_GADGET_PRODUCT_IDX].s = longname,
+ [USB_GADGET_SERIAL_IDX].s = serial,
{ } /* end of list */
};
@@ -265,30 +253,18 @@
static int __init zero_bind(struct usb_composite_dev *cdev)
{
- int gcnum;
- struct usb_gadget *gadget = cdev->gadget;
- int id;
+ int status;
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
*/
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_MANUFACTURER_IDX].id = id;
- device_desc.iManufacturer = id;
+ status = usb_string_ids_tab(cdev, strings_dev);
+ if (status < 0)
+ return status;
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_PRODUCT_IDX].id = id;
- device_desc.iProduct = id;
-
- id = usb_string_id(cdev);
- if (id < 0)
- return id;
- strings_dev[STRING_SERIAL_IDX].id = id;
- device_desc.iSerialNumber = id;
+ device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
+ device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
+ device_desc.iSerialNumber = strings_dev[USB_GADGET_SERIAL_IDX].id;
setup_timer(&autoresume_timer, zero_autoresume, (unsigned long) cdev);
@@ -303,28 +279,10 @@
loopback_add(cdev, autoresume != 0);
}
- gcnum = usb_gadget_controller_number(gadget);
- if (gcnum >= 0)
- device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
- else {
- /* gadget zero is so simple (for now, no altsettings) that
- * it SHOULD NOT have problems with bulk-capable hardware.
- * so just warn about unrcognized controllers -- don't panic.
- *
- * things like configuration and altsetting numbering
- * can need hardware-specific attention though.
- */
- pr_warning("%s: controller '%s' not recognized\n",
- longname, gadget->name);
- device_desc.bcdDevice = cpu_to_le16(0x9999);
- }
+ usb_composite_overwrite_options(cdev, &coverwrite);
INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
- snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- init_utsname()->sysname, init_utsname()->release,
- gadget->name);
-
return 0;
}
@@ -334,11 +292,12 @@
return 0;
}
-static struct usb_composite_driver zero_driver = {
+static __refdata struct usb_composite_driver zero_driver = {
.name = "zero",
.dev = &device_desc,
.strings = dev_strings,
.max_speed = USB_SPEED_SUPER,
+ .bind = zero_bind,
.unbind = zero_unbind,
.suspend = zero_suspend,
.resume = zero_resume,
@@ -349,7 +308,7 @@
static int __init init(void)
{
- return usb_composite_probe(&zero_driver, zero_bind);
+ return usb_composite_probe(&zero_driver);
}
module_init(init);
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 075d2ec..13cd6d5 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -292,7 +292,7 @@
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
select USB_OTG_UTILS if ARCH_OMAP
- select USB_ISP1301 if ARCH_LPC32XX || ARCH_PNX4008
+ depends on USB_ISP1301 || !(ARCH_LPC32XX || ARCH_PNX4008)
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
@@ -418,7 +418,7 @@
default n
---help---
Adds an OHCI host driver for a generic platform device, which
- provieds a memory space and an irq.
+ provides a memory space and an irq.
If unsure, say N.
@@ -428,7 +428,7 @@
default n
---help---
Adds an EHCI host driver for a generic platform device, which
- provieds a memory space and an irq.
+ provides a memory space and an irq.
If unsure, say N.
@@ -450,7 +450,7 @@
config USB_UHCI_HCD
tristate "UHCI HCD (most Intel and VIA) support"
- depends on USB && (PCI || SPARC_LEON)
+ depends on USB && (PCI || SPARC_LEON || ARCH_VT8500)
---help---
The Universal Host Controller Interface is a standard by Intel for
accessing the USB hardware in the PC (which is also called the USB
@@ -468,7 +468,15 @@
config USB_UHCI_SUPPORT_NON_PCI_HC
bool
depends on USB_UHCI_HCD
- default y if SPARC_LEON
+ default y if (SPARC_LEON || ARCH_VT8500)
+
+config USB_UHCI_PLATFORM
+ bool "Generic UHCI Platform Driver support"
+ depends on USB_UHCI_SUPPORT_NON_PCI_HC
+ default y if ARCH_VT8500
+ ---help---
+ Enable support for generic UHCI platform devices that require no
+ additional configuration.
config USB_UHCI_BIG_ENDIAN_MMIO
bool
diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index a47e2cf..411bb74 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -150,31 +150,24 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- retval = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto fail_ioremap;
+ goto fail_request_resource;
}
- iclk = clk_get(&pdev->dev, "ehci_clk");
+ iclk = devm_clk_get(&pdev->dev, "ehci_clk");
if (IS_ERR(iclk)) {
dev_err(&pdev->dev, "Error getting interface clock\n");
retval = -ENOENT;
- goto fail_get_iclk;
+ goto fail_request_resource;
}
- fclk = clk_get(&pdev->dev, "uhpck");
+ fclk = devm_clk_get(&pdev->dev, "uhpck");
if (IS_ERR(fclk)) {
dev_err(&pdev->dev, "Error getting function clock\n");
retval = -ENOENT;
- goto fail_get_fclk;
+ goto fail_request_resource;
}
atmel_start_ehci(pdev);
@@ -187,13 +180,6 @@
fail_add_hcd:
atmel_stop_ehci(pdev);
- clk_put(fclk);
-fail_get_fclk:
- clk_put(iclk);
-fail_get_iclk:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -209,13 +195,9 @@
ehci_shutdown(hcd);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
atmel_stop_ehci(pdev);
- clk_put(fclk);
- clk_put(iclk);
fclk = iclk = NULL;
return 0;
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index cba10d6..65c945e 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -98,23 +98,17 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
- pr_debug("ioremap failed");
+ pr_debug("devm_request_and_ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
if (alchemy_usb_control(ALCHEMY_USB_EHCI0, 1)) {
printk(KERN_INFO "%s: controller init failed!\n", pdev->name);
ret = -ENODEV;
- goto err3;
+ goto err1;
}
ret = usb_add_hcd(hcd, pdev->resource[1].start,
@@ -125,10 +119,6 @@
}
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
-err3:
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -140,8 +130,6 @@
usb_remove_hcd(hcd);
alchemy_usb_control(ALCHEMY_USB_EHCI0, 0);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/usb/host/ehci-cns3xxx.c b/drivers/usb/host/ehci-cns3xxx.c
index caaa3e5..d91708d 100644
--- a/drivers/usb/host/ehci-cns3xxx.c
+++ b/drivers/usb/host/ehci-cns3xxx.c
@@ -105,27 +105,17 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(dev, "controller already in use\n");
- retval = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(dev, "error mapping memory\n");
retval = -EFAULT;
- goto err2;
+ goto err1;
}
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval == 0)
return retval;
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
@@ -137,8 +127,6 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
/*
* EHCI and OHCI share the same clock and power,
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index b7451b2..11ff4b4 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -210,11 +210,11 @@
usb_put_hcd(hcd);
}
-static void ehci_fsl_setup_phy(struct usb_hcd *hcd,
+static int ehci_fsl_setup_phy(struct usb_hcd *hcd,
enum fsl_usb2_phy_modes phy_mode,
unsigned int port_offset)
{
- u32 portsc, temp;
+ u32 portsc;
struct ehci_hcd *ehci = hcd_to_ehci(hcd);
void __iomem *non_ehci = hcd->regs;
struct device *dev = hcd->self.controller;
@@ -232,9 +232,15 @@
case FSL_USB2_PHY_ULPI:
if (pdata->controller_ver) {
/* controller version 1.6 or above */
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp |
- USB_CTRL_USB_EN | ULPI_PHY_CLK_SEL);
+ setbits32(non_ehci + FSL_SOC_USB_CTRL,
+ ULPI_PHY_CLK_SEL);
+ /*
+ * Due to controller issue of PHY_CLK_VALID in ULPI
+ * mode, we set USB_CTRL_USB_EN before checking
+ * PHY_CLK_VALID, otherwise PHY_CLK_VALID doesn't work.
+ */
+ clrsetbits_be32(non_ehci + FSL_SOC_USB_CTRL,
+ UTMI_PHY_EN, USB_CTRL_USB_EN);
}
portsc |= PORT_PTS_ULPI;
break;
@@ -247,9 +253,7 @@
case FSL_USB2_PHY_UTMI:
if (pdata->controller_ver) {
/* controller version 1.6 or above */
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp |
- UTMI_PHY_EN | USB_CTRL_USB_EN);
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, UTMI_PHY_EN);
mdelay(FSL_UTMI_PHY_DLY); /* Delay for UTMI PHY CLK to
become stable - 10ms*/
}
@@ -262,23 +266,34 @@
case FSL_USB2_PHY_NONE:
break;
}
+
+ if ((pdata->controller_ver) && ((phy_mode == FSL_USB2_PHY_ULPI) ||
+ (phy_mode == FSL_USB2_PHY_UTMI))) {
+ /* check PHY_CLK_VALID to get phy clk valid */
+ if (!spin_event_timeout(in_be32(non_ehci + FSL_SOC_USB_CTRL) &
+ PHY_CLK_VALID, FSL_USB_PHY_CLK_TIMEOUT, 0)) {
+ printk(KERN_WARNING "fsl-ehci: USB PHY clock invalid\n");
+ return -EINVAL;
+ }
+ }
+
ehci_writel(ehci, portsc, &ehci->regs->port_status[port_offset]);
+
+ if (phy_mode != FSL_USB2_PHY_ULPI)
+ setbits32(non_ehci + FSL_SOC_USB_CTRL, USB_CTRL_USB_EN);
+
+ return 0;
}
-static void ehci_fsl_usb_setup(struct ehci_hcd *ehci)
+static int ehci_fsl_usb_setup(struct ehci_hcd *ehci)
{
struct usb_hcd *hcd = ehci_to_hcd(ehci);
struct fsl_usb2_platform_data *pdata;
void __iomem *non_ehci = hcd->regs;
- u32 temp;
pdata = hcd->self.controller->platform_data;
- /* Enable PHY interface in the control reg. */
if (pdata->have_sysif_regs) {
- temp = in_be32(non_ehci + FSL_SOC_USB_CTRL);
- out_be32(non_ehci + FSL_SOC_USB_CTRL, temp | 0x00000004);
-
/*
* Turn on cache snooping hardware, since some PowerPC platforms
* wholly rely on hardware to deal with cache coherent
@@ -293,7 +308,8 @@
if ((pdata->operating_mode == FSL_USB2_DR_HOST) ||
(pdata->operating_mode == FSL_USB2_DR_OTG))
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
if (pdata->operating_mode == FSL_USB2_MPH_HOST) {
unsigned int chip, rev, svr;
@@ -307,9 +323,12 @@
ehci->has_fsl_port_bug = 1;
if (pdata->port_enables & FSL_USB2_PORT0_ENABLED)
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 0))
+ return -EINVAL;
+
if (pdata->port_enables & FSL_USB2_PORT1_ENABLED)
- ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1);
+ if (ehci_fsl_setup_phy(hcd, pdata->phy_mode, 1))
+ return -EINVAL;
}
if (pdata->have_sysif_regs) {
@@ -322,12 +341,15 @@
#endif
out_be32(non_ehci + FSL_SOC_USB_SICTRL, 0x00000001);
}
+
+ return 0;
}
/* called after powerup, by probe or system-pm "wakeup" */
static int ehci_fsl_reinit(struct ehci_hcd *ehci)
{
- ehci_fsl_usb_setup(ehci);
+ if (ehci_fsl_usb_setup(ehci))
+ return -EINVAL;
ehci_port_power(ehci, 0);
return 0;
diff --git a/drivers/usb/host/ehci-fsl.h b/drivers/usb/host/ehci-fsl.h
index 8840368..dbd292e 100644
--- a/drivers/usb/host/ehci-fsl.h
+++ b/drivers/usb/host/ehci-fsl.h
@@ -61,4 +61,5 @@
#define PLL_RESET (1<<8)
#define UTMI_PHY_EN (1<<9)
#define ULPI_PHY_CLK_SEL (1<<10)
+#define PHY_CLK_VALID (1<<17)
#endif /* _EHCI_FSL_H */
diff --git a/drivers/usb/host/ehci-grlib.c b/drivers/usb/host/ehci-grlib.c
index 22ca45c..3180cb3 100644
--- a/drivers/usb/host/ehci-grlib.c
+++ b/drivers/usb/host/ehci-grlib.c
@@ -127,12 +127,6 @@
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -140,9 +134,9 @@
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
goto err_ioremap;
}
@@ -161,17 +155,13 @@
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
- goto err_ehci;
+ goto err_ioremap;
return 0;
-err_ehci:
- iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -188,9 +178,7 @@
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
irq_dispose_mapping(hcd->irq);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-ixp4xx.c b/drivers/usb/host/ehci-ixp4xx.c
index 488d401..f224c0a 100644
--- a/drivers/usb/host/ehci-ixp4xx.c
+++ b/drivers/usb/host/ehci-ixp4xx.c
@@ -98,30 +98,19 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- retval = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
retval = -EFAULT;
- goto fail_ioremap;
+ goto fail_request_resource;
}
retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval)
- goto fail_add_hcd;
+ goto fail_request_resource;
return retval;
-fail_add_hcd:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -134,8 +123,6 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-ls1x.c b/drivers/usb/host/ehci-ls1x.c
index a283e59..ca75965 100644
--- a/drivers/usb/host/ehci-ls1x.c
+++ b/drivers/usb/host/ehci-ls1x.c
@@ -106,29 +106,19 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto err_put_hcd;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -EFAULT;
- goto err_release_region;
+ goto err_put_hcd;
}
- ret = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ ret = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (ret)
- goto err_iounmap;
+ goto err_put_hcd;
return ret;
-err_iounmap:
- iounmap(hcd->regs);
-err_release_region:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
return ret;
@@ -139,8 +129,6 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-msm.c b/drivers/usb/host/ehci-msm.c
index 17dd9e9..4af4dc5 100644
--- a/drivers/usb/host/ehci-msm.c
+++ b/drivers/usb/host/ehci-msm.c
@@ -133,7 +133,7 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_ioremap(&pdev->dev, hcd->rsrc_start, hcd->rsrc_len);
if (!hcd->regs) {
dev_err(&pdev->dev, "ioremap failed\n");
ret = -ENOMEM;
@@ -145,17 +145,17 @@
* powering up VBUS, mapping of registers address space and power
* management.
*/
- phy = usb_get_phy(USB_PHY_TYPE_USB2);
+ phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(phy)) {
dev_err(&pdev->dev, "unable to find transceiver\n");
ret = -ENODEV;
- goto unmap;
+ goto put_hcd;
}
ret = otg_set_host(phy->otg, &hcd->self);
if (ret < 0) {
dev_err(&pdev->dev, "unable to register with transceiver\n");
- goto put_transceiver;
+ goto put_hcd;
}
device_init_wakeup(&pdev->dev, 1);
@@ -168,10 +168,6 @@
return 0;
-put_transceiver:
- usb_put_phy(phy);
-unmap:
- iounmap(hcd->regs);
put_hcd:
usb_put_hcd(hcd);
@@ -187,7 +183,6 @@
pm_runtime_set_suspended(&pdev->dev);
otg_set_host(phy->otg, NULL);
- usb_put_phy(phy);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-mv.c b/drivers/usb/host/ehci-mv.c
index f6df1cc..f7bfc0b 100644
--- a/drivers/usb/host/ehci-mv.c
+++ b/drivers/usb/host/ehci-mv.c
@@ -161,7 +161,7 @@
return -ENOMEM;
size = sizeof(*ehci_mv) + sizeof(struct clk *) * pdata->clknum;
- ehci_mv = kzalloc(size, GFP_KERNEL);
+ ehci_mv = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (ehci_mv == NULL) {
dev_err(&pdev->dev, "cannot allocate ehci_hcd_mv\n");
retval = -ENOMEM;
@@ -175,12 +175,12 @@
ehci_mv->clknum = pdata->clknum;
for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++) {
ehci_mv->clk[clk_i] =
- clk_get(&pdev->dev, pdata->clkname[clk_i]);
+ devm_clk_get(&pdev->dev, pdata->clkname[clk_i]);
if (IS_ERR(ehci_mv->clk[clk_i])) {
dev_err(&pdev->dev, "error get clck \"%s\"\n",
pdata->clkname[clk_i]);
retval = PTR_ERR(ehci_mv->clk[clk_i]);
- goto err_put_clk;
+ goto err_clear_drvdata;
}
}
@@ -188,34 +188,36 @@
if (r == NULL) {
dev_err(&pdev->dev, "no phy I/O memory resource defined\n");
retval = -ENODEV;
- goto err_put_clk;
+ goto err_clear_drvdata;
}
- ehci_mv->phy_regs = ioremap(r->start, resource_size(r));
+ ehci_mv->phy_regs = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
if (ehci_mv->phy_regs == 0) {
dev_err(&pdev->dev, "failed to map phy I/O memory\n");
retval = -EFAULT;
- goto err_put_clk;
+ goto err_clear_drvdata;
}
r = platform_get_resource_byname(pdev, IORESOURCE_MEM, "capregs");
if (!r) {
dev_err(&pdev->dev, "no I/O memory resource defined\n");
retval = -ENODEV;
- goto err_iounmap_phyreg;
+ goto err_clear_drvdata;
}
- ehci_mv->cap_regs = ioremap(r->start, resource_size(r));
+ ehci_mv->cap_regs = devm_ioremap(&pdev->dev, r->start,
+ resource_size(r));
if (ehci_mv->cap_regs == NULL) {
dev_err(&pdev->dev, "failed to map I/O memory\n");
retval = -EFAULT;
- goto err_iounmap_phyreg;
+ goto err_clear_drvdata;
}
retval = mv_ehci_enable(ehci_mv);
if (retval) {
dev_err(&pdev->dev, "init phy error %d\n", retval);
- goto err_iounmap_capreg;
+ goto err_clear_drvdata;
}
offset = readl(ehci_mv->cap_regs) & CAPLENGTH_MASK;
@@ -239,7 +241,7 @@
ehci_mv->mode = pdata->mode;
if (ehci_mv->mode == MV_USB_MODE_OTG) {
#ifdef CONFIG_USB_OTG_UTILS
- ehci_mv->otg = usb_get_phy(USB_PHY_TYPE_USB2);
+ ehci_mv->otg = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (IS_ERR_OR_NULL(ehci_mv->otg)) {
dev_err(&pdev->dev,
"unable to find transceiver\n");
@@ -252,7 +254,7 @@
dev_err(&pdev->dev,
"unable to register with transceiver\n");
retval = -ENODEV;
- goto err_put_transceiver;
+ goto err_disable_clk;
}
/* otg will enable clock before use as host */
mv_ehci_disable(ehci_mv);
@@ -286,22 +288,10 @@
err_set_vbus:
if (pdata->set_vbus)
pdata->set_vbus(0);
-#ifdef CONFIG_USB_OTG_UTILS
-err_put_transceiver:
- if (!IS_ERR_OR_NULL(ehci_mv->otg))
- usb_put_phy(ehci_mv->otg);
-#endif
err_disable_clk:
mv_ehci_disable(ehci_mv);
-err_iounmap_capreg:
- iounmap(ehci_mv->cap_regs);
-err_iounmap_phyreg:
- iounmap(ehci_mv->phy_regs);
-err_put_clk:
- for (clk_i--; clk_i >= 0; clk_i--)
- clk_put(ehci_mv->clk[clk_i]);
+err_clear_drvdata:
platform_set_drvdata(pdev, NULL);
- kfree(ehci_mv);
err_put_hcd:
usb_put_hcd(hcd);
@@ -317,10 +307,8 @@
if (hcd->rh_registered)
usb_remove_hcd(hcd);
- if (!IS_ERR_OR_NULL(ehci_mv->otg)) {
+ if (!IS_ERR_OR_NULL(ehci_mv->otg))
otg_set_host(ehci_mv->otg->otg, NULL);
- usb_put_phy(ehci_mv->otg);
- }
if (ehci_mv->mode == MV_USB_MODE_HOST) {
if (ehci_mv->pdata->set_vbus)
@@ -329,15 +317,8 @@
mv_ehci_disable(ehci_mv);
}
- iounmap(ehci_mv->cap_regs);
- iounmap(ehci_mv->phy_regs);
-
- for (clk_i = 0; clk_i < ehci_mv->clknum; clk_i++)
- clk_put(ehci_mv->clk[clk_i]);
-
platform_set_drvdata(pdev, NULL);
- kfree(ehci_mv);
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/ehci-mxc.c b/drivers/usb/host/ehci-mxc.c
index 3420137..959e1a4 100644
--- a/drivers/usb/host/ehci-mxc.c
+++ b/drivers/usb/host/ehci-mxc.c
@@ -121,7 +121,7 @@
if (!hcd)
return -ENOMEM;
- priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto err_alloc;
@@ -131,34 +131,28 @@
if (!res) {
dev_err(dev, "Found HC with no register addr. Check setup!\n");
ret = -ENODEV;
- goto err_get_resource;
+ goto err_alloc;
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- dev_dbg(dev, "controller already in use\n");
- ret = -EBUSY;
- goto err_request_mem;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
dev_err(dev, "error mapping memory\n");
ret = -EFAULT;
- goto err_ioremap;
+ goto err_alloc;
}
/* enable clocks */
- priv->usbclk = clk_get(dev, "ipg");
+ priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
if (IS_ERR(priv->usbclk)) {
ret = PTR_ERR(priv->usbclk);
- goto err_clk;
+ goto err_alloc;
}
clk_prepare_enable(priv->usbclk);
- priv->ahbclk = clk_get(dev, "ahb");
+ priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
if (IS_ERR(priv->ahbclk)) {
ret = PTR_ERR(priv->ahbclk);
goto err_clk_ahb;
@@ -166,7 +160,7 @@
clk_prepare_enable(priv->ahbclk);
/* "dr" device has its own clock on i.MX51 */
- priv->phyclk = clk_get(dev, "phy");
+ priv->phyclk = devm_clk_get(&pdev->dev, "phy");
if (IS_ERR(priv->phyclk))
priv->phyclk = NULL;
if (priv->phyclk)
@@ -245,23 +239,12 @@
if (pdata && pdata->exit)
pdata->exit(pdev);
err_init:
- if (priv->phyclk) {
+ if (priv->phyclk)
clk_disable_unprepare(priv->phyclk);
- clk_put(priv->phyclk);
- }
clk_disable_unprepare(priv->ahbclk);
- clk_put(priv->ahbclk);
err_clk_ahb:
clk_disable_unprepare(priv->usbclk);
- clk_put(priv->usbclk);
-err_clk:
- iounmap(hcd->regs);
-err_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_request_mem:
-err_get_resource:
- kfree(priv);
err_alloc:
usb_put_hcd(hcd);
return ret;
@@ -280,22 +263,14 @@
usb_phy_shutdown(pdata->otg);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
clk_disable_unprepare(priv->usbclk);
- clk_put(priv->usbclk);
clk_disable_unprepare(priv->ahbclk);
- clk_put(priv->ahbclk);
- if (priv->phyclk) {
+ if (priv->phyclk)
clk_disable_unprepare(priv->phyclk);
- clk_put(priv->phyclk);
- }
-
- kfree(priv);
return 0;
}
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index bb55eb4..d7fe287 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -56,15 +56,6 @@
#define EHCI_INSNREG05_ULPI_EXTREGADD_SHIFT 8
#define EHCI_INSNREG05_ULPI_WRDATA_SHIFT 0
-/* Errata i693 */
-static struct clk *utmi_p1_fck;
-static struct clk *utmi_p2_fck;
-static struct clk *xclk60mhsp1_ck;
-static struct clk *xclk60mhsp2_ck;
-static struct clk *usbhost_p1_fck;
-static struct clk *usbhost_p2_fck;
-static struct clk *init_60m_fclk;
-
/*-------------------------------------------------------------------------*/
static const struct hc_driver ehci_omap_hc_driver;
@@ -80,40 +71,6 @@
return __raw_readl(base + reg);
}
-/* Erratum i693 workaround sequence */
-static void omap_ehci_erratum_i693(struct ehci_hcd *ehci)
-{
- int ret = 0;
-
- /* Switch to the internal 60 MHz clock */
- ret = clk_set_parent(utmi_p1_fck, init_60m_fclk);
- if (ret != 0)
- ehci_err(ehci, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
-
- ret = clk_set_parent(utmi_p2_fck, init_60m_fclk);
- if (ret != 0)
- ehci_err(ehci, "init_60m_fclk set parent"
- "failed error:%d\n", ret);
-
- clk_enable(usbhost_p1_fck);
- clk_enable(usbhost_p2_fck);
-
- /* Wait 1ms and switch back to the external clock */
- mdelay(1);
- ret = clk_set_parent(utmi_p1_fck, xclk60mhsp1_ck);
- if (ret != 0)
- ehci_err(ehci, "xclk60mhsp1_ck set parent"
- "failed error:%d\n", ret);
-
- ret = clk_set_parent(utmi_p2_fck, xclk60mhsp2_ck);
- if (ret != 0)
- ehci_err(ehci, "xclk60mhsp2_ck set parent"
- "failed error:%d\n", ret);
-
- clk_disable(usbhost_p1_fck);
- clk_disable(usbhost_p2_fck);
-}
static void omap_ehci_soft_phy_reset(struct usb_hcd *hcd, u8 port)
{
@@ -195,50 +152,6 @@
return rc;
}
-static int omap_ehci_hub_control(
- struct usb_hcd *hcd,
- u16 typeReq,
- u16 wValue,
- u16 wIndex,
- char *buf,
- u16 wLength
-)
-{
- struct ehci_hcd *ehci = hcd_to_ehci(hcd);
- u32 __iomem *status_reg = &ehci->regs->port_status[
- (wIndex & 0xff) - 1];
- u32 temp;
- unsigned long flags;
- int retval = 0;
-
- spin_lock_irqsave(&ehci->lock, flags);
-
- if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
- temp = ehci_readl(ehci, status_reg);
- if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
- retval = -EPIPE;
- goto done;
- }
-
- temp &= ~PORT_WKCONN_E;
- temp |= PORT_WKDISC_E | PORT_WKOC_E;
- ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
-
- omap_ehci_erratum_i693(ehci);
-
- set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
- goto done;
- }
-
- spin_unlock_irqrestore(&ehci->lock, flags);
-
- /* Handle the hub control events here */
- return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
-done:
- spin_unlock_irqrestore(&ehci->lock, flags);
- return retval;
-}
-
static void disable_put_regulator(
struct ehci_hcd_omap_platform_data *pdata)
{
@@ -351,79 +264,9 @@
goto err_pm_runtime;
}
- /* get clocks */
- utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk");
- if (IS_ERR(utmi_p1_fck)) {
- ret = PTR_ERR(utmi_p1_fck);
- dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret);
- goto err_add_hcd;
- }
-
- xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck");
- if (IS_ERR(xclk60mhsp1_ck)) {
- ret = PTR_ERR(xclk60mhsp1_ck);
- dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret);
- goto err_utmi_p1_fck;
- }
-
- utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk");
- if (IS_ERR(utmi_p2_fck)) {
- ret = PTR_ERR(utmi_p2_fck);
- dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret);
- goto err_xclk60mhsp1_ck;
- }
-
- xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck");
- if (IS_ERR(xclk60mhsp2_ck)) {
- ret = PTR_ERR(xclk60mhsp2_ck);
- dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret);
- goto err_utmi_p2_fck;
- }
-
- usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk");
- if (IS_ERR(usbhost_p1_fck)) {
- ret = PTR_ERR(usbhost_p1_fck);
- dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret);
- goto err_xclk60mhsp2_ck;
- }
-
- usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk");
- if (IS_ERR(usbhost_p2_fck)) {
- ret = PTR_ERR(usbhost_p2_fck);
- dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret);
- goto err_usbhost_p1_fck;
- }
-
- init_60m_fclk = clk_get(dev, "init_60m_fclk");
- if (IS_ERR(init_60m_fclk)) {
- ret = PTR_ERR(init_60m_fclk);
- dev_err(dev, "init_60m_fclk failed error:%d\n", ret);
- goto err_usbhost_p2_fck;
- }
return 0;
-err_usbhost_p2_fck:
- clk_put(usbhost_p2_fck);
-
-err_usbhost_p1_fck:
- clk_put(usbhost_p1_fck);
-
-err_xclk60mhsp2_ck:
- clk_put(xclk60mhsp2_ck);
-
-err_utmi_p2_fck:
- clk_put(utmi_p2_fck);
-
-err_xclk60mhsp1_ck:
- clk_put(xclk60mhsp1_ck);
-
-err_utmi_p1_fck:
- clk_put(utmi_p1_fck);
-
-err_add_hcd:
- usb_remove_hcd(hcd);
-
err_pm_runtime:
disable_put_regulator(pdata);
pm_runtime_put_sync(dev);
@@ -454,14 +297,6 @@
iounmap(hcd->regs);
usb_put_hcd(hcd);
- clk_put(utmi_p1_fck);
- clk_put(utmi_p2_fck);
- clk_put(xclk60mhsp1_ck);
- clk_put(xclk60mhsp2_ck);
- clk_put(usbhost_p1_fck);
- clk_put(usbhost_p2_fck);
- clk_put(init_60m_fclk);
-
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
@@ -532,7 +367,7 @@
* root hub support
*/
.hub_status_data = ehci_hub_status_data,
- .hub_control = omap_ehci_hub_control,
+ .hub_control = ehci_hub_control,
.bus_suspend = ehci_bus_suspend,
.bus_resume = ehci_bus_resume,
diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
index 4b1d896..764e010 100644
--- a/drivers/usb/host/ehci-platform.c
+++ b/drivers/usb/host/ehci-platform.c
@@ -82,10 +82,14 @@
{
struct usb_hcd *hcd;
struct resource *res_mem;
+ struct usb_ehci_pdata *pdata = dev->dev.platform_data;
int irq;
int err = -ENOMEM;
- BUG_ON(!dev->dev.platform_data);
+ if (!pdata) {
+ WARN_ON(1);
+ return -ENODEV;
+ }
if (usb_disabled())
return -ENODEV;
@@ -101,10 +105,18 @@
return -ENXIO;
}
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ return err;
+ }
+
hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
- if (!hcd)
- return -ENOMEM;
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err_power;
+ }
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
@@ -116,8 +128,10 @@
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
+ if (!hcd->regs) {
+ err = -ENOMEM;
goto err_release_region;
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_iounmap;
@@ -132,12 +146,17 @@
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return err;
}
static int __devexit ehci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->dev.platform_data;
usb_remove_hcd(hcd);
iounmap(hcd->regs);
@@ -145,6 +164,9 @@
usb_put_hcd(hcd);
platform_set_drvdata(dev, NULL);
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return 0;
}
@@ -153,14 +175,32 @@
static int ehci_platform_suspend(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
bool do_wakeup = device_may_wakeup(dev);
+ int ret;
- return ehci_suspend(hcd, do_wakeup);
+ ret = ehci_suspend(hcd, do_wakeup);
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
+ return ret;
}
static int ehci_platform_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ehci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
ehci_resume(hcd, false);
return 0;
diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c
index bbbe89d..fa937d0 100644
--- a/drivers/usb/host/ehci-ppc-of.c
+++ b/drivers/usb/host/ehci-ppc-of.c
@@ -114,12 +114,6 @@
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (irq == NO_IRQ) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -127,9 +121,9 @@
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
goto err_ioremap;
}
@@ -139,8 +133,10 @@
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);
+ ehci->ohci_hcctrl_reg =
+ devm_ioremap(&op->dev,
+ res.start + OHCI_HCCTRL_OFFSET,
+ OHCI_HCCTRL_LEN);
else
pr_debug("%s: no ohci offset in fdt\n", __FILE__);
if (!ehci->ohci_hcctrl_reg) {
@@ -169,19 +165,13 @@
rv = usb_add_hcd(hcd, irq, 0);
if (rv)
- goto err_ehci;
+ goto err_ioremap;
return 0;
-err_ehci:
- if (ehci->has_amcc_usb23)
- iounmap(ehci->ohci_hcctrl_reg);
- iounmap(hcd->regs);
err_ioremap:
irq_dispose_mapping(irq);
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -202,9 +192,7 @@
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
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.
@@ -222,8 +210,6 @@
pr_debug("%s: no ohci offset in fdt\n", __FILE__);
of_node_put(np);
}
-
- iounmap(ehci->ohci_hcctrl_reg);
}
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-s5p.c b/drivers/usb/host/ehci-s5p.c
index 9d8f1dd..d055503 100644
--- a/drivers/usb/host/ehci-s5p.c
+++ b/drivers/usb/host/ehci-s5p.c
@@ -128,7 +128,7 @@
}
s5p_ehci->hcd = hcd;
- s5p_ehci->clk = clk_get(&pdev->dev, "usbhost");
+ s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
if (IS_ERR(s5p_ehci->clk)) {
dev_err(&pdev->dev, "Failed to get usbhost clock\n");
@@ -138,7 +138,7 @@
err = clk_enable(s5p_ehci->clk);
if (err)
- goto fail_clken;
+ goto fail_clk;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
@@ -184,8 +184,6 @@
fail_io:
clk_disable(s5p_ehci->clk);
-fail_clken:
- clk_put(s5p_ehci->clk);
fail_clk:
usb_put_hcd(hcd);
return err;
@@ -203,7 +201,6 @@
pdata->phy_exit(pdev, S5P_USB_PHY_HOST);
clk_disable(s5p_ehci->clk);
- clk_put(s5p_ehci->clk);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ehci-sead3.c b/drivers/usb/host/ehci-sead3.c
index 58c96bd..efad02d 100644
--- a/drivers/usb/host/ehci-sead3.c
+++ b/drivers/usb/host/ehci-sead3.c
@@ -40,7 +40,7 @@
ehci->need_io_watchdog = 0;
/* Set burst length to 16 words. */
- ehci_writel(ehci, 0x1010, &ehci->regs->reserved[1]);
+ ehci_writel(ehci, 0x1010, &ehci->regs->reserved1[1]);
return ret;
}
@@ -112,17 +112,11 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
pr_debug("ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
/* Root hub has integrated TT. */
@@ -135,9 +129,6 @@
return ret;
}
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -148,8 +139,6 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
diff --git a/drivers/usb/host/ehci-sh.c b/drivers/usb/host/ehci-sh.c
index b3f1e36..6081e1e 100644
--- a/drivers/usb/host/ehci-sh.c
+++ b/drivers/usb/host/ehci-sh.c
@@ -125,33 +125,27 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
- driver->description)) {
- dev_dbg(&pdev->dev, "controller already in use\n");
- ret = -EBUSY;
- goto fail_request_resource;
- }
-
- hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (hcd->regs == NULL) {
dev_dbg(&pdev->dev, "error mapping memory\n");
ret = -ENXIO;
- goto fail_ioremap;
+ goto fail_request_resource;
}
- priv = kmalloc(sizeof(struct ehci_sh_priv), GFP_KERNEL);
+ priv = devm_kzalloc(&pdev->dev, sizeof(struct ehci_sh_priv),
+ GFP_KERNEL);
if (!priv) {
dev_dbg(&pdev->dev, "error allocating priv data\n");
ret = -ENOMEM;
- goto fail_alloc;
+ goto fail_request_resource;
}
/* These are optional, we don't care if they fail */
- priv->fclk = clk_get(&pdev->dev, "usb_fck");
+ priv->fclk = devm_clk_get(&pdev->dev, "usb_fck");
if (IS_ERR(priv->fclk))
priv->fclk = NULL;
- priv->iclk = clk_get(&pdev->dev, "usb_ick");
+ priv->iclk = devm_clk_get(&pdev->dev, "usb_ick");
if (IS_ERR(priv->iclk))
priv->iclk = NULL;
@@ -176,14 +170,6 @@
clk_disable(priv->iclk);
clk_disable(priv->fclk);
- clk_put(priv->iclk);
- clk_put(priv->fclk);
-
- kfree(priv);
-fail_alloc:
- iounmap(hcd->regs);
-fail_ioremap:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
fail_request_resource:
usb_put_hcd(hcd);
fail_create_hcd:
@@ -198,19 +184,12 @@
struct usb_hcd *hcd = priv->hcd;
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
clk_disable(priv->fclk);
clk_disable(priv->iclk);
- clk_put(priv->fclk);
- clk_put(priv->iclk);
-
- kfree(priv);
-
return 0;
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 65408f7..6223d175 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -634,7 +634,8 @@
setup_vbus_gpio(pdev, pdata);
- tegra = kzalloc(sizeof(struct tegra_ehci_hcd), GFP_KERNEL);
+ tegra = devm_kzalloc(&pdev->dev, sizeof(struct tegra_ehci_hcd),
+ GFP_KERNEL);
if (!tegra)
return -ENOMEM;
@@ -642,13 +643,12 @@
dev_name(&pdev->dev));
if (!hcd) {
dev_err(&pdev->dev, "Unable to create HCD\n");
- err = -ENOMEM;
- goto fail_hcd;
+ return -ENOMEM;
}
platform_set_drvdata(pdev, tegra);
- tegra->clk = clk_get(&pdev->dev, NULL);
+ tegra->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(tegra->clk)) {
dev_err(&pdev->dev, "Can't get ehci clock\n");
err = PTR_ERR(tegra->clk);
@@ -657,9 +657,9 @@
err = clk_prepare_enable(tegra->clk);
if (err)
- goto fail_clken;
+ goto fail_clk;
- tegra->emc_clk = clk_get(&pdev->dev, "emc");
+ tegra->emc_clk = devm_clk_get(&pdev->dev, "emc");
if (IS_ERR(tegra->emc_clk)) {
dev_err(&pdev->dev, "Can't get emc clock\n");
err = PTR_ERR(tegra->emc_clk);
@@ -677,7 +677,7 @@
}
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- hcd->regs = ioremap(res->start, resource_size(res));
+ hcd->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!hcd->regs) {
dev_err(&pdev->dev, "Failed to remap I/O memory\n");
err = -ENOMEM;
@@ -702,7 +702,7 @@
default:
err = -ENODEV;
dev_err(&pdev->dev, "unknown usb instance\n");
- goto fail_phy;
+ goto fail_io;
}
}
@@ -712,7 +712,7 @@
if (IS_ERR(tegra->phy)) {
dev_err(&pdev->dev, "Failed to open USB phy\n");
err = -ENXIO;
- goto fail_phy;
+ goto fail_io;
}
usb_phy_init(&tegra->phy->u_phy);
@@ -735,7 +735,8 @@
#ifdef CONFIG_USB_OTG_UTILS
if (pdata->operating_mode == TEGRA_USB_OTG) {
- tegra->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ tegra->transceiver =
+ devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, &hcd->self);
}
@@ -759,25 +760,16 @@
fail:
#ifdef CONFIG_USB_OTG_UTILS
- if (!IS_ERR_OR_NULL(tegra->transceiver)) {
+ if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, NULL);
- usb_put_phy(tegra->transceiver);
- }
#endif
usb_phy_shutdown(&tegra->phy->u_phy);
-fail_phy:
- iounmap(hcd->regs);
fail_io:
clk_disable_unprepare(tegra->emc_clk);
- clk_put(tegra->emc_clk);
fail_emc_clk:
clk_disable_unprepare(tegra->clk);
-fail_clken:
- clk_put(tegra->clk);
fail_clk:
usb_put_hcd(hcd);
-fail_hcd:
- kfree(tegra);
return err;
}
@@ -794,25 +786,19 @@
pm_runtime_put_noidle(&pdev->dev);
#ifdef CONFIG_USB_OTG_UTILS
- if (!IS_ERR_OR_NULL(tegra->transceiver)) {
+ if (!IS_ERR_OR_NULL(tegra->transceiver))
otg_set_host(tegra->transceiver->otg, NULL);
- usb_put_phy(tegra->transceiver);
- }
#endif
usb_remove_hcd(hcd);
usb_put_hcd(hcd);
usb_phy_shutdown(&tegra->phy->u_phy);
- iounmap(hcd->regs);
clk_disable_unprepare(tegra->clk);
- clk_put(tegra->clk);
clk_disable_unprepare(tegra->emc_clk);
- clk_put(tegra->emc_clk);
- kfree(tegra);
return 0;
}
diff --git a/drivers/usb/host/ehci-vt8500.c b/drivers/usb/host/ehci-vt8500.c
index 4d147c4..96722bf 100644
--- a/drivers/usb/host/ehci-vt8500.c
+++ b/drivers/usb/host/ehci-vt8500.c
@@ -16,6 +16,7 @@
*
*/
+#include <linux/of.h>
#include <linux/platform_device.h>
static int ehci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
@@ -106,17 +107,11 @@
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- pr_debug("request_mem_region failed");
- ret = -EBUSY;
- goto err1;
- }
-
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&pdev->dev, res);
if (!hcd->regs) {
pr_debug("ioremap failed");
ret = -ENOMEM;
- goto err2;
+ goto err1;
}
ehci = hcd_to_ehci(hcd);
@@ -129,9 +124,6 @@
return ret;
}
- iounmap(hcd->regs);
-err2:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
return ret;
@@ -142,14 +134,18 @@
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
platform_set_drvdata(pdev, NULL);
return 0;
}
+static const struct of_device_id vt8500_ehci_ids[] = {
+ { .compatible = "via,vt8500-ehci", },
+ { .compatible = "wm,prizm-ehci", },
+ {}
+};
+
static struct platform_driver vt8500_ehci_driver = {
.probe = vt8500_ehci_drv_probe,
.remove = vt8500_ehci_drv_remove,
@@ -157,7 +153,9 @@
.driver = {
.name = "vt8500-ehci",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(vt8500_ehci_ids),
}
};
MODULE_ALIAS("platform:vt8500-ehci");
+MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
diff --git a/drivers/usb/host/ehci-xilinx-of.c b/drivers/usb/host/ehci-xilinx-of.c
index 39f24fa..6a3f921a 100644
--- a/drivers/usb/host/ehci-xilinx-of.c
+++ b/drivers/usb/host/ehci-xilinx-of.c
@@ -152,12 +152,6 @@
hcd->rsrc_start = res.start;
hcd->rsrc_len = resource_size(&res);
- if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
- printk(KERN_ERR "%s: request_mem_region failed\n", __FILE__);
- rv = -EBUSY;
- goto err_rmr;
- }
-
irq = irq_of_parse_and_map(dn, 0);
if (!irq) {
printk(KERN_ERR "%s: irq_of_parse_and_map failed\n", __FILE__);
@@ -165,11 +159,11 @@
goto err_irq;
}
- hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ hcd->regs = devm_request_and_ioremap(&op->dev, &res);
if (!hcd->regs) {
- printk(KERN_ERR "%s: ioremap failed\n", __FILE__);
+ pr_err("%s: devm_request_and_ioremap failed\n", __FILE__);
rv = -ENOMEM;
- goto err_ioremap;
+ goto err_irq;
}
ehci = hcd_to_ehci(hcd);
@@ -200,12 +194,7 @@
if (rv == 0)
return 0;
- iounmap(hcd->regs);
-
-err_ioremap:
err_irq:
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-err_rmr:
usb_put_hcd(hcd);
return rv;
@@ -227,9 +216,6 @@
usb_remove_hcd(hcd);
- iounmap(hcd->regs);
- release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-
usb_put_hcd(hcd);
return 0;
diff --git a/drivers/usb/host/fhci-sched.c b/drivers/usb/host/fhci-sched.c
index 2dc8a40..8f18538 100644
--- a/drivers/usb/host/fhci-sched.c
+++ b/drivers/usb/host/fhci-sched.c
@@ -261,8 +261,7 @@
struct list_head *node = list->next;
if (!list_empty(list)) {
- list_del(node);
- list_add_tail(node, list);
+ list_move_tail(node, list);
}
}
diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c
index 2ed112d..2563263 100644
--- a/drivers/usb/host/isp1362-hcd.c
+++ b/drivers/usb/host/isp1362-hcd.c
@@ -543,12 +543,12 @@
usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid,
short_ok ? "" : "not_",
PTD_GET_COUNT(ptd), ep->maxpacket, len);
+ /* save the data underrun error code for later and
+ * proceed with the status stage
+ */
+ urb->actual_length += PTD_GET_COUNT(ptd);
if (usb_pipecontrol(urb->pipe)) {
ep->nextpid = USB_PID_ACK;
- /* save the data underrun error code for later and
- * proceed with the status stage
- */
- urb->actual_length += PTD_GET_COUNT(ptd);
BUG_ON(urb->actual_length > urb->transfer_buffer_length);
if (urb->status == -EINPROGRESS)
diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c
index a446386..c60066a 100644
--- a/drivers/usb/host/ohci-nxp.c
+++ b/drivers/usb/host/ohci-nxp.c
@@ -355,7 +355,7 @@
usb_otg_clk = clk_get(&pdev->dev, "ck_usb_otg");
if (IS_ERR(usb_otg_clk)) {
dev_err(&pdev->dev, "failed to acquire USB DEV Clock\n");
- ret = PTR_ERR(usb_dev_clk);
+ ret = PTR_ERR(usb_otg_clk);
goto out6;
}
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index e7d75d2..f8b2d91 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -403,8 +403,6 @@
static inline void
usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-
usb_remove_hcd(hcd);
if (!IS_ERR_OR_NULL(hcd->phy)) {
(void) otg_set_host(hcd->phy->otg, 0);
diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c
index 670c705..e24ec9f 100644
--- a/drivers/usb/host/ohci-platform.c
+++ b/drivers/usb/host/ohci-platform.c
@@ -83,10 +83,14 @@
{
struct usb_hcd *hcd;
struct resource *res_mem;
+ struct usb_ohci_pdata *pdata = dev->dev.platform_data;
int irq;
int err = -ENOMEM;
- BUG_ON(!dev->dev.platform_data);
+ if (!pdata) {
+ WARN_ON(1);
+ return -ENODEV;
+ }
if (usb_disabled())
return -ENODEV;
@@ -103,10 +107,18 @@
return -ENXIO;
}
+ if (pdata->power_on) {
+ err = pdata->power_on(dev);
+ if (err < 0)
+ return err;
+ }
+
hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
dev_name(&dev->dev));
- if (!hcd)
- return -ENOMEM;
+ if (!hcd) {
+ err = -ENOMEM;
+ goto err_power;
+ }
hcd->rsrc_start = res_mem->start;
hcd->rsrc_len = resource_size(res_mem);
@@ -118,8 +130,10 @@
}
hcd->regs = ioremap_nocache(hcd->rsrc_start, hcd->rsrc_len);
- if (!hcd->regs)
+ if (!hcd->regs) {
+ err = -ENOMEM;
goto err_release_region;
+ }
err = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (err)
goto err_iounmap;
@@ -134,12 +148,17 @@
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err_put_hcd:
usb_put_hcd(hcd);
+err_power:
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return err;
}
static int __devexit ohci_platform_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->dev.platform_data;
usb_remove_hcd(hcd);
iounmap(hcd->regs);
@@ -147,6 +166,9 @@
usb_put_hcd(hcd);
platform_set_drvdata(dev, NULL);
+ if (pdata->power_off)
+ pdata->power_off(dev);
+
return 0;
}
@@ -154,12 +176,28 @@
static int ohci_platform_suspend(struct device *dev)
{
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_suspend)
+ pdata->power_suspend(pdev);
+
return 0;
}
static int ohci_platform_resume(struct device *dev)
{
struct usb_hcd *hcd = dev_get_drvdata(dev);
+ struct usb_ohci_pdata *pdata = dev->platform_data;
+ struct platform_device *pdev =
+ container_of(dev, struct platform_device, dev);
+
+ if (pdata->power_on) {
+ int err = pdata->power_on(pdev);
+ if (err < 0)
+ return err;
+ }
ohci_finish_controller_resume(hcd);
return 0;
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index e1a3cc6..77f4402 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -23,6 +23,8 @@
#include <linux/signal.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
#include <mach/hardware.h>
#include <mach/ohci.h>
#include <mach/pxa3xx-u2d.h>
@@ -272,6 +274,67 @@
clk_disable_unprepare(ohci->clk);
}
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ohci_dt_ids[] = {
+ { .compatible = "marvell,pxa-ohci" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, pxa_ohci_dt_ids);
+
+static u64 pxa_ohci_dma_mask = DMA_BIT_MASK(32);
+
+static int __devinit ohci_pxa_of_init(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pxaohci_platform_data *pdata;
+ u32 tmp;
+
+ if (!np)
+ return 0;
+
+ /* Right now device-tree probed devices don't get dma_mask set.
+ * Since shared usb code relies on it, set it here for now.
+ * Once we have dma capability bindings this can go away.
+ */
+ if (!pdev->dev.dma_mask)
+ pdev->dev.dma_mask = &pxa_ohci_dma_mask;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "marvell,enable-port1", NULL))
+ pdata->flags |= ENABLE_PORT1;
+ if (of_get_property(np, "marvell,enable-port2", NULL))
+ pdata->flags |= ENABLE_PORT2;
+ if (of_get_property(np, "marvell,enable-port3", NULL))
+ pdata->flags |= ENABLE_PORT3;
+ if (of_get_property(np, "marvell,port-sense-low", NULL))
+ pdata->flags |= POWER_SENSE_LOW;
+ if (of_get_property(np, "marvell,power-control-low", NULL))
+ pdata->flags |= POWER_CONTROL_LOW;
+ if (of_get_property(np, "marvell,no-oc-protection", NULL))
+ pdata->flags |= NO_OC_PROTECTION;
+ if (of_get_property(np, "marvell,oc-mode-perport", NULL))
+ pdata->flags |= OC_MODE_PERPORT;
+ if (!of_property_read_u32(np, "marvell,power-on-delay", &tmp))
+ pdata->power_on_delay = tmp;
+ if (!of_property_read_u32(np, "marvell,port-mode", &tmp))
+ pdata->port_mode = tmp;
+ if (!of_property_read_u32(np, "marvell,power-budget", &tmp))
+ pdata->power_budget = tmp;
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static int __devinit ohci_pxa_of_init(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
/*-------------------------------------------------------------------------*/
@@ -297,6 +360,10 @@
struct resource *r;
struct clk *usb_clk;
+ retval = ohci_pxa_of_init(pdev);
+ if (retval)
+ return retval;
+
inf = pdev->dev.platform_data;
if (!inf)
@@ -544,6 +611,7 @@
.driver = {
.name = "pxa27x-ohci",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(pxa_ohci_dt_ids),
#ifdef CONFIG_PM
.pm = &ohci_hcd_pxa27x_pm_ops,
#endif
diff --git a/drivers/usb/host/ohci-xls.c b/drivers/usb/host/ohci-xls.c
index 41e378f..84201cd 100644
--- a/drivers/usb/host/ohci-xls.c
+++ b/drivers/usb/host/ohci-xls.c
@@ -56,7 +56,7 @@
goto err3;
}
- retval = usb_add_hcd(hcd, irq, IRQF_DISABLED | IRQF_SHARED);
+ retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
if (retval != 0)
goto err4;
return retval;
diff --git a/drivers/usb/host/pci-quirks.c b/drivers/usb/host/pci-quirks.c
index df0828c..c5e9e4a 100644
--- a/drivers/usb/host/pci-quirks.c
+++ b/drivers/usb/host/pci-quirks.c
@@ -800,6 +800,13 @@
}
EXPORT_SYMBOL_GPL(usb_enable_xhci_ports);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev)
+{
+ pci_write_config_dword(xhci_pdev, USB_INTEL_USB3_PSSEN, 0x0);
+ pci_write_config_dword(xhci_pdev, USB_INTEL_XUSB2PR, 0x0);
+}
+EXPORT_SYMBOL_GPL(usb_disable_xhci_ports);
+
/**
* PCI Quirks for xHCI.
*
diff --git a/drivers/usb/host/pci-quirks.h b/drivers/usb/host/pci-quirks.h
index b1002a8..ef004a5 100644
--- a/drivers/usb/host/pci-quirks.h
+++ b/drivers/usb/host/pci-quirks.h
@@ -10,6 +10,7 @@
void usb_amd_quirk_pll_enable(void);
bool usb_is_intel_switchable_xhci(struct pci_dev *pdev);
void usb_enable_xhci_ports(struct pci_dev *xhci_pdev);
+void usb_disable_xhci_ports(struct pci_dev *xhci_pdev);
#else
static inline void usb_amd_quirk_pll_disable(void) {}
static inline void usb_amd_quirk_pll_enable(void) {}
diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c
index 4c634eb..fcc09e5 100644
--- a/drivers/usb/host/r8a66597-hcd.c
+++ b/drivers/usb/host/r8a66597-hcd.c
@@ -2029,15 +2029,14 @@
static void collect_usb_address_map(struct usb_device *udev, unsigned long *map)
{
int chix;
+ struct usb_device *childdev;
if (udev->state == USB_STATE_CONFIGURED &&
udev->parent && udev->parent->devnum > 1 &&
udev->parent->descriptor.bDeviceClass == USB_CLASS_HUB)
map[udev->devnum/32] |= (1 << (udev->devnum % 32));
- for (chix = 0; chix < udev->maxchild; chix++) {
- struct usb_device *childdev = udev->children[chix];
-
+ usb_hub_for_each_child(udev, chix, childdev) {
if (childdev)
collect_usb_address_map(childdev, map);
}
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index e4db350..4b9e9ab 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -846,6 +846,11 @@
#define PLATFORM_DRIVER uhci_grlib_driver
#endif
+#ifdef CONFIG_USB_UHCI_PLATFORM
+#include "uhci-platform.c"
+#define PLATFORM_DRIVER uhci_platform_driver
+#endif
+
#if !defined(PCI_DRIVER) && !defined(PLATFORM_DRIVER)
#error "missing bus glue for uhci-hcd"
#endif
diff --git a/drivers/usb/host/uhci-platform.c b/drivers/usb/host/uhci-platform.c
new file mode 100644
index 0000000..e478049
--- /dev/null
+++ b/drivers/usb/host/uhci-platform.c
@@ -0,0 +1,157 @@
+/*
+ * Generic UHCI HCD (Host Controller Driver) for Platform Devices
+ *
+ * Copyright (c) 2011 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This file is based on uhci-grlib.c
+ * (C) Copyright 2004-2007 Alan Stern, stern@rowland.harvard.edu
+ */
+
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+static int uhci_platform_init(struct usb_hcd *hcd)
+{
+ struct uhci_hcd *uhci = hcd_to_uhci(hcd);
+
+ uhci->rh_numports = uhci_count_ports(hcd);
+
+ /* Set up pointers to to generic functions */
+ uhci->reset_hc = uhci_generic_reset_hc;
+ uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc;
+
+ /* No special actions need to be taken for the functions below */
+ uhci->configure_hc = NULL;
+ uhci->resume_detect_interrupts_are_broken = NULL;
+ uhci->global_suspend_mode_is_broken = NULL;
+
+ /* Reset if the controller isn't already safely quiescent. */
+ check_and_reset_hc(uhci);
+ return 0;
+}
+
+static const struct hc_driver uhci_platform_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Generic UHCI Host Controller",
+ .hcd_priv_size = sizeof(struct uhci_hcd),
+
+ /* Generic hardware linkage */
+ .irq = uhci_irq,
+ .flags = HCD_MEMORY | HCD_USB11,
+
+ /* Basic lifecycle operations */
+ .reset = uhci_platform_init,
+ .start = uhci_start,
+#ifdef CONFIG_PM
+ .pci_suspend = NULL,
+ .pci_resume = NULL,
+ .bus_suspend = uhci_rh_suspend,
+ .bus_resume = uhci_rh_resume,
+#endif
+ .stop = uhci_stop,
+
+ .urb_enqueue = uhci_urb_enqueue,
+ .urb_dequeue = uhci_urb_dequeue,
+
+ .endpoint_disable = uhci_hcd_endpoint_disable,
+ .get_frame_number = uhci_hcd_get_frame_number,
+
+ .hub_status_data = uhci_hub_status_data,
+ .hub_control = uhci_hub_control,
+};
+
+
+static int __devinit uhci_hcd_platform_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ struct uhci_hcd *uhci;
+ struct resource *res;
+ int ret;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ hcd = usb_create_hcd(&uhci_platform_hc_driver, &pdev->dev,
+ pdev->name);
+ if (!hcd)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ hcd->rsrc_start = res->start;
+ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_err("%s: request_mem_region failed\n", __func__);
+ ret = -EBUSY;
+ goto err_rmr;
+ }
+
+ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
+ if (!hcd->regs) {
+ pr_err("%s: ioremap failed\n", __func__);
+ ret = -ENOMEM;
+ goto err_irq;
+ }
+ uhci = hcd_to_uhci(hcd);
+
+ uhci->regs = hcd->regs;
+
+ ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED |
+ IRQF_SHARED);
+ if (ret)
+ goto err_uhci;
+
+ return 0;
+
+err_uhci:
+ iounmap(hcd->regs);
+err_irq:
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_rmr:
+ usb_put_hcd(hcd);
+
+ return ret;
+}
+
+static int uhci_hcd_platform_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+/* Make sure the controller is quiescent and that we're not using it
+ * any more. This is mainly for the benefit of programs which, like kexec,
+ * expect the hardware to be idle: not doing DMA or generating IRQs.
+ *
+ * This routine may be called in a damaged or failing kernel. Hence we
+ * do not acquire the spinlock before shutting down the controller.
+ */
+static void uhci_hcd_platform_shutdown(struct platform_device *op)
+{
+ struct usb_hcd *hcd = dev_get_drvdata(&op->dev);
+
+ uhci_hc_died(hcd_to_uhci(hcd));
+}
+
+static const struct of_device_id platform_uhci_ids[] = {
+ { .compatible = "platform-uhci", },
+ {}
+};
+
+static struct platform_driver uhci_platform_driver = {
+ .probe = uhci_hcd_platform_probe,
+ .remove = uhci_hcd_platform_remove,
+ .shutdown = uhci_hcd_platform_shutdown,
+ .driver = {
+ .name = "platform-uhci",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(platform_uhci_ids),
+ },
+};
diff --git a/drivers/usb/host/whci/hcd.c b/drivers/usb/host/whci/hcd.c
index 1e141f7..c3a6478 100644
--- a/drivers/usb/host/whci/hcd.c
+++ b/drivers/usb/host/whci/hcd.c
@@ -238,16 +238,16 @@
static int whc_probe(struct umc_dev *umc)
{
- int ret = -ENOMEM;
+ int ret;
struct usb_hcd *usb_hcd;
- struct wusbhc *wusbhc = NULL;
- struct whc *whc = NULL;
+ struct wusbhc *wusbhc;
+ struct whc *whc;
struct device *dev = &umc->dev;
usb_hcd = usb_create_hcd(&whc_hc_driver, dev, "whci");
if (usb_hcd == NULL) {
dev_err(dev, "unable to create hcd\n");
- goto error;
+ return -ENOMEM;
}
usb_hcd->wireless = 1;
diff --git a/drivers/usb/host/whci/qset.c b/drivers/usb/host/whci/qset.c
index 76083ae..dc31c42 100644
--- a/drivers/usb/host/whci/qset.c
+++ b/drivers/usb/host/whci/qset.c
@@ -436,7 +436,7 @@
int i;
int ntds = 0;
struct whc_std *std = NULL;
- struct whc_page_list_entry *entry;
+ struct whc_page_list_entry *new_pl_virt;
dma_addr_t prev_end = 0;
size_t pl_len;
int p = 0;
@@ -508,12 +508,15 @@
pl_len = std->num_pointers * sizeof(struct whc_page_list_entry);
- std->pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
- if (std->pl_virt == NULL) {
+ new_pl_virt = krealloc(std->pl_virt, pl_len, mem_flags);
+ if (new_pl_virt == NULL) {
+ kfree(std->pl_virt);
+ std->pl_virt = NULL;
return -ENOMEM;
}
+ std->pl_virt = new_pl_virt;
- for (;p < std->num_pointers; p++, entry++) {
+ for (;p < std->num_pointers; p++) {
std->pl_virt[p].buf_ptr = cpu_to_le64(dma_addr);
dma_addr = (dma_addr + WHCI_PAGE_SIZE) & ~(WHCI_PAGE_SIZE-1);
}
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 74bfc86..630e9e6 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -766,6 +766,12 @@
temp = xhci_readl(xhci, port_array[wIndex]);
xhci_dbg(xhci, "set port power, actual port %d status = 0x%x\n", wIndex, temp);
+
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, true);
break;
case USB_PORT_FEAT_RESET:
temp = (temp | PORT_RESET);
@@ -865,6 +871,16 @@
xhci_disable_port(hcd, xhci, wIndex,
port_array[wIndex], temp);
break;
+ case USB_PORT_FEAT_POWER:
+ xhci_writel(xhci, temp & ~PORT_POWER,
+ port_array[wIndex]);
+
+ temp = usb_acpi_power_manageable(hcd->self.root_hub,
+ wIndex);
+ if (temp)
+ usb_acpi_set_power_state(hcd->self.root_hub,
+ wIndex, false);
+ break;
default:
goto error;
}
diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
index 18b231b..9bfd4ca11 100644
--- a/drivers/usb/host/xhci-pci.c
+++ b/drivers/usb/host/xhci-pci.c
@@ -94,11 +94,21 @@
xhci->quirks |= XHCI_EP_LIMIT_QUIRK;
xhci->limit_active_eps = 64;
xhci->quirks |= XHCI_SW_BW_CHECKING;
+ /*
+ * PPT desktop boards DH77EB and DH77DF will power back on after
+ * a few seconds of being shutdown. The fix for this is to
+ * switch the ports from xHCI to EHCI on shutdown. We can't use
+ * DMI information to find those particular boards (since each
+ * vendor will change the board name), so we have to key off all
+ * PPT chipsets.
+ */
+ xhci->quirks |= XHCI_SPURIOUS_REBOOT;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_ASROCK_P67) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci_dbg(xhci, "QUIRK: Resetting on resume\n");
+ xhci->quirks |= XHCI_TRUST_TX_LENGTH;
}
if (pdev->vendor == PCI_VENDOR_ID_VIA)
xhci->quirks |= XHCI_RESET_ON_RESUME;
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 8275645..643c2f3 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -145,29 +145,37 @@
*/
static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
{
- union xhci_trb *next;
unsigned long long addr;
ring->deq_updates++;
- /* If this is not event ring, there is one more usable TRB */
+ /*
+ * If this is not event ring, and the dequeue pointer
+ * is not on a link TRB, there is one more usable TRB
+ */
if (ring->type != TYPE_EVENT &&
!last_trb(xhci, ring, ring->deq_seg, ring->dequeue))
ring->num_trbs_free++;
- next = ++(ring->dequeue);
- /* Update the dequeue pointer further if that was a link TRB or we're at
- * the end of an event ring segment (which doesn't have link TRBS)
- */
- while (last_trb(xhci, ring, ring->deq_seg, next)) {
- if (ring->type == TYPE_EVENT && last_trb_on_last_seg(xhci,
- ring, ring->deq_seg, next)) {
- ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ do {
+ /*
+ * Update the dequeue pointer further if that was a link TRB or
+ * we're at the end of an event ring segment (which doesn't have
+ * link TRBS)
+ */
+ if (last_trb(xhci, ring, ring->deq_seg, ring->dequeue)) {
+ if (ring->type == TYPE_EVENT &&
+ last_trb_on_last_seg(xhci, ring,
+ ring->deq_seg, ring->dequeue)) {
+ ring->cycle_state = (ring->cycle_state ? 0 : 1);
+ }
+ ring->deq_seg = ring->deq_seg->next;
+ ring->dequeue = ring->deq_seg->trbs;
+ } else {
+ ring->dequeue++;
}
- ring->deq_seg = ring->deq_seg->next;
- ring->dequeue = ring->deq_seg->trbs;
- next = ring->dequeue;
- }
+ } while (last_trb(xhci, ring, ring->deq_seg, ring->dequeue));
+
addr = (unsigned long long) xhci_trb_virt_to_dma(ring->deq_seg, ring->dequeue);
}
@@ -2073,8 +2081,8 @@
if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
trb_comp_code = COMP_SHORT_TX;
else
- xhci_warn(xhci, "WARN Successful completion on short TX: "
- "needs XHCI_TRUST_TX_LENGTH quirk?\n");
+ xhci_warn_ratelimited(xhci,
+ "WARN Successful completion on short TX: needs XHCI_TRUST_TX_LENGTH quirk?\n");
case COMP_SHORT_TX:
break;
case COMP_STOP:
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 7648b2d..c59d5b5 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -166,7 +166,7 @@
xhci_writel(xhci, command, &xhci->op_regs->command);
ret = handshake(xhci, &xhci->op_regs->command,
- CMD_RESET, 0, 250 * 1000);
+ CMD_RESET, 0, 10 * 1000 * 1000);
if (ret)
return ret;
@@ -175,7 +175,8 @@
* xHCI cannot write to any doorbells or operational registers other
* than status until the "Controller Not Ready" flag is cleared.
*/
- ret = handshake(xhci, &xhci->op_regs->status, STS_CNR, 0, 250 * 1000);
+ ret = handshake(xhci, &xhci->op_regs->status,
+ STS_CNR, 0, 10 * 1000 * 1000);
for (i = 0; i < 2; ++i) {
xhci->bus_state[i].port_c_suspend = 0;
@@ -658,6 +659,9 @@
{
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+ if (xhci->quirks && XHCI_SPURIOUS_REBOOT)
+ usb_disable_xhci_ports(to_pci_dev(hcd->self.controller));
+
spin_lock_irq(&xhci->lock);
xhci_halt(xhci);
spin_unlock_irq(&xhci->lock);
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 55c0785..c713256 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1494,6 +1494,7 @@
#define XHCI_TRUST_TX_LENGTH (1 << 10)
#define XHCI_LPM_SUPPORT (1 << 11)
#define XHCI_INTEL_HOST (1 << 12)
+#define XHCI_SPURIOUS_REBOOT (1 << 13)
unsigned int num_active_eps;
unsigned int limit_active_eps;
/* There are two roothubs to keep track of bus suspend info for */
@@ -1537,6 +1538,8 @@
dev_err(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
#define xhci_warn(xhci, fmt, args...) \
dev_warn(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
+#define xhci_warn_ratelimited(xhci, fmt, args...) \
+ dev_warn_ratelimited(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
/* TODO: copied from ehci.h - can be refactored? */
/* xHCI spec says all registers are little endian */
diff --git a/drivers/usb/misc/emi62.c b/drivers/usb/misc/emi62.c
index ff08015..ae794b9 100644
--- a/drivers/usb/misc/emi62.c
+++ b/drivers/usb/misc/emi62.c
@@ -232,7 +232,7 @@
return err;
}
-static const struct usb_device_id id_table[] __devinitconst = {
+static const struct usb_device_id id_table[] = {
{ USB_DEVICE(EMI62_VENDOR_ID, EMI62_PRODUCT_ID) },
{ } /* Terminating entry */
};
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index a2702cb..8089479 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -868,9 +868,6 @@
dbg(2, "%s: enter", __func__);
- if (udev == NULL)
- dev_info(&interface->dev, "udev is NULL.\n");
-
/* allocate memory for our device state and initialize it */
dev = kmalloc (sizeof(struct lego_usb_tower), GFP_KERNEL);
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index ef0c3f9..09f6856 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -8,11 +8,10 @@
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
depends on USB && USB_GADGET
select NOP_USB_XCEIV if (ARCH_DAVINCI || MACH_OMAP3EVM || BLACKFIN)
- select NOP_USB_XCEIV if (SOC_OMAPTI81XX || SOC_OMAPAM33XX)
+ select NOP_USB_XCEIV if (SOC_TI81XX || SOC_AM33XX)
select TWL4030_USB if MACH_OMAP_3430SDP
select TWL6030_USB if MACH_OMAP_4430SDP || MACH_OMAP4_PANDA
select USB_OTG_UTILS
- select USB_GADGET_DUALSPEED
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
@@ -57,7 +56,7 @@
config USB_MUSB_DSPS
tristate "TI DSPS platforms"
- depends on SOC_OMAPTI81XX || SOC_OMAPAM33XX
+ depends on SOC_TI81XX || SOC_AM33XX
config USB_MUSB_BLACKFIN
tristate "Blackfin"
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 4e899bb..c20b877 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -480,9 +480,9 @@
ret = -ENODEV;
goto err0;
}
- strcpy((u8 *)res->name, "mc");
res->parent = NULL;
resources[1] = *res;
+ resources[1].name = "mc";
/* allocate the child platform device */
musb = platform_device_alloc("musb-hdrc", -1);
@@ -567,27 +567,28 @@
}
platform_set_drvdata(pdev, glue);
- /* create the child platform device for first instances of musb */
- ret = dsps_create_musb_pdev(glue, 0);
- if (ret != 0) {
- dev_err(&pdev->dev, "failed to create child pdev\n");
- goto err2;
- }
-
/* enable the usbss clocks */
pm_runtime_enable(&pdev->dev);
ret = pm_runtime_get_sync(&pdev->dev);
if (ret < 0) {
dev_err(&pdev->dev, "pm_runtime_get_sync FAILED");
+ goto err2;
+ }
+
+ /* create the child platform device for first instances of musb */
+ ret = dsps_create_musb_pdev(glue, 0);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "failed to create child pdev\n");
goto err3;
}
return 0;
err3:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
err2:
+ pm_runtime_disable(&pdev->dev);
kfree(glue->wrp);
err1:
kfree(glue);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 8c9bb1a..681da06 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -603,12 +603,12 @@
struct usbhs_priv *priv = dev_get_drvdata(dev);
struct platform_device *pdev = usbhs_priv_to_pdev(priv);
- usbhs_platform_call(priv, phy_reset, pdev);
-
if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
usbhsc_power_ctrl(priv, 1);
- usbhsc_hotplug(priv);
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ usbhsc_drvcllbck_notify_hotplug(pdev);
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index ecd1730..143c4e9 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -818,7 +818,7 @@
usbhs_pipe_is_dcp(pipe))
goto usbhsf_pio_prepare_push;
- if (len % 4) /* 32bit alignment */
+ if (len & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_push;
if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
@@ -905,7 +905,7 @@
/* use PIO if packet is less than pio_dma_border */
len = usbhsf_fifo_rcv_len(priv, fifo);
len = min(pkt->length - pkt->actual, len);
- if (len % 4) /* 32bit alignment */
+ if (len & 0x7) /* 8byte alignment */
goto usbhsf_pio_prepare_pop_unselect;
if (len < usbhs_get_dparam(priv, pio_dma_border))
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
index 82a628f..35c5208 100644
--- a/drivers/usb/renesas_usbhs/mod.c
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -209,14 +209,18 @@
return (int)irq_state->intsts0 & CTSQ_MASK;
}
-static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
- struct usbhs_irq_state *state)
+static int usbhs_status_get_each_irq(struct usbhs_priv *priv,
+ struct usbhs_irq_state *state)
{
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ u16 intenb0, intenb1;
state->intsts0 = usbhs_read(priv, INTSTS0);
state->intsts1 = usbhs_read(priv, INTSTS1);
+ intenb0 = usbhs_read(priv, INTENB0);
+ intenb1 = usbhs_read(priv, INTENB1);
+
/* mask */
if (mod) {
state->brdysts = usbhs_read(priv, BRDYSTS);
@@ -226,6 +230,20 @@
state->bempsts &= mod->irq_bempsts;
state->brdysts &= mod->irq_brdysts;
}
+
+ /*
+ * Check whether the irq enable registers and the irq status are set
+ * when IRQF_SHARED is set.
+ */
+ if (priv->irqflags & IRQF_SHARED) {
+ if (!(intenb0 & state->intsts0) &&
+ !(intenb1 & state->intsts1) &&
+ !(state->bempsts) &&
+ !(state->brdysts))
+ return -EIO;
+ }
+
+ return 0;
}
/*
@@ -238,7 +256,8 @@
struct usbhs_priv *priv = data;
struct usbhs_irq_state irq_state;
- usbhs_status_get_each_irq(priv, &irq_state);
+ if (usbhs_status_get_each_irq(priv, &irq_state) < 0)
+ return IRQ_NONE;
/*
* clear interrupt
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 1834cf5..9b69a13 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1266,6 +1266,12 @@
return ret;
}
+static int usbhsh_bus_nop(struct usb_hcd *hcd)
+{
+ /* nothing to do */
+ return 0;
+}
+
static struct hc_driver usbhsh_driver = {
.description = usbhsh_hcd_name,
.hcd_priv_size = sizeof(struct usbhsh_hpriv),
@@ -1290,6 +1296,8 @@
*/
.hub_status_data = usbhsh_hub_status_data,
.hub_control = usbhsh_hub_control,
+ .bus_suspend = usbhsh_bus_nop,
+ .bus_resume = usbhsh_bus_nop,
};
/*
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
index f398d1e..c15f2e7 100644
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -61,18 +61,23 @@
goto exit;
}
+ /* make sure suspend/resume doesn't race against port_probe */
+ retval = usb_autopm_get_interface(port->serial->interface);
+ if (retval)
+ goto exit;
+
driver = port->serial->type;
if (driver->port_probe) {
retval = driver->port_probe(port);
if (retval)
- goto exit;
+ goto exit_with_autopm;
}
retval = device_create_file(dev, &dev_attr_port_number);
if (retval) {
if (driver->port_remove)
retval = driver->port_remove(port);
- goto exit;
+ goto exit_with_autopm;
}
minor = port->number;
@@ -81,6 +86,8 @@
"%s converter now attached to ttyUSB%d\n",
driver->description, minor);
+exit_with_autopm:
+ usb_autopm_put_interface(port->serial->interface);
exit:
return retval;
}
@@ -96,6 +103,9 @@
if (!port)
return -ENODEV;
+ /* make sure suspend/resume doesn't race against port_remove */
+ usb_autopm_get_interface(port->serial->interface);
+
device_remove_file(&port->dev, &dev_attr_port_number);
driver = port->serial->type;
@@ -107,6 +117,7 @@
dev_info(dev, "%s converter now disconnected from ttyUSB%d\n",
driver->description, minor);
+ usb_autopm_put_interface(port->serial->interface);
return retval;
}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
index 1e71079..3f86cf3 100644
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -865,7 +865,6 @@
if (!port_priv)
return -ENOMEM;
- memset(port_priv, 0x00, sizeof(*port_priv));
port_priv->bInterfaceNumber =
serial->interface->cur_altsetting->desc.bInterfaceNumber;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index bc912e5..29b81ad 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -811,6 +811,7 @@
{ USB_DEVICE(LARSENBRUSGAARD_VID, LB_ALTITRACK_PID) },
{ USB_DEVICE(GN_OTOMETRICS_VID, AURICAL_USB_PID) },
{ USB_DEVICE(PI_VID, PI_E861_PID) },
+ { USB_DEVICE(KONDO_VID, KONDO_USB_SERIAL_PID) },
{ USB_DEVICE(BAYER_VID, BAYER_CONTOUR_CABLE_PID) },
{ USB_DEVICE(FTDI_VID, MARVELL_OPENRD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
@@ -2106,7 +2107,7 @@
cflag = termios->c_cflag;
- if (old_termios == 0)
+ if (!old_termios)
goto no_skip;
if (old_termios->c_cflag == termios->c_cflag
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 5661c7e..5dd96ca 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -795,6 +795,13 @@
#define PI_E861_PID 0x1008 /* E-861 piezo controller USB connection */
/*
+ * Kondo Kagaku Co.Ltd.
+ * http://www.kondo-robot.com/EN
+ */
+#define KONDO_VID 0x165c
+#define KONDO_USB_SERIAL_PID 0x0002
+
+/*
* Bayer Ascensia Contour blood glucose meter USB-converter cable.
* http://winglucofacts.com/cables/
*/
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 5811d34..2cb30c5 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -227,7 +227,6 @@
{
struct usb_wwan_intf_private *data = usb_get_serial_data(serial);
- usb_wwan_release(serial);
usb_set_serial_data(serial, NULL);
kfree(data);
}
@@ -309,12 +308,12 @@
.description = "IPWireless converter",
.id_table = id_table,
.num_ports = 1,
- .disconnect = usb_wwan_disconnect,
.open = ipw_open,
.close = ipw_close,
.probe = ipw_probe,
.attach = usb_wwan_startup,
.release = ipw_release,
+ .port_remove = usb_wwan_port_remove,
.dtr_rts = ipw_dtr_rts,
.write = usb_wwan_write,
};
diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c
index a07dd3c..9817d9d 100644
--- a/drivers/usb/serial/mos7720.c
+++ b/drivers/usb/serial/mos7720.c
@@ -281,7 +281,7 @@
int ret_val;
unsigned long flags;
struct mos7715_parport *mos_parport = (void *)_mos_parport;
- struct urbtracker *urbtrack;
+ struct urbtracker *urbtrack, *tmp;
struct list_head *cursor, *next;
/* if release function ran, game over */
@@ -312,7 +312,7 @@
/* move contents of deferred_urbs list to active_urbs list and submit */
list_for_each_safe(cursor, next, &mos_parport->deferred_urbs)
list_move_tail(cursor, &mos_parport->active_urbs);
- list_for_each_entry(urbtrack, &mos_parport->active_urbs,
+ list_for_each_entry_safe(urbtrack, tmp, &mos_parport->active_urbs,
urblist_entry) {
ret_val = usb_submit_urb(urbtrack->urb, GFP_ATOMIC);
dbg("%s: urb submitted", __func__);
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
index 57eca24..2f6da1e 100644
--- a/drivers/usb/serial/mos7840.c
+++ b/drivers/usb/serial/mos7840.c
@@ -82,8 +82,7 @@
* Defines used for sending commands to port
*/
-#define WAIT_FOR_EVER (HZ * 0) /* timeout urb is wait for ever */
-#define MOS_WDR_TIMEOUT (HZ * 5) /* default urb timeout */
+#define MOS_WDR_TIMEOUT 5000 /* default urb timeout */
#define MOS_PORT1 0x0200
#define MOS_PORT2 0x0300
@@ -1232,9 +1231,12 @@
return 0;
spin_lock_irqsave(&mos7840_port->pool_lock, flags);
- for (i = 0; i < NUM_URBS; ++i)
- if (mos7840_port->busy[i])
- chars += URB_TRANSFER_BUFFER_SIZE;
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->busy[i]) {
+ struct urb *urb = mos7840_port->write_urb_pool[i];
+ chars += urb->transfer_buffer_length;
+ }
+ }
spin_unlock_irqrestore(&mos7840_port->pool_lock, flags);
dbg("%s - returns %d", __func__, chars);
return chars;
@@ -1344,7 +1346,7 @@
static void mos7840_block_until_chase_response(struct tty_struct *tty,
struct moschip_port *mos7840_port)
{
- int timeout = 1 * HZ;
+ int timeout = msecs_to_jiffies(1000);
int wait = 10;
int count;
@@ -2672,7 +2674,7 @@
/* setting configuration feature to one */
usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
- (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, 5 * HZ);
+ (__u8) 0x03, 0x00, 0x01, 0x00, NULL, 0x00, MOS_WDR_TIMEOUT);
return 0;
error:
for (/* nothing */; i >= 0; i--) {
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
index 08ff9b8..cc40f47 100644
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -80,85 +80,9 @@
#define OPTION_PRODUCT_GTM380_MODEM 0x7201
#define HUAWEI_VENDOR_ID 0x12D1
-#define HUAWEI_PRODUCT_E600 0x1001
-#define HUAWEI_PRODUCT_E220 0x1003
-#define HUAWEI_PRODUCT_E220BIS 0x1004
-#define HUAWEI_PRODUCT_E1401 0x1401
-#define HUAWEI_PRODUCT_E1402 0x1402
-#define HUAWEI_PRODUCT_E1403 0x1403
-#define HUAWEI_PRODUCT_E1404 0x1404
-#define HUAWEI_PRODUCT_E1405 0x1405
-#define HUAWEI_PRODUCT_E1406 0x1406
-#define HUAWEI_PRODUCT_E1407 0x1407
-#define HUAWEI_PRODUCT_E1408 0x1408
-#define HUAWEI_PRODUCT_E1409 0x1409
-#define HUAWEI_PRODUCT_E140A 0x140A
-#define HUAWEI_PRODUCT_E140B 0x140B
-#define HUAWEI_PRODUCT_E140C 0x140C
-#define HUAWEI_PRODUCT_E140D 0x140D
-#define HUAWEI_PRODUCT_E140E 0x140E
-#define HUAWEI_PRODUCT_E140F 0x140F
-#define HUAWEI_PRODUCT_E1410 0x1410
-#define HUAWEI_PRODUCT_E1411 0x1411
-#define HUAWEI_PRODUCT_E1412 0x1412
-#define HUAWEI_PRODUCT_E1413 0x1413
-#define HUAWEI_PRODUCT_E1414 0x1414
-#define HUAWEI_PRODUCT_E1415 0x1415
-#define HUAWEI_PRODUCT_E1416 0x1416
-#define HUAWEI_PRODUCT_E1417 0x1417
-#define HUAWEI_PRODUCT_E1418 0x1418
-#define HUAWEI_PRODUCT_E1419 0x1419
-#define HUAWEI_PRODUCT_E141A 0x141A
-#define HUAWEI_PRODUCT_E141B 0x141B
-#define HUAWEI_PRODUCT_E141C 0x141C
-#define HUAWEI_PRODUCT_E141D 0x141D
-#define HUAWEI_PRODUCT_E141E 0x141E
-#define HUAWEI_PRODUCT_E141F 0x141F
-#define HUAWEI_PRODUCT_E1420 0x1420
-#define HUAWEI_PRODUCT_E1421 0x1421
-#define HUAWEI_PRODUCT_E1422 0x1422
-#define HUAWEI_PRODUCT_E1423 0x1423
-#define HUAWEI_PRODUCT_E1424 0x1424
-#define HUAWEI_PRODUCT_E1425 0x1425
-#define HUAWEI_PRODUCT_E1426 0x1426
-#define HUAWEI_PRODUCT_E1427 0x1427
-#define HUAWEI_PRODUCT_E1428 0x1428
-#define HUAWEI_PRODUCT_E1429 0x1429
-#define HUAWEI_PRODUCT_E142A 0x142A
-#define HUAWEI_PRODUCT_E142B 0x142B
-#define HUAWEI_PRODUCT_E142C 0x142C
-#define HUAWEI_PRODUCT_E142D 0x142D
-#define HUAWEI_PRODUCT_E142E 0x142E
-#define HUAWEI_PRODUCT_E142F 0x142F
-#define HUAWEI_PRODUCT_E1430 0x1430
-#define HUAWEI_PRODUCT_E1431 0x1431
-#define HUAWEI_PRODUCT_E1432 0x1432
-#define HUAWEI_PRODUCT_E1433 0x1433
-#define HUAWEI_PRODUCT_E1434 0x1434
-#define HUAWEI_PRODUCT_E1435 0x1435
-#define HUAWEI_PRODUCT_E1436 0x1436
-#define HUAWEI_PRODUCT_E1437 0x1437
-#define HUAWEI_PRODUCT_E1438 0x1438
-#define HUAWEI_PRODUCT_E1439 0x1439
-#define HUAWEI_PRODUCT_E143A 0x143A
-#define HUAWEI_PRODUCT_E143B 0x143B
-#define HUAWEI_PRODUCT_E143C 0x143C
-#define HUAWEI_PRODUCT_E143D 0x143D
-#define HUAWEI_PRODUCT_E143E 0x143E
-#define HUAWEI_PRODUCT_E143F 0x143F
#define HUAWEI_PRODUCT_K4505 0x1464
#define HUAWEI_PRODUCT_K3765 0x1465
-#define HUAWEI_PRODUCT_E14AC 0x14AC
-#define HUAWEI_PRODUCT_K3806 0x14AE
#define HUAWEI_PRODUCT_K4605 0x14C6
-#define HUAWEI_PRODUCT_K5005 0x14C8
-#define HUAWEI_PRODUCT_K3770 0x14C9
-#define HUAWEI_PRODUCT_K3771 0x14CA
-#define HUAWEI_PRODUCT_K4510 0x14CB
-#define HUAWEI_PRODUCT_K4511 0x14CC
-#define HUAWEI_PRODUCT_ETS1220 0x1803
-#define HUAWEI_PRODUCT_E353 0x1506
-#define HUAWEI_PRODUCT_E173S 0x1C05
#define QUANTA_VENDOR_ID 0x0408
#define QUANTA_PRODUCT_Q101 0xEA02
@@ -615,104 +539,123 @@
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLX) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GKE) },
{ USB_DEVICE(QUANTA_VENDOR_ID, QUANTA_PRODUCT_GLE) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E600, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E220BIS, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1401, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1402, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1403, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1404, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1405, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1406, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1407, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1408, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1409, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E140F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1410, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1411, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1412, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1413, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1414, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1415, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1416, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1417, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1418, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1419, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E141F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1420, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1421, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1422, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1423, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1424, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1425, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1426, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1427, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1428, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1429, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E142F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1430, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1431, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1432, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1433, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1434, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1435, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1436, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1437, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1438, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E1439, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143A, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143B, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143C, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143D, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143E, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E143F, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E173S, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4505, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3765, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ETS1220, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E14AC, 0xff, 0xff, 0xff) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3806, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t) &huawei_cdc12_blacklist },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4605, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K5005, 0xff, 0x01, 0x33) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3770, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K3771, 0xff, 0x02, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4510, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x31) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_K4511, 0xff, 0x01, 0x32) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x01) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x02) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x03) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x10) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x12) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x01, 0x13) },
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x01) }, /* E398 3G Modem */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x02) }, /* E398 3G PC UI Interface */
- { USB_DEVICE_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_E353, 0xff, 0x02, 0x03) }, /* E398 3G Application Interface */
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0xff, 0xff) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x01, 0x7C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x01) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x02) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x03) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x04) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x05) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x06) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x0F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x10) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x12) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x13) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x14) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x15) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x17) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x18) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x19) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x1C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x31) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x32) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x33) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x34) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x35) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x36) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x3F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x48) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x49) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x4C) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x61) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x62) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x63) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x64) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x65) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x66) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6D) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6E) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x6F) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x78) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x79) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7A) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7B) },
+ { USB_VENDOR_AND_INTERFACE_INFO(HUAWEI_VENDOR_ID, 0xff, 0x02, 0x7C) },
+
+
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V640) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V620) },
{ USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, NOVATELWIRELESS_PRODUCT_V740) },
@@ -943,6 +886,8 @@
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1010, 0xff, 0xff, 0xff),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1012, 0xff, 0xff, 0xff) },
+ { USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1018, 0xff, 0xff, 0xff),
+ .driver_info = (kernel_ulong_t)&net_intf3_blacklist },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1057, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1058, 0xff, 0xff, 0xff) },
{ USB_DEVICE_AND_INTERFACE_INFO(ZTE_VENDOR_ID, 0x1059, 0xff, 0xff, 0xff) },
@@ -1297,8 +1242,8 @@
.tiocmset = usb_wwan_tiocmset,
.ioctl = usb_wwan_ioctl,
.attach = usb_wwan_startup,
- .disconnect = usb_wwan_disconnect,
.release = option_release,
+ .port_remove = usb_wwan_port_remove,
.read_int_callback = option_instat_callback,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
@@ -1414,8 +1359,6 @@
struct usb_wwan_intf_private *intfdata = usb_get_serial_data(serial);
struct option_private *priv = intfdata->private;
- usb_wwan_release(serial);
-
kfree(priv);
kfree(intfdata);
}
diff --git a/drivers/usb/serial/qcserial.c b/drivers/usb/serial/qcserial.c
index 8d10301..bfd5077 100644
--- a/drivers/usb/serial/qcserial.c
+++ b/drivers/usb/serial/qcserial.c
@@ -199,43 +199,49 @@
/* default to enabling interface */
altsetting = 0;
- switch (ifnum) {
- /* Composite mode; don't bind to the QMI/net interface as that
- * gets handled by other drivers.
- */
+ /* Composite mode; don't bind to the QMI/net interface as that
+ * gets handled by other drivers.
+ */
+
+ if (is_gobi1k) {
/* Gobi 1K USB layout:
* 0: serial port (doesn't respond)
* 1: serial port (doesn't respond)
* 2: AT-capable modem port
* 3: QMI/net
- *
- * Gobi 2K+ USB layout:
+ */
+ if (ifnum == 2)
+ dev_dbg(dev, "Modem port found\n");
+ else
+ altsetting = -1;
+ } else {
+ /* Gobi 2K+ USB layout:
* 0: QMI/net
* 1: DM/DIAG (use libqcdm from ModemManager for communication)
* 2: AT-capable modem port
* 3: NMEA
*/
-
- case 1:
- if (is_gobi1k)
+ switch (ifnum) {
+ case 0:
+ /* Don't claim the QMI/net interface */
altsetting = -1;
- else
+ break;
+ case 1:
dev_dbg(dev, "Gobi 2K+ DM/DIAG interface found\n");
- break;
- case 2:
- dev_dbg(dev, "Modem port found\n");
- break;
- case 3:
- if (is_gobi1k)
- altsetting = -1;
- else
+ break;
+ case 2:
+ dev_dbg(dev, "Modem port found\n");
+ break;
+ case 3:
/*
* NMEA (serial line 9600 8N1)
* # echo "\$GPS_START" > /dev/ttyUSBx
* # echo "\$GPS_STOP" > /dev/ttyUSBx
*/
dev_dbg(dev, "Gobi 2K+ NMEA GPS interface found\n");
+ break;
+ }
}
done:
@@ -262,8 +268,7 @@
{
struct usb_wwan_intf_private *priv = usb_get_serial_data(serial);
- /* Call usb_wwan release & free the private data allocated in qcprobe */
- usb_wwan_release(serial);
+ /* Free the private data allocated in qcprobe */
usb_set_serial_data(serial, NULL);
kfree(priv);
}
@@ -283,8 +288,8 @@
.write_room = usb_wwan_write_room,
.chars_in_buffer = usb_wwan_chars_in_buffer,
.attach = usb_wwan_startup,
- .disconnect = usb_wwan_disconnect,
.release = qc_release,
+ .port_remove = usb_wwan_port_remove,
#ifdef CONFIG_PM
.suspend = usb_wwan_suspend,
.resume = usb_wwan_resume,
diff --git a/drivers/usb/serial/usb-wwan.h b/drivers/usb/serial/usb-wwan.h
index c47b6ec..1f034d2 100644
--- a/drivers/usb/serial/usb-wwan.h
+++ b/drivers/usb/serial/usb-wwan.h
@@ -9,8 +9,7 @@
extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
extern void usb_wwan_close(struct usb_serial_port *port);
extern int usb_wwan_startup(struct usb_serial *serial);
-extern void usb_wwan_disconnect(struct usb_serial *serial);
-extern void usb_wwan_release(struct usb_serial *serial);
+extern int usb_wwan_port_remove(struct usb_serial_port *port);
extern int usb_wwan_write_room(struct tty_struct *tty);
extern void usb_wwan_set_termios(struct tty_struct *tty,
struct usb_serial_port *port,
diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c
index f35971d..6855d5e 100644
--- a/drivers/usb/serial/usb_wwan.c
+++ b/drivers/usb/serial/usb_wwan.c
@@ -565,6 +565,33 @@
}
EXPORT_SYMBOL(usb_wwan_startup);
+int usb_wwan_port_remove(struct usb_serial_port *port)
+{
+ int i;
+ struct usb_wwan_port_private *portdata;
+
+ portdata = usb_get_serial_port_data(port);
+ usb_set_serial_port_data(port, NULL);
+
+ /* Stop reading/writing urbs and free them */
+ for (i = 0; i < N_IN_URB; i++) {
+ usb_kill_urb(portdata->in_urbs[i]);
+ usb_free_urb(portdata->in_urbs[i]);
+ free_page((unsigned long)portdata->in_buffer[i]);
+ }
+ for (i = 0; i < N_OUT_URB; i++) {
+ usb_kill_urb(portdata->out_urbs[i]);
+ usb_free_urb(portdata->out_urbs[i]);
+ kfree(portdata->out_buffer[i]);
+ }
+
+ /* Now free port private data */
+ kfree(portdata);
+ return 0;
+}
+EXPORT_SYMBOL(usb_wwan_port_remove);
+
+#ifdef CONFIG_PM
static void stop_read_write_urbs(struct usb_serial *serial)
{
int i, j;
@@ -575,6 +602,8 @@
for (i = 0; i < serial->num_ports; ++i) {
port = serial->port[i];
portdata = usb_get_serial_port_data(port);
+ if (!portdata)
+ continue;
for (j = 0; j < N_IN_URB; j++)
usb_kill_urb(portdata->in_urbs[j]);
for (j = 0; j < N_OUT_URB; j++)
@@ -582,45 +611,6 @@
}
}
-void usb_wwan_disconnect(struct usb_serial *serial)
-{
- stop_read_write_urbs(serial);
-}
-EXPORT_SYMBOL(usb_wwan_disconnect);
-
-void usb_wwan_release(struct usb_serial *serial)
-{
- int i, j;
- struct usb_serial_port *port;
- struct usb_wwan_port_private *portdata;
-
- /* Now free them */
- for (i = 0; i < serial->num_ports; ++i) {
- port = serial->port[i];
- portdata = usb_get_serial_port_data(port);
-
- for (j = 0; j < N_IN_URB; j++) {
- usb_free_urb(portdata->in_urbs[j]);
- free_page((unsigned long)
- portdata->in_buffer[j]);
- portdata->in_urbs[j] = NULL;
- }
- for (j = 0; j < N_OUT_URB; j++) {
- usb_free_urb(portdata->out_urbs[j]);
- kfree(portdata->out_buffer[j]);
- portdata->out_urbs[j] = NULL;
- }
- }
-
- /* Now free per port private data */
- for (i = 0; i < serial->num_ports; i++) {
- port = serial->port[i];
- kfree(usb_get_serial_port_data(port));
- }
-}
-EXPORT_SYMBOL(usb_wwan_release);
-
-#ifdef CONFIG_PM
int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
{
struct usb_wwan_intf_private *intfdata = serial->private;
@@ -712,7 +702,7 @@
/* skip closed ports */
spin_lock_irq(&intfdata->susp_lock);
- if (!portdata->opened) {
+ if (!portdata || !portdata->opened) {
spin_unlock_irq(&intfdata->susp_lock);
continue;
}
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index 7691c86..0ae7bb6 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -213,17 +213,3 @@
say 'Y' or 'M' here and the kernel will use the right driver.
If you compile this driver as a module, it will be named uas.
-
-config USB_LIBUSUAL
- bool "The shared table of common (or usual) storage devices"
- depends on USB
- help
- This module contains a table of common (or usual) devices
- for usb-storage and ub drivers, and allows to switch binding
- of these devices without rebuilding modules.
-
- Typical syntax of /etc/modprobe.d/*conf is:
-
- options libusual bias="ub"
-
- If unsure, say N.
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 82e6416..4cd5548 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -12,16 +12,9 @@
usb-storage-y := scsiglue.o protocol.o transport.o usb.o
usb-storage-y += initializers.o sierra_ms.o option_ms.o
-
+usb-storage-y += usual-tables.o
usb-storage-$(CONFIG_USB_STORAGE_DEBUG) += debug.o
-ifeq ($(CONFIG_USB_LIBUSUAL),)
- usb-storage-y += usual-tables.o
-else
- obj-$(CONFIG_USB) += usb-libusual.o
- usb-libusual-y := libusual.o usual-tables.o
-endif
-
obj-$(CONFIG_USB_STORAGE_ALAUDA) += ums-alauda.o
obj-$(CONFIG_USB_STORAGE_CYPRESS_ATACB) += ums-cypress.o
obj-$(CONFIG_USB_STORAGE_DATAFAB) += ums-datafab.o
diff --git a/drivers/usb/storage/alauda.c b/drivers/usb/storage/alauda.c
index bab8c8f..be5564c 100644
--- a/drivers/usb/storage/alauda.c
+++ b/drivers/usb/storage/alauda.c
@@ -137,7 +137,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id alauda_usb_ids[] = {
# include "unusual_alauda.h"
diff --git a/drivers/usb/storage/cypress_atacb.c b/drivers/usb/storage/cypress_atacb.c
index 5fe451d..070b5c0 100644
--- a/drivers/usb/storage/cypress_atacb.c
+++ b/drivers/usb/storage/cypress_atacb.c
@@ -41,7 +41,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id cypress_usb_ids[] = {
# include "unusual_cypress.h"
diff --git a/drivers/usb/storage/datafab.c b/drivers/usb/storage/datafab.c
index 35e9c51..494fee5 100644
--- a/drivers/usb/storage/datafab.c
+++ b/drivers/usb/storage/datafab.c
@@ -86,7 +86,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id datafab_usb_ids[] = {
# include "unusual_datafab.h"
diff --git a/drivers/usb/storage/ene_ub6250.c b/drivers/usb/storage/ene_ub6250.c
index b28f2ad..118b134 100644
--- a/drivers/usb/storage/ene_ub6250.c
+++ b/drivers/usb/storage/ene_ub6250.c
@@ -29,9 +29,21 @@
#include "protocol.h"
#include "debug.h"
+#define SD_INIT1_FIRMWARE "ene-ub6250/sd_init1.bin"
+#define SD_INIT2_FIRMWARE "ene-ub6250/sd_init2.bin"
+#define SD_RW_FIRMWARE "ene-ub6250/sd_rdwr.bin"
+#define MS_INIT_FIRMWARE "ene-ub6250/ms_init.bin"
+#define MSP_RW_FIRMWARE "ene-ub6250/msp_rdwr.bin"
+#define MS_RW_FIRMWARE "ene-ub6250/ms_rdwr.bin"
+
MODULE_DESCRIPTION("Driver for ENE UB6250 reader");
MODULE_LICENSE("GPL");
-
+MODULE_FIRMWARE(SD_INIT1_FIRMWARE);
+MODULE_FIRMWARE(SD_INIT2_FIRMWARE);
+MODULE_FIRMWARE(SD_RW_FIRMWARE);
+MODULE_FIRMWARE(MS_INIT_FIRMWARE);
+MODULE_FIRMWARE(MSP_RW_FIRMWARE);
+MODULE_FIRMWARE(MS_RW_FIRMWARE);
/*
* The table of devices
@@ -40,7 +52,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags)}
static struct usb_device_id ene_ub6250_usb_ids[] = {
# include "unusual_ene_ub6250.h"
@@ -1883,28 +1895,28 @@
/* For SD */
case SD_INIT1_PATTERN:
US_DEBUGP("SD_INIT1_PATTERN\n");
- fw_name = "ene-ub6250/sd_init1.bin";
+ fw_name = SD_INIT1_FIRMWARE;
break;
case SD_INIT2_PATTERN:
US_DEBUGP("SD_INIT2_PATTERN\n");
- fw_name = "ene-ub6250/sd_init2.bin";
+ fw_name = SD_INIT2_FIRMWARE;
break;
case SD_RW_PATTERN:
- US_DEBUGP("SD_RDWR_PATTERN\n");
- fw_name = "ene-ub6250/sd_rdwr.bin";
+ US_DEBUGP("SD_RW_PATTERN\n");
+ fw_name = SD_RW_FIRMWARE;
break;
/* For MS */
case MS_INIT_PATTERN:
US_DEBUGP("MS_INIT_PATTERN\n");
- fw_name = "ene-ub6250/ms_init.bin";
+ fw_name = MS_INIT_FIRMWARE;
break;
case MSP_RW_PATTERN:
US_DEBUGP("MSP_RW_PATTERN\n");
- fw_name = "ene-ub6250/msp_rdwr.bin";
+ fw_name = MSP_RW_FIRMWARE;
break;
case MS_RW_PATTERN:
US_DEBUGP("MS_RW_PATTERN\n");
- fw_name = "ene-ub6250/ms_rdwr.bin";
+ fw_name = MS_RW_FIRMWARE;
break;
default:
US_DEBUGP("----------- Unknown PATTERN ----------\n");
diff --git a/drivers/usb/storage/freecom.c b/drivers/usb/storage/freecom.c
index 042cf9e..e6df087 100644
--- a/drivers/usb/storage/freecom.c
+++ b/drivers/usb/storage/freecom.c
@@ -117,7 +117,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id freecom_usb_ids[] = {
# include "unusual_freecom.h"
diff --git a/drivers/usb/storage/isd200.c b/drivers/usb/storage/isd200.c
index 31fa24e..ecea478 100644
--- a/drivers/usb/storage/isd200.c
+++ b/drivers/usb/storage/isd200.c
@@ -74,7 +74,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id isd200_usb_ids[] = {
# include "unusual_isd200.h"
@@ -83,7 +83,6 @@
MODULE_DEVICE_TABLE(usb, isd200_usb_ids);
#undef UNUSUAL_DEV
-#undef USUAL_DEV
/*
* The flags table
@@ -105,8 +104,6 @@
};
#undef UNUSUAL_DEV
-#undef USUAL_DEV
-
/* Timeout defines (in Seconds) */
diff --git a/drivers/usb/storage/jumpshot.c b/drivers/usb/storage/jumpshot.c
index e3b9738..ddc7878 100644
--- a/drivers/usb/storage/jumpshot.c
+++ b/drivers/usb/storage/jumpshot.c
@@ -69,7 +69,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id jumpshot_usb_ids[] = {
# include "unusual_jumpshot.h"
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
index a8708ea..f085ffb 100644
--- a/drivers/usb/storage/karma.c
+++ b/drivers/usb/storage/karma.c
@@ -57,7 +57,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id karma_usb_ids[] = {
# include "unusual_karma.h"
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
deleted file mode 100644
index fe3ffe1..0000000
--- a/drivers/usb/storage/libusual.c
+++ /dev/null
@@ -1,243 +0,0 @@
-/*
- * libusual
- *
- * The libusual contains the table of devices common for ub and usb-storage.
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/usb.h>
-#include <linux/usb_usual.h>
-#include <linux/vmalloc.h>
-#include <linux/kthread.h>
-#include <linux/mutex.h>
-
-/*
- */
-#define USU_MOD_FL_THREAD 1 /* Thread is running */
-#define USU_MOD_FL_PRESENT 2 /* The module is loaded */
-
-struct mod_status {
- unsigned long fls;
-};
-
-static struct mod_status stat[3];
-static DEFINE_SPINLOCK(usu_lock);
-
-/*
- */
-#define USB_US_DEFAULT_BIAS USB_US_TYPE_STOR
-static atomic_t usu_bias = ATOMIC_INIT(USB_US_DEFAULT_BIAS);
-
-#define BIAS_NAME_SIZE (sizeof("usb-storage"))
-static const char *bias_names[3] = { "none", "usb-storage", "ub" };
-
-static DEFINE_MUTEX(usu_probe_mutex);
-static DECLARE_COMPLETION(usu_end_notify);
-static atomic_t total_threads = ATOMIC_INIT(0);
-
-static int usu_probe_thread(void *arg);
-
-/*
- * @type: the module type as an integer
- */
-void usb_usual_set_present(int type)
-{
- struct mod_status *st;
- unsigned long flags;
-
- if (type <= 0 || type >= 3)
- return;
- st = &stat[type];
- spin_lock_irqsave(&usu_lock, flags);
- st->fls |= USU_MOD_FL_PRESENT;
- spin_unlock_irqrestore(&usu_lock, flags);
-}
-EXPORT_SYMBOL_GPL(usb_usual_set_present);
-
-void usb_usual_clear_present(int type)
-{
- struct mod_status *st;
- unsigned long flags;
-
- if (type <= 0 || type >= 3)
- return;
- st = &stat[type];
- spin_lock_irqsave(&usu_lock, flags);
- st->fls &= ~USU_MOD_FL_PRESENT;
- spin_unlock_irqrestore(&usu_lock, flags);
-}
-EXPORT_SYMBOL_GPL(usb_usual_clear_present);
-
-/*
- * Match the calling driver type against the table.
- * Returns: 0 if the device matches.
- */
-int usb_usual_check_type(const struct usb_device_id *id, int caller_type)
-{
- int id_type = USB_US_TYPE(id->driver_info);
-
- if (caller_type <= 0 || caller_type >= 3)
- return -EINVAL;
-
- /* Drivers grab fixed assignment devices */
- if (id_type == caller_type)
- return 0;
- /* Drivers grab devices biased to them */
- if (id_type == USB_US_TYPE_NONE && caller_type == atomic_read(&usu_bias))
- return 0;
- return -ENODEV;
-}
-EXPORT_SYMBOL_GPL(usb_usual_check_type);
-
-/*
- */
-static int usu_probe(struct usb_interface *intf,
- const struct usb_device_id *id)
-{
- int rc;
- unsigned long type;
- struct task_struct* task;
- unsigned long flags;
-
- type = USB_US_TYPE(id->driver_info);
- if (type == 0)
- type = atomic_read(&usu_bias);
-
- spin_lock_irqsave(&usu_lock, flags);
- if ((stat[type].fls & (USU_MOD_FL_THREAD|USU_MOD_FL_PRESENT)) != 0) {
- spin_unlock_irqrestore(&usu_lock, flags);
- return -ENXIO;
- }
- stat[type].fls |= USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
-
- task = kthread_run(usu_probe_thread, (void*)type, "libusual_%ld", type);
- if (IS_ERR(task)) {
- rc = PTR_ERR(task);
- printk(KERN_WARNING "libusual: "
- "Unable to start the thread for %s: %d\n",
- bias_names[type], rc);
- spin_lock_irqsave(&usu_lock, flags);
- stat[type].fls &= ~USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
- return rc; /* Not being -ENXIO causes a message printed */
- }
- atomic_inc(&total_threads);
-
- return -ENXIO;
-}
-
-static void usu_disconnect(struct usb_interface *intf)
-{
- ; /* We should not be here. */
-}
-
-static struct usb_driver usu_driver = {
- .name = "libusual",
- .probe = usu_probe,
- .disconnect = usu_disconnect,
- .id_table = usb_storage_usb_ids,
-};
-
-/*
- * A whole new thread for a purpose of request_module seems quite stupid.
- * The request_module forks once inside again. However, if we attempt
- * to load a storage module from our own modprobe thread, that module
- * references our symbols, which cannot be resolved until our module is
- * initialized. I wish there was a way to wait for the end of initialization.
- * The module notifier reports MODULE_STATE_COMING only.
- * So, we wait until module->init ends as the next best thing.
- */
-static int usu_probe_thread(void *arg)
-{
- int type = (unsigned long) arg;
- struct mod_status *st = &stat[type];
- int rc;
- unsigned long flags;
-
- mutex_lock(&usu_probe_mutex);
- rc = request_module(bias_names[type]);
- spin_lock_irqsave(&usu_lock, flags);
- if (rc == 0 && (st->fls & USU_MOD_FL_PRESENT) == 0) {
- /*
- * This should not happen, but let us keep tabs on it.
- */
- printk(KERN_NOTICE "libusual: "
- "modprobe for %s succeeded, but module is not present\n",
- bias_names[type]);
- }
- st->fls &= ~USU_MOD_FL_THREAD;
- spin_unlock_irqrestore(&usu_lock, flags);
- mutex_unlock(&usu_probe_mutex);
-
- complete_and_exit(&usu_end_notify, 0);
-}
-
-/*
- */
-static int __init usb_usual_init(void)
-{
- int rc;
-
- mutex_lock(&usu_probe_mutex);
- rc = usb_register(&usu_driver);
- mutex_unlock(&usu_probe_mutex);
- return rc;
-}
-
-static void __exit usb_usual_exit(void)
-{
- /*
- * We do not check for any drivers present, because
- * they keep us pinned with symbol references.
- */
-
- usb_deregister(&usu_driver);
-
- while (atomic_read(&total_threads) > 0) {
- wait_for_completion(&usu_end_notify);
- atomic_dec(&total_threads);
- }
-}
-
-/*
- * Validate and accept the bias parameter.
- */
-static int usu_set_bias(const char *bias_s, struct kernel_param *kp)
-{
- int i;
- int len;
- int bias_n = 0;
-
- len = strlen(bias_s);
- if (len == 0)
- return -EDOM;
- if (bias_s[len-1] == '\n')
- --len;
-
- for (i = 1; i < 3; i++) {
- if (strncmp(bias_s, bias_names[i], len) == 0) {
- bias_n = i;
- break;
- }
- }
- if (bias_n == 0)
- return -EINVAL;
-
- atomic_set(&usu_bias, bias_n);
- return 0;
-}
-
-static int usu_get_bias(char *buffer, struct kernel_param *kp)
-{
- return strlen(strcpy(buffer, bias_names[atomic_read(&usu_bias)]));
-}
-
-module_init(usb_usual_init);
-module_exit(usb_usual_exit);
-
-module_param_call(bias, usu_set_bias, usu_get_bias, NULL, S_IRUGO|S_IWUSR);
-__MODULE_PARM_TYPE(bias, "string");
-MODULE_PARM_DESC(bias, "Bias to usb-storage or ub");
-
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 886567a..cb79de6 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -67,7 +67,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id onetouch_usb_ids[] = {
# include "unusual_onetouch.h"
diff --git a/drivers/usb/storage/realtek_cr.c b/drivers/usb/storage/realtek_cr.c
index 63cf282..d36446d 100644
--- a/drivers/usb/storage/realtek_cr.c
+++ b/drivers/usb/storage/realtek_cr.c
@@ -172,7 +172,7 @@
initFunction, flags) \
{\
USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24)\
+ .driver_info = (flags) \
}
static const struct usb_device_id realtek_cr_ids[] = {
diff --git a/drivers/usb/storage/sddr09.c b/drivers/usb/storage/sddr09.c
index 3252a62..7bd54e0 100644
--- a/drivers/usb/storage/sddr09.c
+++ b/drivers/usb/storage/sddr09.c
@@ -69,7 +69,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id sddr09_usb_ids[] = {
# include "unusual_sddr09.h"
diff --git a/drivers/usb/storage/sddr55.c b/drivers/usb/storage/sddr55.c
index c144078..d278c5a 100644
--- a/drivers/usb/storage/sddr55.c
+++ b/drivers/usb/storage/sddr55.c
@@ -46,7 +46,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id sddr55_usb_ids[] = {
# include "unusual_sddr55.h"
diff --git a/drivers/usb/storage/shuttle_usbat.c b/drivers/usb/storage/shuttle_usbat.c
index fa1ceeb..daf2fc5 100644
--- a/drivers/usb/storage/shuttle_usbat.c
+++ b/drivers/usb/storage/shuttle_usbat.c
@@ -168,7 +168,7 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
+ .driver_info = (flags) }
static struct usb_device_id usbat_usb_ids[] = {
# include "unusual_usbat.h"
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index c70109e..c0543c8 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -1331,7 +1331,7 @@
int result;
/*for these devices we must use the class specific method */
- if (us->pusb_dev->quirks & USB_QUIRK_RESET_MORPHS)
+ if (us->pusb_dev->quirks & USB_QUIRK_RESET)
return -EPERM;
result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index 62a31be..779cd95 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -2038,25 +2038,25 @@
USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
/* Control/Bulk transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_CB, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_CB, USB_US_TYPE_STOR),
+USUAL_DEV(USB_SC_RBC, USB_PR_CB),
+USUAL_DEV(USB_SC_8020, USB_PR_CB),
+USUAL_DEV(USB_SC_QIC, USB_PR_CB),
+USUAL_DEV(USB_SC_UFI, USB_PR_CB),
+USUAL_DEV(USB_SC_8070, USB_PR_CB),
+USUAL_DEV(USB_SC_SCSI, USB_PR_CB),
/* Control/Bulk/Interrupt transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_CBI, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_CBI, USB_US_TYPE_STOR),
+USUAL_DEV(USB_SC_RBC, USB_PR_CBI),
+USUAL_DEV(USB_SC_8020, USB_PR_CBI),
+USUAL_DEV(USB_SC_QIC, USB_PR_CBI),
+USUAL_DEV(USB_SC_UFI, USB_PR_CBI),
+USUAL_DEV(USB_SC_8070, USB_PR_CBI),
+USUAL_DEV(USB_SC_SCSI, USB_PR_CBI),
/* Bulk-only transport for all SubClass values */
-USUAL_DEV(USB_SC_RBC, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8020, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_QIC, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_UFI, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_8070, USB_PR_BULK, USB_US_TYPE_STOR),
-USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0),
+USUAL_DEV(USB_SC_RBC, USB_PR_BULK),
+USUAL_DEV(USB_SC_8020, USB_PR_BULK),
+USUAL_DEV(USB_SC_QIC, USB_PR_BULK),
+USUAL_DEV(USB_SC_UFI, USB_PR_BULK),
+USUAL_DEV(USB_SC_8070, USB_PR_BULK),
+USUAL_DEV(USB_SC_SCSI, USB_PR_BULK),
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index d012fe4..12aa726 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -114,7 +114,7 @@
#define COMPLIANT_DEV UNUSUAL_DEV
-#define USUAL_DEV(use_protocol, use_transport, use_type) \
+#define USUAL_DEV(use_protocol, use_transport) \
{ \
.useProtocol = use_protocol, \
.useTransport = use_transport, \
@@ -126,7 +126,7 @@
};
static struct us_unusual_dev for_dynamic_ids =
- USUAL_DEV(USB_SC_SCSI, USB_PR_BULK, 0);
+ USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);
#undef UNUSUAL_DEV
#undef COMPLIANT_DEV
@@ -564,7 +564,7 @@
us->protocol = (unusual_dev->useTransport == USB_PR_DEVICE) ?
idesc->bInterfaceProtocol :
unusual_dev->useTransport;
- us->fflags = USB_US_ORIG_FLAGS(id->driver_info);
+ us->fflags = id->driver_info;
adjust_quirks(us);
if (us->fflags & US_FL_IGNORE_DEVICE) {
@@ -1041,13 +1041,10 @@
int size;
/*
- * If libusual is configured, let it decide whether a standard
- * device should be handled by usb-storage or by ub.
* If the device isn't standard (is handled by a subdriver
* module) then don't accept it.
*/
- if (usb_usual_check_type(id, USB_US_TYPE_STOR) ||
- usb_usual_ignore_device(intf))
+ if (usb_usual_ignore_device(intf))
return -ENXIO;
/*
@@ -1105,10 +1102,8 @@
/* register the driver, return usb_register return code if error */
retval = usb_register(&usb_storage_driver);
- if (retval == 0) {
+ if (retval == 0)
pr_info("USB Mass Storage support registered.\n");
- usb_usual_set_present(USB_US_TYPE_STOR);
- }
return retval;
}
@@ -1122,8 +1117,6 @@
*/
US_DEBUGP("-- calling usb_deregister()\n");
usb_deregister(&usb_storage_driver) ;
-
- usb_usual_clear_present(USB_US_TYPE_STOR);
}
module_init(usb_stor_init);
diff --git a/drivers/usb/storage/usual-tables.c b/drivers/usb/storage/usual-tables.c
index b969279..b78a526 100644
--- a/drivers/usb/storage/usual-tables.c
+++ b/drivers/usb/storage/usual-tables.c
@@ -34,31 +34,23 @@
vendorName, productName, useProtocol, useTransport, \
initFunction, flags) \
{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
- .driver_info = (flags)|(USB_US_TYPE_STOR<<24) }
-
-#define COMPLIANT_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
- vendorName, productName, useProtocol, useTransport, \
- initFunction, flags) \
-{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
.driver_info = (flags) }
-#define USUAL_DEV(useProto, useTrans, useType) \
-{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans), \
- .driver_info = ((useType)<<24) }
+#define COMPLIANT_DEV UNUSUAL_DEV
+
+#define USUAL_DEV(useProto, useTrans) \
+{ USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, useProto, useTrans) }
struct usb_device_id usb_storage_usb_ids[] = {
# include "unusual_devs.h"
{ } /* Terminating entry */
};
-EXPORT_SYMBOL_GPL(usb_storage_usb_ids);
-
MODULE_DEVICE_TABLE(usb, usb_storage_usb_ids);
#undef UNUSUAL_DEV
#undef COMPLIANT_DEV
#undef USUAL_DEV
-
/*
* The table of devices to ignore
*/
@@ -95,7 +87,6 @@
#undef UNUSUAL_DEV
-
/* Return an error if a device is in the ignore_ids list */
int usb_usual_ignore_device(struct usb_interface *intf)
{
@@ -115,4 +106,3 @@
}
return 0;
}
-EXPORT_SYMBOL_GPL(usb_usual_ignore_device);
diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c
index fa810a8..dd88441 100644
--- a/drivers/usb/wusbcore/security.c
+++ b/drivers/usb/wusbcore/security.c
@@ -202,7 +202,7 @@
{
int result, bytes, secd_size;
struct device *dev = &usb_dev->dev;
- struct usb_security_descriptor *secd;
+ struct usb_security_descriptor *secd, *new_secd;
const struct usb_encryption_descriptor *etd, *ccm1_etd = NULL;
const void *itr, *top;
char buf[64];
@@ -221,11 +221,12 @@
goto out;
}
secd_size = le16_to_cpu(secd->wTotalLength);
- secd = krealloc(secd, secd_size, GFP_KERNEL);
- if (secd == NULL) {
+ new_secd = krealloc(secd, secd_size, GFP_KERNEL);
+ if (new_secd == NULL) {
dev_err(dev, "Can't allocate space for security descriptors\n");
goto out;
}
+ secd = new_secd;
result = usb_get_descriptor(usb_dev, USB_DT_SECURITY,
0, secd, secd_size);
if (result < secd_size) {
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c
index 9e4a924..a09b65e 100644
--- a/drivers/usb/wusbcore/wa-hc.c
+++ b/drivers/usb/wusbcore/wa-hc.c
@@ -46,8 +46,10 @@
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
- if (wa->xfer_result == NULL)
+ if (wa->xfer_result == NULL) {
+ result = -ENOMEM;
goto error_xfer_result_alloc;
+ }
result = wa_nep_create(wa, iface);
if (result < 0) {
dev_err(dev, "WA-CDS: can't initialize notif endpoint: %d\n",
diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
index 9591e2b..17830c9 100644
--- a/drivers/vfio/vfio.c
+++ b/drivers/vfio/vfio.c
@@ -264,6 +264,7 @@
return group;
}
+/* called with vfio.group_lock held */
static void vfio_group_release(struct kref *kref)
{
struct vfio_group *group = container_of(kref, struct vfio_group, kref);
@@ -287,13 +288,7 @@
static void vfio_group_put(struct vfio_group *group)
{
- mutex_lock(&vfio.group_lock);
- /*
- * Release needs to unlock to unregister the notifier, so only
- * unlock if not released.
- */
- if (!kref_put(&group->kref, vfio_group_release))
- mutex_unlock(&vfio.group_lock);
+ kref_put_mutex(&group->kref, vfio_group_release, &vfio.group_lock);
}
/* Assume group_lock or group reference is held */
@@ -401,7 +396,6 @@
struct vfio_device, kref);
struct vfio_group *group = device->group;
- mutex_lock(&group->device_lock);
list_del(&device->group_next);
mutex_unlock(&group->device_lock);
@@ -416,8 +410,9 @@
/* Device reference always implies a group reference */
static void vfio_device_put(struct vfio_device *device)
{
- kref_put(&device->kref, vfio_device_release);
- vfio_group_put(device->group);
+ struct vfio_group *group = device->group;
+ kref_put_mutex(&device->kref, vfio_device_release, &group->device_lock);
+ vfio_group_put(group);
}
static void vfio_device_get(struct vfio_device *device)
@@ -1116,10 +1111,10 @@
*/
filep->f_mode |= (FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE);
- fd_install(ret, filep);
-
vfio_device_get(device);
atomic_inc(&group->container_users);
+
+ fd_install(ret, filep);
break;
}
mutex_unlock(&group->device_lock);
diff --git a/drivers/vhost/Kconfig b/drivers/vhost/Kconfig
index e4e2fd1..202bba6 100644
--- a/drivers/vhost/Kconfig
+++ b/drivers/vhost/Kconfig
@@ -9,3 +9,6 @@
To compile this driver as a module, choose M here: the module will
be called vhost_net.
+if STAGING
+source "drivers/vhost/Kconfig.tcm"
+endif
diff --git a/drivers/vhost/Kconfig.tcm b/drivers/vhost/Kconfig.tcm
new file mode 100644
index 0000000..a9c6f76
--- /dev/null
+++ b/drivers/vhost/Kconfig.tcm
@@ -0,0 +1,6 @@
+config TCM_VHOST
+ tristate "TCM_VHOST fabric module (EXPERIMENTAL)"
+ depends on TARGET_CORE && EVENTFD && EXPERIMENTAL && m
+ default n
+ ---help---
+ Say M here to enable the TCM_VHOST fabric module for use with virtio-scsi guests
diff --git a/drivers/vhost/Makefile b/drivers/vhost/Makefile
index 72dd020..a27b053 100644
--- a/drivers/vhost/Makefile
+++ b/drivers/vhost/Makefile
@@ -1,2 +1,4 @@
obj-$(CONFIG_VHOST_NET) += vhost_net.o
vhost_net-y := vhost.o net.o
+
+obj-$(CONFIG_TCM_VHOST) += tcm_vhost.o
diff --git a/drivers/vhost/tcm_vhost.c b/drivers/vhost/tcm_vhost.c
new file mode 100644
index 0000000..ed8e2e6
--- /dev/null
+++ b/drivers/vhost/tcm_vhost.c
@@ -0,0 +1,1649 @@
+/*******************************************************************************
+ * Vhost kernel TCM fabric driver for virtio SCSI initiators
+ *
+ * (C) Copyright 2010-2012 RisingTide Systems LLC.
+ * (C) Copyright 2010-2012 IBM Corp.
+ *
+ * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
+ *
+ * Authors: Nicholas A. Bellinger <nab@risingtidesystems.com>
+ * Stefan Hajnoczi <stefanha@linux.vnet.ibm.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.
+ *
+ ****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <generated/utsrelease.h>
+#include <linux/utsname.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/kthread.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/configfs.h>
+#include <linux/ctype.h>
+#include <linux/compat.h>
+#include <linux/eventfd.h>
+#include <linux/vhost.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <asm/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_tcq.h>
+#include <target/target_core_base.h>
+#include <target/target_core_fabric.h>
+#include <target/target_core_fabric_configfs.h>
+#include <target/target_core_configfs.h>
+#include <target/configfs_macros.h>
+#include <linux/vhost.h>
+#include <linux/virtio_net.h> /* TODO vhost.h currently depends on this */
+#include <linux/virtio_scsi.h>
+
+#include "vhost.c"
+#include "vhost.h"
+#include "tcm_vhost.h"
+
+enum {
+ VHOST_SCSI_VQ_CTL = 0,
+ VHOST_SCSI_VQ_EVT = 1,
+ VHOST_SCSI_VQ_IO = 2,
+};
+
+struct vhost_scsi {
+ struct tcm_vhost_tpg *vs_tpg; /* Protected by vhost_scsi->dev.mutex */
+ struct vhost_dev dev;
+ struct vhost_virtqueue vqs[3];
+
+ struct vhost_work vs_completion_work; /* cmd completion work item */
+ struct list_head vs_completion_list; /* cmd completion queue */
+ spinlock_t vs_completion_lock; /* protects s_completion_list */
+};
+
+/* Local pointer to allocated TCM configfs fabric module */
+static struct target_fabric_configfs *tcm_vhost_fabric_configfs;
+
+static struct workqueue_struct *tcm_vhost_workqueue;
+
+/* Global spinlock to protect tcm_vhost TPG list for vhost IOCTL access */
+static DEFINE_MUTEX(tcm_vhost_mutex);
+static LIST_HEAD(tcm_vhost_list);
+
+static int tcm_vhost_check_true(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static int tcm_vhost_check_false(struct se_portal_group *se_tpg)
+{
+ return 0;
+}
+
+static char *tcm_vhost_get_fabric_name(void)
+{
+ return "vhost";
+}
+
+static u8 tcm_vhost_get_fabric_proto_ident(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_fabric_proto_ident(se_tpg);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_fabric_proto_ident(se_tpg);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_fabric_proto_ident(se_tpg);
+}
+
+static char *tcm_vhost_get_fabric_wwn(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ return &tport->tport_name[0];
+}
+
+static u16 tcm_vhost_get_tag(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ return tpg->tport_tpgt;
+}
+
+static u32 tcm_vhost_get_default_depth(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static u32 tcm_vhost_get_pr_transport_id(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code,
+ unsigned char *buf)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id(se_tpg, se_nacl, pr_reg,
+ format_code, buf);
+}
+
+static u32 tcm_vhost_get_pr_transport_id_len(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl,
+ struct t10_pr_registration *pr_reg,
+ int *format_code)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_FCP:
+ return fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg,
+ format_code);
+}
+
+static char *tcm_vhost_parse_pr_out_transport_id(struct se_portal_group *se_tpg,
+ const char *buf,
+ u32 *out_tid_len,
+ char **port_nexus_ptr)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport = tpg->tport;
+
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_FCP:
+ return fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ case SCSI_PROTOCOL_ISCSI:
+ return iscsi_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+ default:
+ pr_err("Unknown tport_proto_id: 0x%02x, using"
+ " SAS emulation\n", tport->tport_proto_id);
+ break;
+ }
+
+ return sas_parse_pr_out_transport_id(se_tpg, buf, out_tid_len,
+ port_nexus_ptr);
+}
+
+static struct se_node_acl *tcm_vhost_alloc_fabric_acl(
+ struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_nacl *nacl;
+
+ nacl = kzalloc(sizeof(struct tcm_vhost_nacl), GFP_KERNEL);
+ if (!nacl) {
+ pr_err("Unable to alocate struct tcm_vhost_nacl\n");
+ return NULL;
+ }
+
+ return &nacl->se_node_acl;
+}
+
+static void tcm_vhost_release_fabric_acl(struct se_portal_group *se_tpg,
+ struct se_node_acl *se_nacl)
+{
+ struct tcm_vhost_nacl *nacl = container_of(se_nacl,
+ struct tcm_vhost_nacl, se_node_acl);
+ kfree(nacl);
+}
+
+static u32 tcm_vhost_tpg_get_inst_index(struct se_portal_group *se_tpg)
+{
+ return 1;
+}
+
+static void tcm_vhost_release_cmd(struct se_cmd *se_cmd)
+{
+ return;
+}
+
+static int tcm_vhost_shutdown_session(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static void tcm_vhost_close_session(struct se_session *se_sess)
+{
+ return;
+}
+
+static u32 tcm_vhost_sess_get_index(struct se_session *se_sess)
+{
+ return 0;
+}
+
+static int tcm_vhost_write_pending(struct se_cmd *se_cmd)
+{
+ /* Go ahead and process the write immediately */
+ target_execute_cmd(se_cmd);
+ return 0;
+}
+
+static int tcm_vhost_write_pending_status(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void tcm_vhost_set_default_node_attrs(struct se_node_acl *nacl)
+{
+ return;
+}
+
+static u32 tcm_vhost_get_task_tag(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static int tcm_vhost_get_cmd_state(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static void vhost_scsi_complete_cmd(struct tcm_vhost_cmd *tv_cmd)
+{
+ struct vhost_scsi *vs = tv_cmd->tvc_vhost;
+
+ spin_lock_bh(&vs->vs_completion_lock);
+ list_add_tail(&tv_cmd->tvc_completion_list, &vs->vs_completion_list);
+ spin_unlock_bh(&vs->vs_completion_lock);
+
+ vhost_work_queue(&vs->dev, &vs->vs_completion_work);
+}
+
+static int tcm_vhost_queue_data_in(struct se_cmd *se_cmd)
+{
+ struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd, tvc_se_cmd);
+ vhost_scsi_complete_cmd(tv_cmd);
+ return 0;
+}
+
+static int tcm_vhost_queue_status(struct se_cmd *se_cmd)
+{
+ struct tcm_vhost_cmd *tv_cmd = container_of(se_cmd,
+ struct tcm_vhost_cmd, tvc_se_cmd);
+ vhost_scsi_complete_cmd(tv_cmd);
+ return 0;
+}
+
+static int tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd)
+{
+ return 0;
+}
+
+static u16 tcm_vhost_set_fabric_sense_len(struct se_cmd *se_cmd,
+ u32 sense_length)
+{
+ return 0;
+}
+
+static u16 tcm_vhost_get_fabric_sense_len(void)
+{
+ return 0;
+}
+
+static void vhost_scsi_free_cmd(struct tcm_vhost_cmd *tv_cmd)
+{
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+
+ /* TODO locking against target/backend threads? */
+ transport_generic_free_cmd(se_cmd, 1);
+
+ if (tv_cmd->tvc_sgl_count) {
+ u32 i;
+ for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&tv_cmd->tvc_sgl[i]));
+
+ kfree(tv_cmd->tvc_sgl);
+ }
+
+ kfree(tv_cmd);
+}
+
+/* Dequeue a command from the completion list */
+static struct tcm_vhost_cmd *vhost_scsi_get_cmd_from_completion(
+ struct vhost_scsi *vs)
+{
+ struct tcm_vhost_cmd *tv_cmd = NULL;
+
+ spin_lock_bh(&vs->vs_completion_lock);
+ if (list_empty(&vs->vs_completion_list)) {
+ spin_unlock_bh(&vs->vs_completion_lock);
+ return NULL;
+ }
+
+ list_for_each_entry(tv_cmd, &vs->vs_completion_list,
+ tvc_completion_list) {
+ list_del(&tv_cmd->tvc_completion_list);
+ break;
+ }
+ spin_unlock_bh(&vs->vs_completion_lock);
+ return tv_cmd;
+}
+
+/* Fill in status and signal that we are done processing this command
+ *
+ * This is scheduled in the vhost work queue so we are called with the owner
+ * process mm and can access the vring.
+ */
+static void vhost_scsi_complete_cmd_work(struct vhost_work *work)
+{
+ struct vhost_scsi *vs = container_of(work, struct vhost_scsi,
+ vs_completion_work);
+ struct tcm_vhost_cmd *tv_cmd;
+
+ while ((tv_cmd = vhost_scsi_get_cmd_from_completion(vs))) {
+ struct virtio_scsi_cmd_resp v_rsp;
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ int ret;
+
+ pr_debug("%s tv_cmd %p resid %u status %#02x\n", __func__,
+ tv_cmd, se_cmd->residual_count, se_cmd->scsi_status);
+
+ memset(&v_rsp, 0, sizeof(v_rsp));
+ v_rsp.resid = se_cmd->residual_count;
+ /* TODO is status_qualifier field needed? */
+ v_rsp.status = se_cmd->scsi_status;
+ v_rsp.sense_len = se_cmd->scsi_sense_length;
+ memcpy(v_rsp.sense, tv_cmd->tvc_sense_buf,
+ v_rsp.sense_len);
+ ret = copy_to_user(tv_cmd->tvc_resp, &v_rsp, sizeof(v_rsp));
+ if (likely(ret == 0))
+ vhost_add_used(&vs->vqs[2], tv_cmd->tvc_vq_desc, 0);
+ else
+ pr_err("Faulted on virtio_scsi_cmd_resp\n");
+
+ vhost_scsi_free_cmd(tv_cmd);
+ }
+
+ vhost_signal(&vs->dev, &vs->vqs[2]);
+}
+
+static struct tcm_vhost_cmd *vhost_scsi_allocate_cmd(
+ struct tcm_vhost_tpg *tv_tpg,
+ struct virtio_scsi_cmd_req *v_req,
+ u32 exp_data_len,
+ int data_direction)
+{
+ struct tcm_vhost_cmd *tv_cmd;
+ struct tcm_vhost_nexus *tv_nexus;
+ struct se_portal_group *se_tpg = &tv_tpg->se_tpg;
+ struct se_session *se_sess;
+ struct se_cmd *se_cmd;
+ int sam_task_attr;
+
+ tv_nexus = tv_tpg->tpg_nexus;
+ if (!tv_nexus) {
+ pr_err("Unable to locate active struct tcm_vhost_nexus\n");
+ return ERR_PTR(-EIO);
+ }
+ se_sess = tv_nexus->tvn_se_sess;
+
+ tv_cmd = kzalloc(sizeof(struct tcm_vhost_cmd), GFP_ATOMIC);
+ if (!tv_cmd) {
+ pr_err("Unable to allocate struct tcm_vhost_cmd\n");
+ return ERR_PTR(-ENOMEM);
+ }
+ INIT_LIST_HEAD(&tv_cmd->tvc_completion_list);
+ tv_cmd->tvc_tag = v_req->tag;
+
+ se_cmd = &tv_cmd->tvc_se_cmd;
+ /*
+ * Locate the SAM Task Attr from virtio_scsi_cmd_req
+ */
+ sam_task_attr = v_req->task_attr;
+ /*
+ * Initialize struct se_cmd descriptor from TCM infrastructure
+ */
+ transport_init_se_cmd(se_cmd, se_tpg->se_tpg_tfo, se_sess, exp_data_len,
+ data_direction, sam_task_attr,
+ &tv_cmd->tvc_sense_buf[0]);
+
+#if 0 /* FIXME: vhost_scsi_allocate_cmd() BIDI operation */
+ if (bidi)
+ se_cmd->se_cmd_flags |= SCF_BIDI;
+#endif
+ return tv_cmd;
+}
+
+/*
+ * Map a user memory range into a scatterlist
+ *
+ * Returns the number of scatterlist entries used or -errno on error.
+ */
+static int vhost_scsi_map_to_sgl(struct scatterlist *sgl,
+ unsigned int sgl_count, void __user *ptr, size_t len, int write)
+{
+ struct scatterlist *sg = sgl;
+ unsigned int npages = 0;
+ int ret;
+
+ while (len > 0) {
+ struct page *page;
+ unsigned int offset = (uintptr_t)ptr & ~PAGE_MASK;
+ unsigned int nbytes = min_t(unsigned int,
+ PAGE_SIZE - offset, len);
+
+ if (npages == sgl_count) {
+ ret = -ENOBUFS;
+ goto err;
+ }
+
+ ret = get_user_pages_fast((unsigned long)ptr, 1, write, &page);
+ BUG_ON(ret == 0); /* we should either get our page or fail */
+ if (ret < 0)
+ goto err;
+
+ sg_set_page(sg, page, nbytes, offset);
+ ptr += nbytes;
+ len -= nbytes;
+ sg++;
+ npages++;
+ }
+ return npages;
+
+err:
+ /* Put pages that we hold */
+ for (sg = sgl; sg != &sgl[npages]; sg++)
+ put_page(sg_page(sg));
+ return ret;
+}
+
+static int vhost_scsi_map_iov_to_sgl(struct tcm_vhost_cmd *tv_cmd,
+ struct iovec *iov, unsigned int niov, int write)
+{
+ int ret;
+ unsigned int i;
+ u32 sgl_count;
+ struct scatterlist *sg;
+
+ /*
+ * Find out how long sglist needs to be
+ */
+ sgl_count = 0;
+ for (i = 0; i < niov; i++) {
+ sgl_count += (((uintptr_t)iov[i].iov_base + iov[i].iov_len +
+ PAGE_SIZE - 1) >> PAGE_SHIFT) -
+ ((uintptr_t)iov[i].iov_base >> PAGE_SHIFT);
+ }
+ /* TODO overflow checking */
+
+ sg = kmalloc(sizeof(tv_cmd->tvc_sgl[0]) * sgl_count, GFP_ATOMIC);
+ if (!sg)
+ return -ENOMEM;
+ pr_debug("%s sg %p sgl_count %u is_err %d\n", __func__,
+ sg, sgl_count, !sg);
+ sg_init_table(sg, sgl_count);
+
+ tv_cmd->tvc_sgl = sg;
+ tv_cmd->tvc_sgl_count = sgl_count;
+
+ pr_debug("Mapping %u iovecs for %u pages\n", niov, sgl_count);
+ for (i = 0; i < niov; i++) {
+ ret = vhost_scsi_map_to_sgl(sg, sgl_count, iov[i].iov_base,
+ iov[i].iov_len, write);
+ if (ret < 0) {
+ for (i = 0; i < tv_cmd->tvc_sgl_count; i++)
+ put_page(sg_page(&tv_cmd->tvc_sgl[i]));
+ kfree(tv_cmd->tvc_sgl);
+ tv_cmd->tvc_sgl = NULL;
+ tv_cmd->tvc_sgl_count = 0;
+ return ret;
+ }
+
+ sg += ret;
+ sgl_count -= ret;
+ }
+ return 0;
+}
+
+static void tcm_vhost_submission_work(struct work_struct *work)
+{
+ struct tcm_vhost_cmd *tv_cmd =
+ container_of(work, struct tcm_vhost_cmd, work);
+ struct se_cmd *se_cmd = &tv_cmd->tvc_se_cmd;
+ struct scatterlist *sg_ptr, *sg_bidi_ptr = NULL;
+ int rc, sg_no_bidi = 0;
+ /*
+ * Locate the struct se_lun pointer based on v_req->lun, and
+ * attach it to struct se_cmd
+ */
+ rc = transport_lookup_cmd_lun(&tv_cmd->tvc_se_cmd, tv_cmd->tvc_lun);
+ if (rc < 0) {
+ pr_err("Failed to look up lun: %d\n", tv_cmd->tvc_lun);
+ transport_send_check_condition_and_sense(&tv_cmd->tvc_se_cmd,
+ tv_cmd->tvc_se_cmd.scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ rc = target_setup_cmd_from_cdb(se_cmd, tv_cmd->tvc_cdb);
+ if (rc == -ENOMEM) {
+ transport_send_check_condition_and_sense(se_cmd,
+ TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ } else if (rc < 0) {
+ if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT)
+ tcm_vhost_queue_status(se_cmd);
+ else
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+
+ if (tv_cmd->tvc_sgl_count) {
+ sg_ptr = tv_cmd->tvc_sgl;
+ /*
+ * For BIDI commands, pass in the extra READ buffer
+ * to transport_generic_map_mem_to_cmd() below..
+ */
+/* FIXME: Fix BIDI operation in tcm_vhost_submission_work() */
+#if 0
+ if (se_cmd->se_cmd_flags & SCF_BIDI) {
+ sg_bidi_ptr = NULL;
+ sg_no_bidi = 0;
+ }
+#endif
+ } else {
+ sg_ptr = NULL;
+ }
+
+ rc = transport_generic_map_mem_to_cmd(se_cmd, sg_ptr,
+ tv_cmd->tvc_sgl_count, sg_bidi_ptr,
+ sg_no_bidi);
+ if (rc < 0) {
+ transport_send_check_condition_and_sense(se_cmd,
+ se_cmd->scsi_sense_reason, 0);
+ transport_generic_free_cmd(se_cmd, 0);
+ return;
+ }
+ transport_handle_cdb_direct(se_cmd);
+}
+
+static void vhost_scsi_handle_vq(struct vhost_scsi *vs)
+{
+ struct vhost_virtqueue *vq = &vs->vqs[2];
+ struct virtio_scsi_cmd_req v_req;
+ struct tcm_vhost_tpg *tv_tpg;
+ struct tcm_vhost_cmd *tv_cmd;
+ u32 exp_data_len, data_first, data_num, data_direction;
+ unsigned out, in, i;
+ int head, ret;
+
+ /* Must use ioctl VHOST_SCSI_SET_ENDPOINT */
+ tv_tpg = vs->vs_tpg;
+ if (unlikely(!tv_tpg)) {
+ pr_err("%s endpoint not set\n", __func__);
+ return;
+ }
+
+ mutex_lock(&vq->mutex);
+ vhost_disable_notify(&vs->dev, vq);
+
+ for (;;) {
+ head = vhost_get_vq_desc(&vs->dev, vq, vq->iov,
+ ARRAY_SIZE(vq->iov), &out, &in,
+ NULL, NULL);
+ pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n",
+ head, out, in);
+ /* On error, stop handling until the next kick. */
+ if (unlikely(head < 0))
+ break;
+ /* Nothing new? Wait for eventfd to tell us they refilled. */
+ if (head == vq->num) {
+ if (unlikely(vhost_enable_notify(&vs->dev, vq))) {
+ vhost_disable_notify(&vs->dev, vq);
+ continue;
+ }
+ break;
+ }
+
+/* FIXME: BIDI operation */
+ if (out == 1 && in == 1) {
+ data_direction = DMA_NONE;
+ data_first = 0;
+ data_num = 0;
+ } else if (out == 1 && in > 1) {
+ data_direction = DMA_FROM_DEVICE;
+ data_first = out + 1;
+ data_num = in - 1;
+ } else if (out > 1 && in == 1) {
+ data_direction = DMA_TO_DEVICE;
+ data_first = 1;
+ data_num = out - 1;
+ } else {
+ vq_err(vq, "Invalid buffer layout out: %u in: %u\n",
+ out, in);
+ break;
+ }
+
+ /*
+ * Check for a sane resp buffer so we can report errors to
+ * the guest.
+ */
+ if (unlikely(vq->iov[out].iov_len !=
+ sizeof(struct virtio_scsi_cmd_resp))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+ " bytes\n", vq->iov[out].iov_len);
+ break;
+ }
+
+ if (unlikely(vq->iov[0].iov_len != sizeof(v_req))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_req, got %zu"
+ " bytes\n", vq->iov[0].iov_len);
+ break;
+ }
+ pr_debug("Calling __copy_from_user: vq->iov[0].iov_base: %p,"
+ " len: %zu\n", vq->iov[0].iov_base, sizeof(v_req));
+ ret = __copy_from_user(&v_req, vq->iov[0].iov_base,
+ sizeof(v_req));
+ if (unlikely(ret)) {
+ vq_err(vq, "Faulted on virtio_scsi_cmd_req\n");
+ break;
+ }
+
+ exp_data_len = 0;
+ for (i = 0; i < data_num; i++)
+ exp_data_len += vq->iov[data_first + i].iov_len;
+
+ tv_cmd = vhost_scsi_allocate_cmd(tv_tpg, &v_req,
+ exp_data_len, data_direction);
+ if (IS_ERR(tv_cmd)) {
+ vq_err(vq, "vhost_scsi_allocate_cmd failed %ld\n",
+ PTR_ERR(tv_cmd));
+ break;
+ }
+ pr_debug("Allocated tv_cmd: %p exp_data_len: %d, data_direction"
+ ": %d\n", tv_cmd, exp_data_len, data_direction);
+
+ tv_cmd->tvc_vhost = vs;
+
+ if (unlikely(vq->iov[out].iov_len !=
+ sizeof(struct virtio_scsi_cmd_resp))) {
+ vq_err(vq, "Expecting virtio_scsi_cmd_resp, got %zu"
+ " bytes, out: %d, in: %d\n",
+ vq->iov[out].iov_len, out, in);
+ break;
+ }
+
+ tv_cmd->tvc_resp = vq->iov[out].iov_base;
+
+ /*
+ * Copy in the recieved CDB descriptor into tv_cmd->tvc_cdb
+ * that will be used by tcm_vhost_new_cmd_map() and down into
+ * target_setup_cmd_from_cdb()
+ */
+ memcpy(tv_cmd->tvc_cdb, v_req.cdb, TCM_VHOST_MAX_CDB_SIZE);
+ /*
+ * Check that the recieved CDB size does not exceeded our
+ * hardcoded max for tcm_vhost
+ */
+ /* TODO what if cdb was too small for varlen cdb header? */
+ if (unlikely(scsi_command_size(tv_cmd->tvc_cdb) >
+ TCM_VHOST_MAX_CDB_SIZE)) {
+ vq_err(vq, "Received SCSI CDB with command_size: %d that"
+ " exceeds SCSI_MAX_VARLEN_CDB_SIZE: %d\n",
+ scsi_command_size(tv_cmd->tvc_cdb),
+ TCM_VHOST_MAX_CDB_SIZE);
+ break; /* TODO */
+ }
+ tv_cmd->tvc_lun = ((v_req.lun[2] << 8) | v_req.lun[3]) & 0x3FFF;
+
+ pr_debug("vhost_scsi got command opcode: %#02x, lun: %d\n",
+ tv_cmd->tvc_cdb[0], tv_cmd->tvc_lun);
+
+ if (data_direction != DMA_NONE) {
+ ret = vhost_scsi_map_iov_to_sgl(tv_cmd,
+ &vq->iov[data_first], data_num,
+ data_direction == DMA_TO_DEVICE);
+ if (unlikely(ret)) {
+ vq_err(vq, "Failed to map iov to sgl\n");
+ break; /* TODO */
+ }
+ }
+
+ /*
+ * Save the descriptor from vhost_get_vq_desc() to be used to
+ * complete the virtio-scsi request in TCM callback context via
+ * tcm_vhost_queue_data_in() and tcm_vhost_queue_status()
+ */
+ tv_cmd->tvc_vq_desc = head;
+ /*
+ * Dispatch tv_cmd descriptor for cmwq execution in process
+ * context provided by tcm_vhost_workqueue. This also ensures
+ * tv_cmd is executed on the same kworker CPU as this vhost
+ * thread to gain positive L2 cache locality effects..
+ */
+ INIT_WORK(&tv_cmd->work, tcm_vhost_submission_work);
+ queue_work(tcm_vhost_workqueue, &tv_cmd->work);
+ }
+
+ mutex_unlock(&vq->mutex);
+}
+
+static void vhost_scsi_ctl_handle_kick(struct vhost_work *work)
+{
+ pr_debug("%s: The handling func for control queue.\n", __func__);
+}
+
+static void vhost_scsi_evt_handle_kick(struct vhost_work *work)
+{
+ pr_debug("%s: The handling func for event queue.\n", __func__);
+}
+
+static void vhost_scsi_handle_kick(struct vhost_work *work)
+{
+ struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue,
+ poll.work);
+ struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev);
+
+ vhost_scsi_handle_vq(vs);
+}
+
+/*
+ * Called from vhost_scsi_ioctl() context to walk the list of available
+ * tcm_vhost_tpg with an active struct tcm_vhost_nexus
+ */
+static int vhost_scsi_set_endpoint(
+ struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
+{
+ struct tcm_vhost_tport *tv_tport;
+ struct tcm_vhost_tpg *tv_tpg;
+ int index;
+
+ mutex_lock(&vs->dev.mutex);
+ /* Verify that ring has been setup correctly. */
+ for (index = 0; index < vs->dev.nvqs; ++index) {
+ /* Verify that ring has been setup correctly. */
+ if (!vhost_vq_access_ok(&vs->vqs[index])) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EFAULT;
+ }
+ }
+ mutex_unlock(&vs->dev.mutex);
+
+ mutex_lock(&tcm_vhost_mutex);
+ list_for_each_entry(tv_tpg, &tcm_vhost_list, tv_tpg_list) {
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ if (!tv_tpg->tpg_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ continue;
+ }
+ if (tv_tpg->tv_tpg_vhost_count != 0) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ continue;
+ }
+ tv_tport = tv_tpg->tport;
+
+ if (!strcmp(tv_tport->tport_name, t->vhost_wwpn) &&
+ (tv_tpg->tport_tpgt == t->vhost_tpgt)) {
+ tv_tpg->tv_tpg_vhost_count++;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ mutex_unlock(&tcm_vhost_mutex);
+
+ mutex_lock(&vs->dev.mutex);
+ if (vs->vs_tpg) {
+ mutex_unlock(&vs->dev.mutex);
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_vhost_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return -EEXIST;
+ }
+
+ vs->vs_tpg = tv_tpg;
+ smp_mb__after_atomic_inc();
+ mutex_unlock(&vs->dev.mutex);
+ return 0;
+ }
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ }
+ mutex_unlock(&tcm_vhost_mutex);
+ return -EINVAL;
+}
+
+static int vhost_scsi_clear_endpoint(
+ struct vhost_scsi *vs,
+ struct vhost_scsi_target *t)
+{
+ struct tcm_vhost_tport *tv_tport;
+ struct tcm_vhost_tpg *tv_tpg;
+ int index, ret;
+
+ mutex_lock(&vs->dev.mutex);
+ /* Verify that ring has been setup correctly. */
+ for (index = 0; index < vs->dev.nvqs; ++index) {
+ if (!vhost_vq_access_ok(&vs->vqs[index])) {
+ ret = -EFAULT;
+ goto err;
+ }
+ }
+
+ if (!vs->vs_tpg) {
+ ret = -ENODEV;
+ goto err;
+ }
+ tv_tpg = vs->vs_tpg;
+ tv_tport = tv_tpg->tport;
+
+ if (strcmp(tv_tport->tport_name, t->vhost_wwpn) ||
+ (tv_tpg->tport_tpgt != t->vhost_tpgt)) {
+ pr_warn("tv_tport->tport_name: %s, tv_tpg->tport_tpgt: %hu"
+ " does not match t->vhost_wwpn: %s, t->vhost_tpgt: %hu\n",
+ tv_tport->tport_name, tv_tpg->tport_tpgt,
+ t->vhost_wwpn, t->vhost_tpgt);
+ ret = -EINVAL;
+ goto err;
+ }
+ tv_tpg->tv_tpg_vhost_count--;
+ vs->vs_tpg = NULL;
+ mutex_unlock(&vs->dev.mutex);
+
+ return 0;
+
+err:
+ mutex_unlock(&vs->dev.mutex);
+ return ret;
+}
+
+static int vhost_scsi_open(struct inode *inode, struct file *f)
+{
+ struct vhost_scsi *s;
+ int r;
+
+ s = kzalloc(sizeof(*s), GFP_KERNEL);
+ if (!s)
+ return -ENOMEM;
+
+ vhost_work_init(&s->vs_completion_work, vhost_scsi_complete_cmd_work);
+ INIT_LIST_HEAD(&s->vs_completion_list);
+ spin_lock_init(&s->vs_completion_lock);
+
+ s->vqs[VHOST_SCSI_VQ_CTL].handle_kick = vhost_scsi_ctl_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_EVT].handle_kick = vhost_scsi_evt_handle_kick;
+ s->vqs[VHOST_SCSI_VQ_IO].handle_kick = vhost_scsi_handle_kick;
+ r = vhost_dev_init(&s->dev, s->vqs, 3);
+ if (r < 0) {
+ kfree(s);
+ return r;
+ }
+
+ f->private_data = s;
+ return 0;
+}
+
+static int vhost_scsi_release(struct inode *inode, struct file *f)
+{
+ struct vhost_scsi *s = f->private_data;
+
+ if (s->vs_tpg && s->vs_tpg->tport) {
+ struct vhost_scsi_target backend;
+
+ memcpy(backend.vhost_wwpn, s->vs_tpg->tport->tport_name,
+ sizeof(backend.vhost_wwpn));
+ backend.vhost_tpgt = s->vs_tpg->tport_tpgt;
+ vhost_scsi_clear_endpoint(s, &backend);
+ }
+
+ vhost_dev_cleanup(&s->dev, false);
+ kfree(s);
+ return 0;
+}
+
+static void vhost_scsi_flush_vq(struct vhost_scsi *vs, int index)
+{
+ vhost_poll_flush(&vs->dev.vqs[index].poll);
+}
+
+static void vhost_scsi_flush(struct vhost_scsi *vs)
+{
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_CTL);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_EVT);
+ vhost_scsi_flush_vq(vs, VHOST_SCSI_VQ_IO);
+}
+
+static int vhost_scsi_set_features(struct vhost_scsi *vs, u64 features)
+{
+ if (features & ~VHOST_FEATURES)
+ return -EOPNOTSUPP;
+
+ mutex_lock(&vs->dev.mutex);
+ if ((features & (1 << VHOST_F_LOG_ALL)) &&
+ !vhost_log_access_ok(&vs->dev)) {
+ mutex_unlock(&vs->dev.mutex);
+ return -EFAULT;
+ }
+ vs->dev.acked_features = features;
+ smp_wmb();
+ vhost_scsi_flush(vs);
+ mutex_unlock(&vs->dev.mutex);
+ return 0;
+}
+
+static long vhost_scsi_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ struct vhost_scsi *vs = f->private_data;
+ struct vhost_scsi_target backend;
+ void __user *argp = (void __user *)arg;
+ u64 __user *featurep = argp;
+ u64 features;
+ int r, abi_version = VHOST_SCSI_ABI_VERSION;
+
+ switch (ioctl) {
+ case VHOST_SCSI_SET_ENDPOINT:
+ if (copy_from_user(&backend, argp, sizeof backend))
+ return -EFAULT;
+ if (backend.reserved != 0)
+ return -EOPNOTSUPP;
+
+ return vhost_scsi_set_endpoint(vs, &backend);
+ case VHOST_SCSI_CLEAR_ENDPOINT:
+ if (copy_from_user(&backend, argp, sizeof backend))
+ return -EFAULT;
+ if (backend.reserved != 0)
+ return -EOPNOTSUPP;
+
+ return vhost_scsi_clear_endpoint(vs, &backend);
+ case VHOST_SCSI_GET_ABI_VERSION:
+ if (copy_to_user(argp, &abi_version, sizeof abi_version))
+ return -EFAULT;
+ return 0;
+ case VHOST_GET_FEATURES:
+ features = VHOST_FEATURES;
+ if (copy_to_user(featurep, &features, sizeof features))
+ return -EFAULT;
+ return 0;
+ case VHOST_SET_FEATURES:
+ if (copy_from_user(&features, featurep, sizeof features))
+ return -EFAULT;
+ return vhost_scsi_set_features(vs, features);
+ default:
+ mutex_lock(&vs->dev.mutex);
+ r = vhost_dev_ioctl(&vs->dev, ioctl, arg);
+ mutex_unlock(&vs->dev.mutex);
+ return r;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static long vhost_scsi_compat_ioctl(struct file *f, unsigned int ioctl,
+ unsigned long arg)
+{
+ return vhost_scsi_ioctl(f, ioctl, (unsigned long)compat_ptr(arg));
+}
+#endif
+
+static const struct file_operations vhost_scsi_fops = {
+ .owner = THIS_MODULE,
+ .release = vhost_scsi_release,
+ .unlocked_ioctl = vhost_scsi_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = vhost_scsi_compat_ioctl,
+#endif
+ .open = vhost_scsi_open,
+ .llseek = noop_llseek,
+};
+
+static struct miscdevice vhost_scsi_misc = {
+ MISC_DYNAMIC_MINOR,
+ "vhost-scsi",
+ &vhost_scsi_fops,
+};
+
+static int __init vhost_scsi_register(void)
+{
+ return misc_register(&vhost_scsi_misc);
+}
+
+static int vhost_scsi_deregister(void)
+{
+ return misc_deregister(&vhost_scsi_misc);
+}
+
+static char *tcm_vhost_dump_proto_id(struct tcm_vhost_tport *tport)
+{
+ switch (tport->tport_proto_id) {
+ case SCSI_PROTOCOL_SAS:
+ return "SAS";
+ case SCSI_PROTOCOL_FCP:
+ return "FCP";
+ case SCSI_PROTOCOL_ISCSI:
+ return "iSCSI";
+ default:
+ break;
+ }
+
+ return "Unknown";
+}
+
+static int tcm_vhost_port_link(struct se_portal_group *se_tpg,
+ struct se_lun *lun)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count++;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+
+ return 0;
+}
+
+static void tcm_vhost_port_unlink(struct se_portal_group *se_tpg,
+ struct se_lun *se_lun)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_tpg->tv_tpg_port_count--;
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+}
+
+static struct se_node_acl *tcm_vhost_make_nodeacl(
+ struct se_portal_group *se_tpg,
+ struct config_group *group,
+ const char *name)
+{
+ struct se_node_acl *se_nacl, *se_nacl_new;
+ struct tcm_vhost_nacl *nacl;
+ u64 wwpn = 0;
+ u32 nexus_depth;
+
+ /* tcm_vhost_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL); */
+ se_nacl_new = tcm_vhost_alloc_fabric_acl(se_tpg);
+ if (!se_nacl_new)
+ return ERR_PTR(-ENOMEM);
+
+ nexus_depth = 1;
+ /*
+ * se_nacl_new may be released by core_tpg_add_initiator_node_acl()
+ * when converting a NodeACL from demo mode -> explict
+ */
+ se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new,
+ name, nexus_depth);
+ if (IS_ERR(se_nacl)) {
+ tcm_vhost_release_fabric_acl(se_tpg, se_nacl_new);
+ return se_nacl;
+ }
+ /*
+ * Locate our struct tcm_vhost_nacl and set the FC Nport WWPN
+ */
+ nacl = container_of(se_nacl, struct tcm_vhost_nacl, se_node_acl);
+ nacl->iport_wwpn = wwpn;
+
+ return se_nacl;
+}
+
+static void tcm_vhost_drop_nodeacl(struct se_node_acl *se_acl)
+{
+ struct tcm_vhost_nacl *nacl = container_of(se_acl,
+ struct tcm_vhost_nacl, se_node_acl);
+ core_tpg_del_initiator_node_acl(se_acl->se_tpg, se_acl, 1);
+ kfree(nacl);
+}
+
+static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tv_tpg,
+ const char *name)
+{
+ struct se_portal_group *se_tpg;
+ struct tcm_vhost_nexus *tv_nexus;
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ if (tv_tpg->tpg_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_debug("tv_tpg->tpg_nexus already exists\n");
+ return -EEXIST;
+ }
+ se_tpg = &tv_tpg->se_tpg;
+
+ tv_nexus = kzalloc(sizeof(struct tcm_vhost_nexus), GFP_KERNEL);
+ if (!tv_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_err("Unable to allocate struct tcm_vhost_nexus\n");
+ return -ENOMEM;
+ }
+ /*
+ * Initialize the struct se_session pointer
+ */
+ tv_nexus->tvn_se_sess = transport_init_session();
+ if (IS_ERR(tv_nexus->tvn_se_sess)) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ kfree(tv_nexus);
+ return -ENOMEM;
+ }
+ /*
+ * Since we are running in 'demo mode' this call with generate a
+ * struct se_node_acl for the tcm_vhost struct se_portal_group with
+ * the SCSI Initiator port name of the passed configfs group 'name'.
+ */
+ tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
+ se_tpg, (unsigned char *)name);
+ if (!tv_nexus->tvn_se_sess->se_node_acl) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ pr_debug("core_tpg_check_initiator_node_acl() failed"
+ " for %s\n", name);
+ transport_free_session(tv_nexus->tvn_se_sess);
+ kfree(tv_nexus);
+ return -ENOMEM;
+ }
+ /*
+ * Now register the TCM vhost virtual I_T Nexus as active with the
+ * call to __transport_register_session()
+ */
+ __transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
+ tv_nexus->tvn_se_sess, tv_nexus);
+ tv_tpg->tpg_nexus = tv_nexus;
+
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return 0;
+}
+
+static int tcm_vhost_drop_nexus(struct tcm_vhost_tpg *tpg)
+{
+ struct se_session *se_sess;
+ struct tcm_vhost_nexus *tv_nexus;
+
+ mutex_lock(&tpg->tv_tpg_mutex);
+ tv_nexus = tpg->tpg_nexus;
+ if (!tv_nexus) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+
+ se_sess = tv_nexus->tvn_se_sess;
+ if (!se_sess) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+
+ if (tpg->tv_tpg_port_count != 0) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
+ " active TPG port count: %d\n",
+ tpg->tv_tpg_port_count);
+ return -EBUSY;
+ }
+
+ if (tpg->tv_tpg_vhost_count != 0) {
+ mutex_unlock(&tpg->tv_tpg_mutex);
+ pr_err("Unable to remove TCM_vhost I_T Nexus with"
+ " active TPG vhost count: %d\n",
+ tpg->tv_tpg_vhost_count);
+ return -EBUSY;
+ }
+
+ pr_debug("TCM_vhost_ConfigFS: Removing I_T Nexus to emulated"
+ " %s Initiator Port: %s\n", tcm_vhost_dump_proto_id(tpg->tport),
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ /*
+ * Release the SCSI I_T Nexus to the emulated vhost Target Port
+ */
+ transport_deregister_session(tv_nexus->tvn_se_sess);
+ tpg->tpg_nexus = NULL;
+ mutex_unlock(&tpg->tv_tpg_mutex);
+
+ kfree(tv_nexus);
+ return 0;
+}
+
+static ssize_t tcm_vhost_tpg_show_nexus(struct se_portal_group *se_tpg,
+ char *page)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_nexus *tv_nexus;
+ ssize_t ret;
+
+ mutex_lock(&tv_tpg->tv_tpg_mutex);
+ tv_nexus = tv_tpg->tpg_nexus;
+ if (!tv_nexus) {
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+ return -ENODEV;
+ }
+ ret = snprintf(page, PAGE_SIZE, "%s\n",
+ tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
+ mutex_unlock(&tv_tpg->tv_tpg_mutex);
+
+ return ret;
+}
+
+static ssize_t tcm_vhost_tpg_store_nexus(struct se_portal_group *se_tpg,
+ const char *page,
+ size_t count)
+{
+ struct tcm_vhost_tpg *tv_tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+ struct tcm_vhost_tport *tport_wwn = tv_tpg->tport;
+ unsigned char i_port[TCM_VHOST_NAMELEN], *ptr, *port_ptr;
+ int ret;
+ /*
+ * Shutdown the active I_T nexus if 'NULL' is passed..
+ */
+ if (!strncmp(page, "NULL", 4)) {
+ ret = tcm_vhost_drop_nexus(tv_tpg);
+ return (!ret) ? count : ret;
+ }
+ /*
+ * Otherwise make sure the passed virtual Initiator port WWN matches
+ * the fabric protocol_id set in tcm_vhost_make_tport(), and call
+ * tcm_vhost_make_nexus().
+ */
+ if (strlen(page) >= TCM_VHOST_NAMELEN) {
+ pr_err("Emulated NAA Sas Address: %s, exceeds"
+ " max: %d\n", page, TCM_VHOST_NAMELEN);
+ return -EINVAL;
+ }
+ snprintf(&i_port[0], TCM_VHOST_NAMELEN, "%s", page);
+
+ ptr = strstr(i_port, "naa.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) {
+ pr_err("Passed SAS Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "fc.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) {
+ pr_err("Passed FCP Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[3]; /* Skip over "fc." */
+ goto check_newline;
+ }
+ ptr = strstr(i_port, "iqn.");
+ if (ptr) {
+ if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) {
+ pr_err("Passed iSCSI Initiator Port %s does not"
+ " match target port protoid: %s\n", i_port,
+ tcm_vhost_dump_proto_id(tport_wwn));
+ return -EINVAL;
+ }
+ port_ptr = &i_port[0];
+ goto check_newline;
+ }
+ pr_err("Unable to locate prefix for emulated Initiator Port:"
+ " %s\n", i_port);
+ return -EINVAL;
+ /*
+ * Clear any trailing newline for the NAA WWN
+ */
+check_newline:
+ if (i_port[strlen(i_port)-1] == '\n')
+ i_port[strlen(i_port)-1] = '\0';
+
+ ret = tcm_vhost_make_nexus(tv_tpg, port_ptr);
+ if (ret < 0)
+ return ret;
+
+ return count;
+}
+
+TF_TPG_BASE_ATTR(tcm_vhost, nexus, S_IRUGO | S_IWUSR);
+
+static struct configfs_attribute *tcm_vhost_tpg_attrs[] = {
+ &tcm_vhost_tpg_nexus.attr,
+ NULL,
+};
+
+static struct se_portal_group *tcm_vhost_make_tpg(struct se_wwn *wwn,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_vhost_tport *tport = container_of(wwn,
+ struct tcm_vhost_tport, tport_wwn);
+
+ struct tcm_vhost_tpg *tpg;
+ unsigned long tpgt;
+ int ret;
+
+ if (strstr(name, "tpgt_") != name)
+ return ERR_PTR(-EINVAL);
+ if (kstrtoul(name + 5, 10, &tpgt) || tpgt > UINT_MAX)
+ return ERR_PTR(-EINVAL);
+
+ tpg = kzalloc(sizeof(struct tcm_vhost_tpg), GFP_KERNEL);
+ if (!tpg) {
+ pr_err("Unable to allocate struct tcm_vhost_tpg");
+ return ERR_PTR(-ENOMEM);
+ }
+ mutex_init(&tpg->tv_tpg_mutex);
+ INIT_LIST_HEAD(&tpg->tv_tpg_list);
+ tpg->tport = tport;
+ tpg->tport_tpgt = tpgt;
+
+ ret = core_tpg_register(&tcm_vhost_fabric_configfs->tf_ops, wwn,
+ &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL);
+ if (ret < 0) {
+ kfree(tpg);
+ return NULL;
+ }
+ mutex_lock(&tcm_vhost_mutex);
+ list_add_tail(&tpg->tv_tpg_list, &tcm_vhost_list);
+ mutex_unlock(&tcm_vhost_mutex);
+
+ return &tpg->se_tpg;
+}
+
+static void tcm_vhost_drop_tpg(struct se_portal_group *se_tpg)
+{
+ struct tcm_vhost_tpg *tpg = container_of(se_tpg,
+ struct tcm_vhost_tpg, se_tpg);
+
+ mutex_lock(&tcm_vhost_mutex);
+ list_del(&tpg->tv_tpg_list);
+ mutex_unlock(&tcm_vhost_mutex);
+ /*
+ * Release the virtual I_T Nexus for this vhost TPG
+ */
+ tcm_vhost_drop_nexus(tpg);
+ /*
+ * Deregister the se_tpg from TCM..
+ */
+ core_tpg_deregister(se_tpg);
+ kfree(tpg);
+}
+
+static struct se_wwn *tcm_vhost_make_tport(struct target_fabric_configfs *tf,
+ struct config_group *group,
+ const char *name)
+{
+ struct tcm_vhost_tport *tport;
+ char *ptr;
+ u64 wwpn = 0;
+ int off = 0;
+
+ /* if (tcm_vhost_parse_wwn(name, &wwpn, 1) < 0)
+ return ERR_PTR(-EINVAL); */
+
+ tport = kzalloc(sizeof(struct tcm_vhost_tport), GFP_KERNEL);
+ if (!tport) {
+ pr_err("Unable to allocate struct tcm_vhost_tport");
+ return ERR_PTR(-ENOMEM);
+ }
+ tport->tport_wwpn = wwpn;
+ /*
+ * Determine the emulated Protocol Identifier and Target Port Name
+ * based on the incoming configfs directory name.
+ */
+ ptr = strstr(name, "naa.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_SAS;
+ goto check_len;
+ }
+ ptr = strstr(name, "fc.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_FCP;
+ off = 3; /* Skip over "fc." */
+ goto check_len;
+ }
+ ptr = strstr(name, "iqn.");
+ if (ptr) {
+ tport->tport_proto_id = SCSI_PROTOCOL_ISCSI;
+ goto check_len;
+ }
+
+ pr_err("Unable to locate prefix for emulated Target Port:"
+ " %s\n", name);
+ kfree(tport);
+ return ERR_PTR(-EINVAL);
+
+check_len:
+ if (strlen(name) >= TCM_VHOST_NAMELEN) {
+ pr_err("Emulated %s Address: %s, exceeds"
+ " max: %d\n", name, tcm_vhost_dump_proto_id(tport),
+ TCM_VHOST_NAMELEN);
+ kfree(tport);
+ return ERR_PTR(-EINVAL);
+ }
+ snprintf(&tport->tport_name[0], TCM_VHOST_NAMELEN, "%s", &name[off]);
+
+ pr_debug("TCM_VHost_ConfigFS: Allocated emulated Target"
+ " %s Address: %s\n", tcm_vhost_dump_proto_id(tport), name);
+
+ return &tport->tport_wwn;
+}
+
+static void tcm_vhost_drop_tport(struct se_wwn *wwn)
+{
+ struct tcm_vhost_tport *tport = container_of(wwn,
+ struct tcm_vhost_tport, tport_wwn);
+
+ pr_debug("TCM_VHost_ConfigFS: Deallocating emulated Target"
+ " %s Address: %s\n", tcm_vhost_dump_proto_id(tport),
+ tport->tport_name);
+
+ kfree(tport);
+}
+
+static ssize_t tcm_vhost_wwn_show_attr_version(
+ struct target_fabric_configfs *tf,
+ char *page)
+{
+ return sprintf(page, "TCM_VHOST fabric module %s on %s/%s"
+ "on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname,
+ utsname()->machine);
+}
+
+TF_WWN_ATTR_RO(tcm_vhost, version);
+
+static struct configfs_attribute *tcm_vhost_wwn_attrs[] = {
+ &tcm_vhost_wwn_version.attr,
+ NULL,
+};
+
+static struct target_core_fabric_ops tcm_vhost_ops = {
+ .get_fabric_name = tcm_vhost_get_fabric_name,
+ .get_fabric_proto_ident = tcm_vhost_get_fabric_proto_ident,
+ .tpg_get_wwn = tcm_vhost_get_fabric_wwn,
+ .tpg_get_tag = tcm_vhost_get_tag,
+ .tpg_get_default_depth = tcm_vhost_get_default_depth,
+ .tpg_get_pr_transport_id = tcm_vhost_get_pr_transport_id,
+ .tpg_get_pr_transport_id_len = tcm_vhost_get_pr_transport_id_len,
+ .tpg_parse_pr_out_transport_id = tcm_vhost_parse_pr_out_transport_id,
+ .tpg_check_demo_mode = tcm_vhost_check_true,
+ .tpg_check_demo_mode_cache = tcm_vhost_check_true,
+ .tpg_check_demo_mode_write_protect = tcm_vhost_check_false,
+ .tpg_check_prod_mode_write_protect = tcm_vhost_check_false,
+ .tpg_alloc_fabric_acl = tcm_vhost_alloc_fabric_acl,
+ .tpg_release_fabric_acl = tcm_vhost_release_fabric_acl,
+ .tpg_get_inst_index = tcm_vhost_tpg_get_inst_index,
+ .release_cmd = tcm_vhost_release_cmd,
+ .shutdown_session = tcm_vhost_shutdown_session,
+ .close_session = tcm_vhost_close_session,
+ .sess_get_index = tcm_vhost_sess_get_index,
+ .sess_get_initiator_sid = NULL,
+ .write_pending = tcm_vhost_write_pending,
+ .write_pending_status = tcm_vhost_write_pending_status,
+ .set_default_node_attributes = tcm_vhost_set_default_node_attrs,
+ .get_task_tag = tcm_vhost_get_task_tag,
+ .get_cmd_state = tcm_vhost_get_cmd_state,
+ .queue_data_in = tcm_vhost_queue_data_in,
+ .queue_status = tcm_vhost_queue_status,
+ .queue_tm_rsp = tcm_vhost_queue_tm_rsp,
+ .get_fabric_sense_len = tcm_vhost_get_fabric_sense_len,
+ .set_fabric_sense_len = tcm_vhost_set_fabric_sense_len,
+ /*
+ * Setup callers for generic logic in target_core_fabric_configfs.c
+ */
+ .fabric_make_wwn = tcm_vhost_make_tport,
+ .fabric_drop_wwn = tcm_vhost_drop_tport,
+ .fabric_make_tpg = tcm_vhost_make_tpg,
+ .fabric_drop_tpg = tcm_vhost_drop_tpg,
+ .fabric_post_link = tcm_vhost_port_link,
+ .fabric_pre_unlink = tcm_vhost_port_unlink,
+ .fabric_make_np = NULL,
+ .fabric_drop_np = NULL,
+ .fabric_make_nodeacl = tcm_vhost_make_nodeacl,
+ .fabric_drop_nodeacl = tcm_vhost_drop_nodeacl,
+};
+
+static int tcm_vhost_register_configfs(void)
+{
+ struct target_fabric_configfs *fabric;
+ int ret;
+
+ pr_debug("TCM_VHOST fabric module %s on %s/%s"
+ " on "UTS_RELEASE"\n", TCM_VHOST_VERSION, utsname()->sysname,
+ utsname()->machine);
+ /*
+ * Register the top level struct config_item_type with TCM core
+ */
+ fabric = target_fabric_configfs_init(THIS_MODULE, "vhost");
+ if (IS_ERR(fabric)) {
+ pr_err("target_fabric_configfs_init() failed\n");
+ return PTR_ERR(fabric);
+ }
+ /*
+ * Setup fabric->tf_ops from our local tcm_vhost_ops
+ */
+ fabric->tf_ops = tcm_vhost_ops;
+ /*
+ * Setup default attribute lists for various fabric->tf_cit_tmpl
+ */
+ TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_vhost_wwn_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_vhost_tpg_attrs;
+ TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL;
+ TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL;
+ /*
+ * Register the fabric for use within TCM
+ */
+ ret = target_fabric_configfs_register(fabric);
+ if (ret < 0) {
+ pr_err("target_fabric_configfs_register() failed"
+ " for TCM_VHOST\n");
+ return ret;
+ }
+ /*
+ * Setup our local pointer to *fabric
+ */
+ tcm_vhost_fabric_configfs = fabric;
+ pr_debug("TCM_VHOST[0] - Set fabric -> tcm_vhost_fabric_configfs\n");
+ return 0;
+};
+
+static void tcm_vhost_deregister_configfs(void)
+{
+ if (!tcm_vhost_fabric_configfs)
+ return;
+
+ target_fabric_configfs_deregister(tcm_vhost_fabric_configfs);
+ tcm_vhost_fabric_configfs = NULL;
+ pr_debug("TCM_VHOST[0] - Cleared tcm_vhost_fabric_configfs\n");
+};
+
+static int __init tcm_vhost_init(void)
+{
+ int ret = -ENOMEM;
+ /*
+ * Use our own dedicated workqueue for submitting I/O into
+ * target core to avoid contention within system_wq.
+ */
+ tcm_vhost_workqueue = alloc_workqueue("tcm_vhost", 0, 0);
+ if (!tcm_vhost_workqueue)
+ goto out;
+
+ ret = vhost_scsi_register();
+ if (ret < 0)
+ goto out_destroy_workqueue;
+
+ ret = tcm_vhost_register_configfs();
+ if (ret < 0)
+ goto out_vhost_scsi_deregister;
+
+ return 0;
+
+out_vhost_scsi_deregister:
+ vhost_scsi_deregister();
+out_destroy_workqueue:
+ destroy_workqueue(tcm_vhost_workqueue);
+out:
+ return ret;
+};
+
+static void tcm_vhost_exit(void)
+{
+ tcm_vhost_deregister_configfs();
+ vhost_scsi_deregister();
+ destroy_workqueue(tcm_vhost_workqueue);
+};
+
+MODULE_DESCRIPTION("TCM_VHOST series fabric driver");
+MODULE_LICENSE("GPL");
+module_init(tcm_vhost_init);
+module_exit(tcm_vhost_exit);
diff --git a/drivers/vhost/tcm_vhost.h b/drivers/vhost/tcm_vhost.h
new file mode 100644
index 0000000..d9e9355
--- /dev/null
+++ b/drivers/vhost/tcm_vhost.h
@@ -0,0 +1,103 @@
+#define TCM_VHOST_VERSION "v0.1"
+#define TCM_VHOST_NAMELEN 256
+#define TCM_VHOST_MAX_CDB_SIZE 32
+
+struct tcm_vhost_cmd {
+ /* Descriptor from vhost_get_vq_desc() for virt_queue segment */
+ int tvc_vq_desc;
+ /* The Tag from include/linux/virtio_scsi.h:struct virtio_scsi_cmd_req */
+ u64 tvc_tag;
+ /* The number of scatterlists associated with this cmd */
+ u32 tvc_sgl_count;
+ /* Saved unpacked SCSI LUN for tcm_vhost_submission_work() */
+ u32 tvc_lun;
+ /* Pointer to the SGL formatted memory from virtio-scsi */
+ struct scatterlist *tvc_sgl;
+ /* Pointer to response */
+ struct virtio_scsi_cmd_resp __user *tvc_resp;
+ /* Pointer to vhost_scsi for our device */
+ struct vhost_scsi *tvc_vhost;
+ /* The TCM I/O descriptor that is accessed via container_of() */
+ struct se_cmd tvc_se_cmd;
+ /* work item used for cmwq dispatch to tcm_vhost_submission_work() */
+ struct work_struct work;
+ /* Copy of the incoming SCSI command descriptor block (CDB) */
+ unsigned char tvc_cdb[TCM_VHOST_MAX_CDB_SIZE];
+ /* Sense buffer that will be mapped into outgoing status */
+ unsigned char tvc_sense_buf[TRANSPORT_SENSE_BUFFER];
+ /* Completed commands list, serviced from vhost worker thread */
+ struct list_head tvc_completion_list;
+};
+
+struct tcm_vhost_nexus {
+ /* Pointer to TCM session for I_T Nexus */
+ struct se_session *tvn_se_sess;
+};
+
+struct tcm_vhost_nacl {
+ /* Binary World Wide unique Port Name for Vhost Initiator port */
+ u64 iport_wwpn;
+ /* ASCII formatted WWPN for Sas Initiator port */
+ char iport_name[TCM_VHOST_NAMELEN];
+ /* Returned by tcm_vhost_make_nodeacl() */
+ struct se_node_acl se_node_acl;
+};
+
+struct tcm_vhost_tpg {
+ /* Vhost port target portal group tag for TCM */
+ u16 tport_tpgt;
+ /* Used to track number of TPG Port/Lun Links wrt to explict I_T Nexus shutdown */
+ int tv_tpg_port_count;
+ /* Used for vhost_scsi device reference to tpg_nexus, protected by tv_tpg_mutex */
+ int tv_tpg_vhost_count;
+ /* list for tcm_vhost_list */
+ struct list_head tv_tpg_list;
+ /* Used to protect access for tpg_nexus */
+ struct mutex tv_tpg_mutex;
+ /* Pointer to the TCM VHost I_T Nexus for this TPG endpoint */
+ struct tcm_vhost_nexus *tpg_nexus;
+ /* Pointer back to tcm_vhost_tport */
+ struct tcm_vhost_tport *tport;
+ /* Returned by tcm_vhost_make_tpg() */
+ struct se_portal_group se_tpg;
+};
+
+struct tcm_vhost_tport {
+ /* SCSI protocol the tport is providing */
+ u8 tport_proto_id;
+ /* Binary World Wide unique Port Name for Vhost Target port */
+ u64 tport_wwpn;
+ /* ASCII formatted WWPN for Vhost Target port */
+ char tport_name[TCM_VHOST_NAMELEN];
+ /* Returned by tcm_vhost_make_tport() */
+ struct se_wwn tport_wwn;
+};
+
+/*
+ * As per request from MST, keep TCM_VHOST related ioctl defines out of
+ * linux/vhost.h (user-space) for now..
+ */
+
+#include <linux/vhost.h>
+
+/*
+ * Used by QEMU userspace to ensure a consistent vhost-scsi ABI.
+ *
+ * ABI Rev 0: July 2012 version starting point for v3.6-rc merge candidate +
+ * RFC-v2 vhost-scsi userspace. Add GET_ABI_VERSION ioctl usage
+ */
+
+#define VHOST_SCSI_ABI_VERSION 0
+
+struct vhost_scsi_target {
+ int abi_version;
+ char vhost_wwpn[TRANSPORT_IQN_LEN];
+ unsigned short vhost_tpgt;
+ unsigned short reserved;
+};
+
+/* VHOST_SCSI specific defines */
+#define VHOST_SCSI_SET_ENDPOINT _IOW(VHOST_VIRTIO, 0x40, struct vhost_scsi_target)
+#define VHOST_SCSI_CLEAR_ENDPOINT _IOW(VHOST_VIRTIO, 0x41, struct vhost_scsi_target)
+/* Changing this breaks userspace. */
+#define VHOST_SCSI_GET_ABI_VERSION _IOW(VHOST_VIRTIO, 0x42, int)
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 2e471c2..88e9204 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -372,8 +372,15 @@
struct vc_data *vc = NULL;
int c;
int mode;
+ int ret;
- console_lock();
+ /* FIXME: we should sort out the unbind locking instead */
+ /* instead we just fail to flash the cursor if we can't get
+ * the lock instead of blocking fbcon deinit */
+ ret = console_trylock();
+ if (ret == 0)
+ return;
+
if (ops && ops->currcon != -1)
vc = vc_cons[ops->currcon].d;
diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c
index d90062b..92d08e7 100644
--- a/drivers/w1/slaves/w1_therm.c
+++ b/drivers/w1/slaves/w1_therm.c
@@ -91,6 +91,11 @@
.fops = &w1_therm_fops,
};
+static struct w1_family w1_therm_family_DS1825 = {
+ .fid = W1_THERM_DS1825,
+ .fops = &w1_therm_fops,
+};
+
struct w1_therm_family_converter
{
u8 broken;
@@ -120,6 +125,10 @@
.f = &w1_therm_family_DS28EA00,
.convert = w1_DS18B20_convert_temp
},
+ {
+ .f = &w1_therm_family_DS1825,
+ .convert = w1_DS18B20_convert_temp
+ }
};
static inline int w1_DS18B20_convert_temp(u8 rom[9])
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index b00ada4..a1f0ce1 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -39,6 +39,7 @@
#define W1_EEPROM_DS2431 0x2D
#define W1_FAMILY_DS2760 0x30
#define W1_FAMILY_DS2780 0x32
+#define W1_THERM_DS1825 0x3B
#define W1_FAMILY_DS2781 0x3D
#define W1_THERM_DS28EA00 0x42
diff --git a/drivers/zorro/zorro.c b/drivers/zorro/zorro.c
index 181fa81..858c971 100644
--- a/drivers/zorro/zorro.c
+++ b/drivers/zorro/zorro.c
@@ -37,7 +37,6 @@
*/
struct zorro_bus {
- struct list_head devices; /* list of devices on this bus */
struct device dev;
};
@@ -136,7 +135,6 @@
if (!bus)
return -ENOMEM;
- INIT_LIST_HEAD(&bus->devices);
bus->dev.parent = &pdev->dev;
dev_set_name(&bus->dev, "zorro");
error = device_register(&bus->dev);
diff --git a/fs/autofs4/expire.c b/fs/autofs4/expire.c
index 1feb68e..842d000 100644
--- a/fs/autofs4/expire.c
+++ b/fs/autofs4/expire.c
@@ -94,25 +94,21 @@
{
struct autofs_sb_info *sbi = autofs4_sbi(root->d_sb);
struct list_head *next;
- struct dentry *p, *q;
+ struct dentry *q;
spin_lock(&sbi->lookup_lock);
+ spin_lock(&root->d_lock);
- if (prev == NULL) {
- spin_lock(&root->d_lock);
+ if (prev)
+ next = prev->d_u.d_child.next;
+ else {
prev = dget_dlock(root);
next = prev->d_subdirs.next;
- p = prev;
- goto start;
}
- p = prev;
- spin_lock(&p->d_lock);
-again:
- next = p->d_u.d_child.next;
-start:
+cont:
if (next == &root->d_subdirs) {
- spin_unlock(&p->d_lock);
+ spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev);
return NULL;
@@ -121,16 +117,15 @@
q = list_entry(next, struct dentry, d_u.d_child);
spin_lock_nested(&q->d_lock, DENTRY_D_LOCK_NESTED);
- /* Negative dentry - try next */
- if (!simple_positive(q)) {
- spin_unlock(&p->d_lock);
- lock_set_subclass(&q->d_lock.dep_map, 0, _RET_IP_);
- p = q;
- goto again;
+ /* Already gone or negative dentry (under construction) - try next */
+ if (q->d_count == 0 || !simple_positive(q)) {
+ spin_unlock(&q->d_lock);
+ next = q->d_u.d_child.next;
+ goto cont;
}
dget_dlock(q);
spin_unlock(&q->d_lock);
- spin_unlock(&p->d_lock);
+ spin_unlock(&root->d_lock);
spin_unlock(&sbi->lookup_lock);
dput(prev);
@@ -404,11 +399,6 @@
DPRINTK("checking mountpoint %p %.*s",
dentry, (int)dentry->d_name.len, dentry->d_name.name);
- /* Path walk currently on this dentry? */
- ino_count = atomic_read(&ino->count) + 2;
- if (dentry->d_count > ino_count)
- goto next;
-
/* Can we umount this guy */
if (autofs4_mount_busy(mnt, dentry))
goto next;
diff --git a/fs/bio.c b/fs/bio.c
index 73922ab..5eaa70c 100644
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -1312,7 +1312,7 @@
* Note that this code is very hard to test under normal circumstances because
* direct-io pins the pages with get_user_pages(). This makes
* is_page_cache_freeable return false, and the VM will not clean the pages.
- * But other code (eg, pdflush) could clean the pages if they are mapped
+ * But other code (eg, flusher threads) could clean the pages if they are mapped
* pagecache.
*
* Simply disabling the call to bio_set_pages_dirty() is a good way to test the
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 83baec2..6e8f416 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -324,7 +324,8 @@
* If this code finds it can't get good compression, it puts an
* entry onto the work queue to write the uncompressed bytes. This
* makes sure that both compressed inodes and uncompressed inodes
- * are written in the same order that pdflush sent them down.
+ * are written in the same order that the flusher thread sent them
+ * down.
*/
static noinline int compress_file_range(struct inode *inode,
struct page *locked_page,
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index bc2f6ff..7bb7556 100644
--- a/fs/btrfs/ioctl.c
+++ b/fs/btrfs/ioctl.c
@@ -664,10 +664,6 @@
struct dentry *dentry;
int error;
- error = mnt_want_write(parent->mnt);
- if (error)
- return error;
-
mutex_lock_nested(&dir->i_mutex, I_MUTEX_PARENT);
dentry = lookup_one_len(name, parent->dentry, namelen);
@@ -703,7 +699,6 @@
dput(dentry);
out_unlock:
mutex_unlock(&dir->i_mutex);
- mnt_drop_write(parent->mnt);
return error;
}
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
index 643335a..051c7fe 100644
--- a/fs/btrfs/ordered-data.c
+++ b/fs/btrfs/ordered-data.c
@@ -596,7 +596,7 @@
/*
* pages in the range can be dirty, clean or writeback. We
* start IO on any dirty ones so the wait doesn't stall waiting
- * for pdflush to find them
+ * for the flusher thread to find them
*/
if (!test_bit(BTRFS_ORDERED_DIRECT, &entry->flags))
filemap_fdatawrite_range(inode->i_mapping, start, end);
diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 8c6e61d..f2eb24c 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -100,10 +100,6 @@
fs_info->fs_state = BTRFS_SUPER_FLAG_ERROR;
}
-/* NOTE:
- * We move write_super stuff at umount in order to avoid deadlock
- * for umount hold all lock.
- */
static void save_error_info(struct btrfs_fs_info *fs_info)
{
__save_error_info(fs_info);
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index b8708f9..e86ae04 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -1744,10 +1744,6 @@
device->fs_devices = root->fs_info->fs_devices;
- /*
- * we don't want write_supers to jump in here with our device
- * half setup
- */
mutex_lock(&root->fs_info->fs_devices->device_list_mutex);
list_add_rcu(&device->dev_list, &root->fs_info->fs_devices->devices);
list_add(&device->dev_alloc_list,
diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c
index fb962ef..6d59006 100644
--- a/fs/ceph/debugfs.c
+++ b/fs/ceph/debugfs.c
@@ -201,6 +201,7 @@
int err = -ENOMEM;
dout("ceph_fs_debugfs_init\n");
+ BUG_ON(!fsc->client->debugfs_dir);
fsc->debugfs_congestion_kb =
debugfs_create_file("writeback_congestion_kb",
0600,
diff --git a/fs/ceph/inode.c b/fs/ceph/inode.c
index 9fff9f3..4b5762e 100644
--- a/fs/ceph/inode.c
+++ b/fs/ceph/inode.c
@@ -992,11 +992,15 @@
if (rinfo->head->is_dentry) {
struct inode *dir = req->r_locked_dir;
- err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag,
- session, req->r_request_started, -1,
- &req->r_caps_reservation);
- if (err < 0)
- return err;
+ if (dir) {
+ err = fill_inode(dir, &rinfo->diri, rinfo->dirfrag,
+ session, req->r_request_started, -1,
+ &req->r_caps_reservation);
+ if (err < 0)
+ return err;
+ } else {
+ WARN_ON_ONCE(1);
+ }
}
/*
@@ -1004,6 +1008,7 @@
* will have trouble splicing in the virtual snapdir later
*/
if (rinfo->head->is_dentry && !req->r_aborted &&
+ req->r_locked_dir &&
(rinfo->head->is_target || strncmp(req->r_dentry->d_name.name,
fsc->mount_options->snapdir_name,
req->r_dentry->d_name.len))) {
diff --git a/fs/ceph/ioctl.c b/fs/ceph/ioctl.c
index 8e3fb69..1396ceb 100644
--- a/fs/ceph/ioctl.c
+++ b/fs/ceph/ioctl.c
@@ -42,7 +42,8 @@
/* validate striping parameters */
if ((l->object_size & ~PAGE_MASK) ||
(l->stripe_unit & ~PAGE_MASK) ||
- ((unsigned)l->object_size % (unsigned)l->stripe_unit))
+ (l->stripe_unit != 0 &&
+ ((unsigned)l->object_size % (unsigned)l->stripe_unit)))
return -EINVAL;
/* make sure it's a valid data pool */
diff --git a/fs/compat.c b/fs/compat.c
index 6161255..1bdb350 100644
--- a/fs/compat.c
+++ b/fs/compat.c
@@ -1155,11 +1155,14 @@
struct file *file;
int fput_needed;
ssize_t ret;
+ loff_t pos;
file = fget_light(fd, &fput_needed);
if (!file)
return -EBADF;
- ret = compat_readv(file, vec, vlen, &file->f_pos);
+ pos = file->f_pos;
+ ret = compat_readv(file, vec, vlen, &pos);
+ file->f_pos = pos;
fput_light(file, fput_needed);
return ret;
}
@@ -1221,11 +1224,14 @@
struct file *file;
int fput_needed;
ssize_t ret;
+ loff_t pos;
file = fget_light(fd, &fput_needed);
if (!file)
return -EBADF;
- ret = compat_writev(file, vec, vlen, &file->f_pos);
+ pos = file->f_pos;
+ ret = compat_writev(file, vec, vlen, &pos);
+ file->f_pos = pos;
fput_light(file, fput_needed);
return ret;
}
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 1c8b556..eedec84 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -1654,8 +1654,8 @@
error = PTR_ERR(file);
goto out_free_fd;
}
- fd_install(fd, file);
ep->file = file;
+ fd_install(fd, file);
return fd;
out_free_fd:
diff --git a/fs/exofs/inode.c b/fs/exofs/inode.c
index 5badb0c..1562c27 100644
--- a/fs/exofs/inode.c
+++ b/fs/exofs/inode.c
@@ -37,15 +37,12 @@
#define EXOFS_DBGMSG2(M...) do {} while (0)
-enum {MAX_PAGES_KMALLOC = PAGE_SIZE / sizeof(struct page *), };
-
unsigned exofs_max_io_pages(struct ore_layout *layout,
unsigned expected_pages)
{
- unsigned pages = min_t(unsigned, expected_pages, MAX_PAGES_KMALLOC);
+ unsigned pages = min_t(unsigned, expected_pages,
+ layout->max_io_length / PAGE_SIZE);
- /* TODO: easily support bio chaining */
- pages = min_t(unsigned, pages, layout->max_io_length / PAGE_SIZE);
return pages;
}
@@ -101,7 +98,8 @@
* it might not end here. don't be left with nothing
*/
if (!pcol->expected_pages)
- pcol->expected_pages = MAX_PAGES_KMALLOC;
+ pcol->expected_pages =
+ exofs_max_io_pages(&pcol->sbi->layout, ~0);
}
static int pcol_try_alloc(struct page_collect *pcol)
@@ -389,6 +387,8 @@
size_t len;
int ret;
+ BUG_ON(!PageLocked(page));
+
/* FIXME: Just for debugging, will be removed */
if (PageUptodate(page))
EXOFS_ERR("PageUptodate(0x%lx, 0x%lx)\n", pcol->inode->i_ino,
@@ -572,8 +572,16 @@
if (!pcol->that_locked_page ||
(pcol->that_locked_page->index != index)) {
- struct page *page = find_get_page(pcol->inode->i_mapping, index);
+ struct page *page;
+ loff_t i_size = i_size_read(pcol->inode);
+ if (offset >= i_size) {
+ *uptodate = true;
+ EXOFS_DBGMSG("offset >= i_size index=0x%lx\n", index);
+ return ZERO_PAGE(0);
+ }
+
+ page = find_get_page(pcol->inode->i_mapping, index);
if (!page) {
page = find_or_create_page(pcol->inode->i_mapping,
index, GFP_NOFS);
@@ -602,12 +610,13 @@
{
struct page_collect *pcol = priv;
- if (pcol->that_locked_page != page) {
+ if ((pcol->that_locked_page != page) && (ZERO_PAGE(0) != page)) {
EXOFS_DBGMSG("index=0x%lx\n", page->index);
page_cache_release(page);
return;
}
- EXOFS_DBGMSG("that_locked_page index=0x%lx\n", page->index);
+ EXOFS_DBGMSG("that_locked_page index=0x%lx\n",
+ ZERO_PAGE(0) == page ? -1 : page->index);
}
static const struct _ore_r4w_op _r4w_op = {
diff --git a/fs/exofs/ore.c b/fs/exofs/ore.c
index 24a49d4..1585db1 100644
--- a/fs/exofs/ore.c
+++ b/fs/exofs/ore.c
@@ -837,11 +837,11 @@
bio->bi_rw |= REQ_WRITE;
}
- osd_req_write(or, _ios_obj(ios, dev), per_dev->offset,
- bio, per_dev->length);
+ osd_req_write(or, _ios_obj(ios, cur_comp),
+ per_dev->offset, bio, per_dev->length);
ORE_DBGMSG("write(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
_LLU(per_dev->offset),
_LLU(per_dev->length), dev);
} else if (ios->kern_buff) {
@@ -853,20 +853,20 @@
(ios->si.unit_off + ios->length >
ios->layout->stripe_unit));
- ret = osd_req_write_kern(or, _ios_obj(ios, per_dev->dev),
+ ret = osd_req_write_kern(or, _ios_obj(ios, cur_comp),
per_dev->offset,
ios->kern_buff, ios->length);
if (unlikely(ret))
goto out;
ORE_DBGMSG2("write_kern(0x%llx) offset=0x%llx "
"length=0x%llx dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
_LLU(per_dev->offset),
_LLU(ios->length), per_dev->dev);
} else {
- osd_req_set_attributes(or, _ios_obj(ios, dev));
+ osd_req_set_attributes(or, _ios_obj(ios, cur_comp));
ORE_DBGMSG2("obj(0x%llx) set_attributes=%d dev=%d\n",
- _LLU(_ios_obj(ios, dev)->id),
+ _LLU(_ios_obj(ios, cur_comp)->id),
ios->out_attr_len, dev);
}
diff --git a/fs/exofs/super.c b/fs/exofs/super.c
index 4337836..dde41a7 100644
--- a/fs/exofs/super.c
+++ b/fs/exofs/super.c
@@ -400,8 +400,6 @@
ret = ore_write(ios);
if (unlikely(ret))
EXOFS_ERR("%s: ore_write failed.\n", __func__);
- else
- sb->s_dirt = 0;
unlock_super(sb);
@@ -412,14 +410,6 @@
return ret;
}
-static void exofs_write_super(struct super_block *sb)
-{
- if (!(sb->s_flags & MS_RDONLY))
- exofs_sync_fs(sb, 1);
- else
- sb->s_dirt = 0;
-}
-
static void _exofs_print_device(const char *msg, const char *dev_path,
struct osd_dev *od, u64 pid)
{
@@ -952,7 +942,6 @@
.write_inode = exofs_write_inode,
.evict_inode = exofs_evict_inode,
.put_super = exofs_put_super,
- .write_super = exofs_write_super,
.sync_fs = exofs_sync_fs,
.statfs = exofs_statfs,
};
diff --git a/fs/ext3/inode.c b/fs/ext3/inode.c
index 9a4a5c4..a075973 100644
--- a/fs/ext3/inode.c
+++ b/fs/ext3/inode.c
@@ -3459,14 +3459,6 @@
* inode out, but prune_icache isn't a user-visible syncing function.
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
* we start and wait on commits.
- *
- * Is this efficient/effective? Well, we're being nice to the system
- * by cleaning up our inodes proactively so they can be reaped
- * without I/O. But we are potentially leaving up to five seconds'
- * worth of inodes floating about which prune_icache wants us to
- * write out. One way to fix that would be to get prune_icache()
- * to do a write_super() to free up some memory. It has the desired
- * effect.
*/
int ext3_mark_inode_dirty(handle_t *handle, struct inode *inode)
{
diff --git a/fs/ext3/super.c b/fs/ext3/super.c
index ff9bcdc..8c892e9 100644
--- a/fs/ext3/super.c
+++ b/fs/ext3/super.c
@@ -64,11 +64,6 @@
/*
* Wrappers for journal_start/end.
- *
- * The only special thing we need to do here is to make sure that all
- * journal_end calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
*/
handle_t *ext3_journal_start_sb(struct super_block *sb, int nblocks)
{
@@ -90,12 +85,6 @@
return journal_start(journal, nblocks);
}
-/*
- * The only special thing we need to do here is to make sure that all
- * journal_stop calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
- */
int __ext3_journal_stop(const char *where, handle_t *handle)
{
struct super_block *sb;
diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c
index d23b31c..1b50890 100644
--- a/fs/ext4/balloc.c
+++ b/fs/ext4/balloc.c
@@ -280,14 +280,18 @@
return desc;
}
-static int ext4_valid_block_bitmap(struct super_block *sb,
- struct ext4_group_desc *desc,
- unsigned int block_group,
- struct buffer_head *bh)
+/*
+ * Return the block number which was discovered to be invalid, or 0 if
+ * the block bitmap is valid.
+ */
+static ext4_fsblk_t ext4_valid_block_bitmap(struct super_block *sb,
+ struct ext4_group_desc *desc,
+ unsigned int block_group,
+ struct buffer_head *bh)
{
ext4_grpblk_t offset;
ext4_grpblk_t next_zero_bit;
- ext4_fsblk_t bitmap_blk;
+ ext4_fsblk_t blk;
ext4_fsblk_t group_first_block;
if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
@@ -297,37 +301,33 @@
* or it has to also read the block group where the bitmaps
* are located to verify they are set.
*/
- return 1;
+ return 0;
}
group_first_block = ext4_group_first_block_no(sb, block_group);
/* check whether block bitmap block number is set */
- bitmap_blk = ext4_block_bitmap(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_block_bitmap(sb, desc);
+ offset = blk - group_first_block;
if (!ext4_test_bit(offset, bh->b_data))
/* bad block bitmap */
- goto err_out;
+ return blk;
/* check whether the inode bitmap block number is set */
- bitmap_blk = ext4_inode_bitmap(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_inode_bitmap(sb, desc);
+ offset = blk - group_first_block;
if (!ext4_test_bit(offset, bh->b_data))
/* bad block bitmap */
- goto err_out;
+ return blk;
/* check whether the inode table block number is set */
- bitmap_blk = ext4_inode_table(sb, desc);
- offset = bitmap_blk - group_first_block;
+ blk = ext4_inode_table(sb, desc);
+ offset = blk - group_first_block;
next_zero_bit = ext4_find_next_zero_bit(bh->b_data,
offset + EXT4_SB(sb)->s_itb_per_group,
offset);
- if (next_zero_bit >= offset + EXT4_SB(sb)->s_itb_per_group)
- /* good bitmap for inode tables */
- return 1;
-
-err_out:
- ext4_error(sb, "Invalid block bitmap - block_group = %d, block = %llu",
- block_group, bitmap_blk);
+ if (next_zero_bit < offset + EXT4_SB(sb)->s_itb_per_group)
+ /* bad bitmap for inode tables */
+ return blk;
return 0;
}
@@ -336,14 +336,26 @@
unsigned int block_group,
struct buffer_head *bh)
{
+ ext4_fsblk_t blk;
+
if (buffer_verified(bh))
return;
ext4_lock_group(sb, block_group);
- if (ext4_valid_block_bitmap(sb, desc, block_group, bh) &&
- ext4_block_bitmap_csum_verify(sb, block_group, desc, bh,
- EXT4_BLOCKS_PER_GROUP(sb) / 8))
- set_buffer_verified(bh);
+ blk = ext4_valid_block_bitmap(sb, desc, block_group, bh);
+ if (unlikely(blk != 0)) {
+ ext4_unlock_group(sb, block_group);
+ ext4_error(sb, "bg %u: block %llu: invalid block bitmap",
+ block_group, blk);
+ return;
+ }
+ if (unlikely(!ext4_block_bitmap_csum_verify(sb, block_group,
+ desc, bh, EXT4_BLOCKS_PER_GROUP(sb) / 8))) {
+ ext4_unlock_group(sb, block_group);
+ ext4_error(sb, "bg %u: bad block bitmap checksum", block_group);
+ return;
+ }
+ set_buffer_verified(bh);
ext4_unlock_group(sb, block_group);
}
diff --git a/fs/ext4/bitmap.c b/fs/ext4/bitmap.c
index f8716ea..5c2d181 100644
--- a/fs/ext4/bitmap.c
+++ b/fs/ext4/bitmap.c
@@ -79,7 +79,6 @@
if (provided == calculated)
return 1;
- ext4_error(sb, "Bad block bitmap checksum: block_group = %u", group);
return 0;
}
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index cd0c7ed..aabbb3f 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2662,6 +2662,7 @@
}
path[0].p_depth = depth;
path[0].p_hdr = ext_inode_hdr(inode);
+ i = 0;
if (ext4_ext_check(inode, path[0].p_hdr, depth)) {
err = -EIO;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 6324f74e..dff171c 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -1970,7 +1970,7 @@
* This function can get called via...
* - ext4_da_writepages after taking page lock (have journal handle)
* - journal_submit_inode_data_buffers (no journal handle)
- * - shrink_page_list via pdflush (no journal handle)
+ * - shrink_page_list via the kswapd/direct reclaim (no journal handle)
* - grab_page_cache when doing write_begin (have journal handle)
*
* We don't do any block allocation in this function. If we have page with
@@ -4589,14 +4589,6 @@
* inode out, but prune_icache isn't a user-visible syncing function.
* Whenever the user wants stuff synced (sys_sync, sys_msync, sys_fsync)
* we start and wait on commits.
- *
- * Is this efficient/effective? Well, we're being nice to the system
- * by cleaning up our inodes proactively so they can be reaped
- * without I/O. But we are potentially leaving up to five seconds'
- * worth of inodes floating about which prune_icache wants us to
- * write out. One way to fix that would be to get prune_icache()
- * to do a write_super() to free up some memory. It has the desired
- * effect.
*/
int ext4_mark_inode_dirty(handle_t *handle, struct inode *inode)
{
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index d76ec82..c6e0cb3 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -326,11 +326,6 @@
/*
* Wrappers for jbd2_journal_start/end.
- *
- * The only special thing we need to do here is to make sure that all
- * journal_end calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
*/
handle_t *ext4_journal_start_sb(struct super_block *sb, int nblocks)
{
@@ -356,12 +351,6 @@
return jbd2_journal_start(journal, nblocks);
}
-/*
- * The only special thing we need to do here is to make sure that all
- * jbd2_journal_stop calls result in the superblock being marked dirty, so
- * that sync() will call the filesystem's write_super callback if
- * appropriate.
- */
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
{
struct super_block *sb;
@@ -959,6 +948,7 @@
ei->i_reserved_meta_blocks = 0;
ei->i_allocated_meta_blocks = 0;
ei->i_da_metadata_calc_len = 0;
+ ei->i_da_metadata_calc_last_lblock = 0;
spin_lock_init(&(ei->i_block_reservation_lock));
#ifdef CONFIG_QUOTA
ei->i_reserved_quota = 0;
@@ -3119,6 +3109,10 @@
ext4_group_t i, ngroups = ext4_get_groups_count(sb);
int s, j, count = 0;
+ if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_BIGALLOC))
+ return (ext4_bg_has_super(sb, grp) + ext4_bg_num_gdb(sb, grp) +
+ sbi->s_itb_per_group + 2);
+
first_block = le32_to_cpu(sbi->s_es->s_first_data_block) +
(grp * EXT4_BLOCKS_PER_GROUP(sb));
last_block = first_block + EXT4_BLOCKS_PER_GROUP(sb) - 1;
@@ -4430,6 +4424,7 @@
ext4_commit_super(sb, 1);
jbd2_journal_clear_err(journal);
+ jbd2_journal_update_sb_errno(journal);
}
}
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index 8964cf3..324bc08 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -383,6 +383,9 @@
struct fuse_entry_out outentry;
struct fuse_file *ff;
+ /* Userspace expects S_IFREG in create mode */
+ BUG_ON((mode & S_IFMT) != S_IFREG);
+
forget = fuse_alloc_forget();
err = -ENOMEM;
if (!forget)
diff --git a/fs/fuse/file.c b/fs/fuse/file.c
index 93d8d6c..aba15f1 100644
--- a/fs/fuse/file.c
+++ b/fs/fuse/file.c
@@ -703,13 +703,16 @@
unsigned long nr_segs, loff_t pos)
{
struct inode *inode = iocb->ki_filp->f_mapping->host;
+ struct fuse_conn *fc = get_fuse_conn(inode);
- if (pos + iov_length(iov, nr_segs) > i_size_read(inode)) {
+ /*
+ * In auto invalidate mode, always update attributes on read.
+ * Otherwise, only update if we attempt to read past EOF (to ensure
+ * i_size is up to date).
+ */
+ if (fc->auto_inval_data ||
+ (pos + iov_length(iov, nr_segs) > i_size_read(inode))) {
int err;
- /*
- * If trying to read past EOF, make sure the i_size
- * attribute is up-to-date.
- */
err = fuse_update_attributes(inode, NULL, iocb->ki_filp, NULL);
if (err)
return err;
@@ -1700,7 +1703,7 @@
size_t n;
u32 max = FUSE_MAX_PAGES_PER_REQ << PAGE_SHIFT;
- for (n = 0; n < count; n++) {
+ for (n = 0; n < count; n++, iov++) {
if (iov->iov_len > (size_t) max)
return -ENOMEM;
max -= iov->iov_len;
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index 771fb63..e24dd74 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -484,6 +484,9 @@
/** Is fallocate not implemented by fs? */
unsigned no_fallocate:1;
+ /** Use enhanced/automatic page cache invalidation. */
+ unsigned auto_inval_data:1;
+
/** The number of requests waiting for completion */
atomic_t num_waiting;
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 1cd6165..ce0a283 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -197,6 +197,7 @@
struct fuse_conn *fc = get_fuse_conn(inode);
struct fuse_inode *fi = get_fuse_inode(inode);
loff_t oldsize;
+ struct timespec old_mtime;
spin_lock(&fc->lock);
if (attr_version != 0 && fi->attr_version > attr_version) {
@@ -204,15 +205,35 @@
return;
}
+ old_mtime = inode->i_mtime;
fuse_change_attributes_common(inode, attr, attr_valid);
oldsize = inode->i_size;
i_size_write(inode, attr->size);
spin_unlock(&fc->lock);
- if (S_ISREG(inode->i_mode) && oldsize != attr->size) {
- truncate_pagecache(inode, oldsize, attr->size);
- invalidate_inode_pages2(inode->i_mapping);
+ if (S_ISREG(inode->i_mode)) {
+ bool inval = false;
+
+ if (oldsize != attr->size) {
+ truncate_pagecache(inode, oldsize, attr->size);
+ inval = true;
+ } else if (fc->auto_inval_data) {
+ struct timespec new_mtime = {
+ .tv_sec = attr->mtime,
+ .tv_nsec = attr->mtimensec,
+ };
+
+ /*
+ * Auto inval mode also checks and invalidates if mtime
+ * has changed.
+ */
+ if (!timespec_equal(&old_mtime, &new_mtime))
+ inval = true;
+ }
+
+ if (inval)
+ invalidate_inode_pages2(inode->i_mapping);
}
}
@@ -834,6 +855,8 @@
fc->big_writes = 1;
if (arg->flags & FUSE_DONT_MASK)
fc->dont_mask = 1;
+ if (arg->flags & FUSE_AUTO_INVAL_DATA)
+ fc->auto_inval_data = 1;
} else {
ra_pages = fc->max_read / PAGE_CACHE_SIZE;
fc->no_lock = 1;
@@ -859,7 +882,8 @@
arg->max_readahead = fc->bdi.ra_pages * PAGE_CACHE_SIZE;
arg->flags |= FUSE_ASYNC_READ | FUSE_POSIX_LOCKS | FUSE_ATOMIC_O_TRUNC |
FUSE_EXPORT_SUPPORT | FUSE_BIG_WRITES | FUSE_DONT_MASK |
- FUSE_FLOCK_LOCKS;
+ FUSE_SPLICE_WRITE | FUSE_SPLICE_MOVE | FUSE_SPLICE_READ |
+ FUSE_FLOCK_LOCKS | FUSE_IOCTL_DIR | FUSE_AUTO_INVAL_DATA;
req->in.h.opcode = FUSE_INIT;
req->in.numargs = 1;
req->in.args[0].size = sizeof(*arg);
diff --git a/fs/gfs2/meta_io.c b/fs/gfs2/meta_io.c
index 3a56c8d..22255d9 100644
--- a/fs/gfs2/meta_io.c
+++ b/fs/gfs2/meta_io.c
@@ -52,7 +52,7 @@
/*
* If it's a fully non-blocking write attempt and we cannot
* lock the buffer then redirty the page. Note that this can
- * potentially cause a busy-wait loop from pdflush and kswapd
+ * potentially cause a busy-wait loop from flusher thread and kswapd
* activity, but those code paths have their own higher-level
* throttling.
*/
diff --git a/fs/hfs/mdb.c b/fs/hfs/mdb.c
index 5fd51a5..b7ec224 100644
--- a/fs/hfs/mdb.c
+++ b/fs/hfs/mdb.c
@@ -236,10 +236,10 @@
* hfs_mdb_commit()
*
* Description:
- * This updates the MDB on disk (look also at hfs_write_super()).
+ * This updates the MDB on disk.
* It does not check, if the superblock has been modified, or
* if the filesystem has been mounted read-only. It is mainly
- * called by hfs_write_super() and hfs_btree_extend().
+ * called by hfs_sync_fs() and flush_mdb().
* Input Variable(s):
* struct hfs_mdb *mdb: Pointer to the hfs MDB
* int backup;
diff --git a/fs/jbd/journal.c b/fs/jbd/journal.c
index 425c2f2..0935750 100644
--- a/fs/jbd/journal.c
+++ b/fs/jbd/journal.c
@@ -534,8 +534,8 @@
ret = 1;
} else if (journal->j_committing_transaction) {
/*
- * If ext3_write_super() recently started a commit, then we
- * have to wait for completion of that transaction
+ * If commit has been started, then we have to wait for
+ * completion of that transaction.
*/
if (ptid)
*ptid = journal->j_committing_transaction->t_tid;
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index e9a3c4c..e149b99 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -612,8 +612,8 @@
ret = 1;
} else if (journal->j_committing_transaction) {
/*
- * If ext3_write_super() recently started a commit, then we
- * have to wait for completion of that transaction
+ * If commit has been started, then we have to wait for
+ * completion of that transaction.
*/
if (ptid)
*ptid = journal->j_committing_transaction->t_tid;
@@ -1377,7 +1377,7 @@
* Update a journal's errno. Write updated superblock to disk waiting for IO
* to complete.
*/
-static void jbd2_journal_update_sb_errno(journal_t *journal)
+void jbd2_journal_update_sb_errno(journal_t *journal)
{
journal_superblock_t *sb = journal->j_superblock;
@@ -1390,6 +1390,7 @@
jbd2_write_superblock(journal, WRITE_SYNC);
}
+EXPORT_SYMBOL(jbd2_journal_update_sb_errno);
/*
* Read the superblock for a given journal, performing initial
diff --git a/fs/namei.c b/fs/namei.c
index 1b46439..dd1ed1b 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -352,6 +352,7 @@
/**
* sb_permission - Check superblock-level permissions
* @sb: Superblock of inode to check permission on
+ * @inode: Inode to check permission on
* @mask: Right to check for (%MAY_READ, %MAY_WRITE, %MAY_EXEC)
*
* Separate out file-system wide checks from inode-specific permission checks.
@@ -656,6 +657,7 @@
/**
* may_follow_link - Check symlink following for unsafe situations
* @link: The path of the symlink
+ * @nd: nameidata pathwalk data
*
* In the case of the sysctl_protected_symlinks sysctl being enabled,
* CAP_DAC_OVERRIDE needs to be specifically ignored if the symlink is
@@ -2414,7 +2416,7 @@
goto out;
}
- mode = op->mode & S_IALLUGO;
+ mode = op->mode;
if ((open_flag & O_CREAT) && !IS_POSIXACL(dir))
mode &= ~current_umask();
@@ -2452,7 +2454,7 @@
}
if (open_flag & O_CREAT) {
- error = may_o_create(&nd->path, dentry, op->mode);
+ error = may_o_create(&nd->path, dentry, mode);
if (error) {
create_error = error;
if (open_flag & O_EXCL)
@@ -2489,6 +2491,10 @@
dput(dentry);
dentry = file->f_path.dentry;
}
+ if (create_error && dentry->d_inode == NULL) {
+ error = create_error;
+ goto out;
+ }
goto looked_up;
}
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 8bf3a3f..b7db608 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -12,19 +12,19 @@
nfs-$(CONFIG_SYSCTL) += sysctl.o
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o
-obj-$(CONFIG_NFS_V2) += nfs2.o
-nfs2-y := nfs2super.o proc.o nfs2xdr.o
+obj-$(CONFIG_NFS_V2) += nfsv2.o
+nfsv2-y := nfs2super.o proc.o nfs2xdr.o
-obj-$(CONFIG_NFS_V3) += nfs3.o
-nfs3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
-nfs3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
+obj-$(CONFIG_NFS_V3) += nfsv3.o
+nfsv3-y := nfs3super.o nfs3client.o nfs3proc.o nfs3xdr.o
+nfsv3-$(CONFIG_NFS_V3_ACL) += nfs3acl.o
-obj-$(CONFIG_NFS_V4) += nfs4.o
-nfs4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
+obj-$(CONFIG_NFS_V4) += nfsv4.o
+nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o \
delegation.o idmap.o callback.o callback_xdr.o callback_proc.o \
nfs4namespace.o nfs4getroot.o nfs4client.o
-nfs4-$(CONFIG_SYSCTL) += nfs4sysctl.o
-nfs4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
+nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
+nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o
obj-$(CONFIG_PNFS_FILE_LAYOUT) += nfs_layout_nfsv41_files.o
nfs_layout_nfsv41_files-y := nfs4filelayout.o nfs4filelayoutdev.o
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 9fc0d9d..9969444 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -105,7 +105,7 @@
if (IS_ERR(nfs)) {
mutex_lock(&nfs_version_mutex);
- request_module("nfs%d", version);
+ request_module("nfsv%d", version);
nfs = find_nfs_version(version);
mutex_unlock(&nfs_version_mutex);
}
diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c
index b701358..a850079 100644
--- a/fs/nfs/idmap.c
+++ b/fs/nfs/idmap.c
@@ -61,6 +61,12 @@
struct mutex idmap_mutex;
};
+struct idmap_legacy_upcalldata {
+ struct rpc_pipe_msg pipe_msg;
+ struct idmap_msg idmap_msg;
+ struct idmap *idmap;
+};
+
/**
* nfs_fattr_init_names - initialise the nfs_fattr owner_name/group_name fields
* @fattr: fully initialised struct nfs_fattr
@@ -324,6 +330,7 @@
ret = nfs_idmap_request_key(&key_type_id_resolver_legacy,
name, namelen, type, data,
data_size, idmap);
+ idmap->idmap_key_cons = NULL;
mutex_unlock(&idmap->idmap_mutex);
}
return ret;
@@ -380,11 +387,13 @@
static int nfs_idmap_legacy_upcall(struct key_construction *, const char *, void *);
static ssize_t idmap_pipe_downcall(struct file *, const char __user *,
size_t);
+static void idmap_release_pipe(struct inode *);
static void idmap_pipe_destroy_msg(struct rpc_pipe_msg *);
static const struct rpc_pipe_ops idmap_upcall_ops = {
.upcall = rpc_pipe_generic_upcall,
.downcall = idmap_pipe_downcall,
+ .release_pipe = idmap_release_pipe,
.destroy_msg = idmap_pipe_destroy_msg,
};
@@ -616,7 +625,8 @@
nfs_idmap_quit_keyring();
}
-static int nfs_idmap_prepare_message(char *desc, struct idmap_msg *im,
+static int nfs_idmap_prepare_message(char *desc, struct idmap *idmap,
+ struct idmap_msg *im,
struct rpc_pipe_msg *msg)
{
substring_t substr;
@@ -659,6 +669,7 @@
const char *op,
void *aux)
{
+ struct idmap_legacy_upcalldata *data;
struct rpc_pipe_msg *msg;
struct idmap_msg *im;
struct idmap *idmap = (struct idmap *)aux;
@@ -666,15 +677,15 @@
int ret = -ENOMEM;
/* msg and im are freed in idmap_pipe_destroy_msg */
- msg = kmalloc(sizeof(*msg), GFP_KERNEL);
- if (!msg)
- goto out0;
-
- im = kmalloc(sizeof(*im), GFP_KERNEL);
- if (!im)
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
goto out1;
- ret = nfs_idmap_prepare_message(key->description, im, msg);
+ msg = &data->pipe_msg;
+ im = &data->idmap_msg;
+ data->idmap = idmap;
+
+ ret = nfs_idmap_prepare_message(key->description, idmap, im, msg);
if (ret < 0)
goto out2;
@@ -683,15 +694,15 @@
ret = rpc_queue_upcall(idmap->idmap_pipe, msg);
if (ret < 0)
- goto out2;
+ goto out3;
return ret;
+out3:
+ idmap->idmap_key_cons = NULL;
out2:
- kfree(im);
+ kfree(data);
out1:
- kfree(msg);
-out0:
complete_request_key(cons, ret);
return ret;
}
@@ -749,9 +760,8 @@
}
if (!(im.im_status & IDMAP_STATUS_SUCCESS)) {
- ret = mlen;
- complete_request_key(cons, -ENOKEY);
- goto out_incomplete;
+ ret = -ENOKEY;
+ goto out;
}
namelen_in = strnlen(im.im_name, IDMAP_NAMESZ);
@@ -768,16 +778,32 @@
out:
complete_request_key(cons, ret);
-out_incomplete:
return ret;
}
static void
idmap_pipe_destroy_msg(struct rpc_pipe_msg *msg)
{
+ struct idmap_legacy_upcalldata *data = container_of(msg,
+ struct idmap_legacy_upcalldata,
+ pipe_msg);
+ struct idmap *idmap = data->idmap;
+ struct key_construction *cons;
+ if (msg->errno) {
+ cons = ACCESS_ONCE(idmap->idmap_key_cons);
+ idmap->idmap_key_cons = NULL;
+ complete_request_key(cons, msg->errno);
+ }
/* Free memory allocated in nfs_idmap_legacy_upcall() */
- kfree(msg->data);
- kfree(msg);
+ kfree(data);
+}
+
+static void
+idmap_release_pipe(struct inode *inode)
+{
+ struct rpc_inode *rpci = RPC_I(inode);
+ struct idmap *idmap = (struct idmap *)rpci->private;
+ idmap->idmap_key_cons = NULL;
}
int nfs_map_name_to_uid(const struct nfs_server *server, const char *name, size_t namelen, __u32 *uid)
diff --git a/fs/nfs/nfs3proc.c b/fs/nfs/nfs3proc.c
index 0952c79..d6b3b5f 100644
--- a/fs/nfs/nfs3proc.c
+++ b/fs/nfs/nfs3proc.c
@@ -69,7 +69,7 @@
nfs_fattr_init(info->fattr);
status = rpc_call_sync(client, &msg, 0);
dprintk("%s: reply fsinfo: %d\n", __func__, status);
- if (!(info->fattr->valid & NFS_ATTR_FATTR)) {
+ if (status == 0 && !(info->fattr->valid & NFS_ATTR_FATTR)) {
msg.rpc_proc = &nfs3_procedures[NFS3PROC_GETATTR];
msg.rpc_resp = info->fattr;
status = rpc_call_sync(client, &msg, 0);
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 3b950dd..da0618a 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -205,6 +205,9 @@
int nfs_atomic_open(struct inode *, struct dentry *, struct file *,
unsigned, umode_t, int *);
+/* super.c */
+extern struct file_system_type nfs4_fs_type;
+
/* nfs4namespace.c */
rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *);
struct rpc_clnt *nfs4_create_sec_client(struct rpc_clnt *, struct inode *, struct qstr *);
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index cbcdfaf..24eb663 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -74,7 +74,7 @@
return clp;
error:
- kfree(clp);
+ nfs_free_client(clp);
return ERR_PTR(err);
}
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index a99a8d9..6352741 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3737,9 +3737,10 @@
static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
{
struct nfs4_cached_acl *acl;
+ size_t buflen = sizeof(*acl) + acl_len;
- if (pages && acl_len <= PAGE_SIZE) {
- acl = kmalloc(sizeof(*acl) + acl_len, GFP_KERNEL);
+ if (pages && buflen <= PAGE_SIZE) {
+ acl = kmalloc(buflen, GFP_KERNEL);
if (acl == NULL)
goto out;
acl->cached = 1;
@@ -3819,7 +3820,7 @@
if (ret)
goto out_free;
- acl_len = res.acl_len - res.acl_data_offset;
+ acl_len = res.acl_len;
if (acl_len > args.acl_len)
nfs4_write_cached_acl(inode, NULL, 0, acl_len);
else
@@ -6223,11 +6224,58 @@
dprintk("<-- %s\n", __func__);
}
+static size_t max_response_pages(struct nfs_server *server)
+{
+ u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
+ return nfs_page_array_len(0, max_resp_sz);
+}
+
+static void nfs4_free_pages(struct page **pages, size_t size)
+{
+ int i;
+
+ if (!pages)
+ return;
+
+ for (i = 0; i < size; i++) {
+ if (!pages[i])
+ break;
+ __free_page(pages[i]);
+ }
+ kfree(pages);
+}
+
+static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags)
+{
+ struct page **pages;
+ int i;
+
+ pages = kcalloc(size, sizeof(struct page *), gfp_flags);
+ if (!pages) {
+ dprintk("%s: can't alloc array of %zu pages\n", __func__, size);
+ return NULL;
+ }
+
+ for (i = 0; i < size; i++) {
+ pages[i] = alloc_page(gfp_flags);
+ if (!pages[i]) {
+ dprintk("%s: failed to allocate page\n", __func__);
+ nfs4_free_pages(pages, size);
+ return NULL;
+ }
+ }
+
+ return pages;
+}
+
static void nfs4_layoutget_release(void *calldata)
{
struct nfs4_layoutget *lgp = calldata;
+ struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+ size_t max_pages = max_response_pages(server);
dprintk("--> %s\n", __func__);
+ nfs4_free_pages(lgp->args.layout.pages, max_pages);
put_nfs_open_context(lgp->args.ctx);
kfree(calldata);
dprintk("<-- %s\n", __func__);
@@ -6239,9 +6287,10 @@
.rpc_release = nfs4_layoutget_release,
};
-int nfs4_proc_layoutget(struct nfs4_layoutget *lgp)
+void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags)
{
struct nfs_server *server = NFS_SERVER(lgp->args.inode);
+ size_t max_pages = max_response_pages(server);
struct rpc_task *task;
struct rpc_message msg = {
.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET],
@@ -6259,12 +6308,19 @@
dprintk("--> %s\n", __func__);
+ lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags);
+ if (!lgp->args.layout.pages) {
+ nfs4_layoutget_release(lgp);
+ return;
+ }
+ lgp->args.layout.pglen = max_pages * PAGE_SIZE;
+
lgp->res.layoutp = &lgp->args.layout;
lgp->res.seq_res.sr_slot = NULL;
nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0);
task = rpc_run_task(&task_setup_data);
if (IS_ERR(task))
- return PTR_ERR(task);
+ return;
status = nfs4_wait_for_completion_rpc_task(task);
if (status == 0)
status = task->tk_status;
@@ -6272,7 +6328,7 @@
status = pnfs_layout_process(lgp);
rpc_put_task(task);
dprintk("<-- %s status=%d\n", __func__, status);
- return status;
+ return;
}
static void
@@ -6304,12 +6360,8 @@
return;
}
spin_lock(&lo->plh_inode->i_lock);
- if (task->tk_status == 0) {
- if (lrp->res.lrs_present) {
- pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
- } else
- BUG_ON(!list_empty(&lo->plh_segs));
- }
+ if (task->tk_status == 0 && lrp->res.lrs_present)
+ pnfs_set_layout_stateid(lo, &lrp->res.stateid, true);
lo->plh_block_lgets--;
spin_unlock(&lo->plh_inode->i_lock);
dprintk("<-- %s\n", __func__);
diff --git a/fs/nfs/nfs4super.c b/fs/nfs/nfs4super.c
index 12a31a9..bd61221 100644
--- a/fs/nfs/nfs4super.c
+++ b/fs/nfs/nfs4super.c
@@ -23,14 +23,6 @@
static struct dentry *nfs4_remote_referral_mount(struct file_system_type *fs_type,
int flags, const char *dev_name, void *raw_data);
-static struct file_system_type nfs4_fs_type = {
- .owner = THIS_MODULE,
- .name = "nfs4",
- .mount = nfs_fs_mount,
- .kill_sb = nfs_kill_super,
- .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
-};
-
static struct file_system_type nfs4_remote_fs_type = {
.owner = THIS_MODULE,
.name = "nfs4",
@@ -344,14 +336,8 @@
if (err)
goto out1;
- err = register_filesystem(&nfs4_fs_type);
- if (err < 0)
- goto out2;
-
register_nfs_version(&nfs_v4);
return 0;
-out2:
- nfs4_unregister_sysctl();
out1:
nfs_idmap_quit();
out:
@@ -361,7 +347,6 @@
static void __exit exit_nfs_v4(void)
{
unregister_nfs_version(&nfs_v4);
- unregister_filesystem(&nfs4_fs_type);
nfs4_unregister_sysctl();
nfs_idmap_quit();
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index ca13483..1bfbd67 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -5045,22 +5045,19 @@
struct nfs_getaclres *res)
{
unsigned int savep;
- __be32 *bm_p;
uint32_t attrlen,
bitmap[3] = {0};
int status;
- size_t page_len = xdr->buf->page_len;
+ unsigned int pg_offset;
res->acl_len = 0;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto out;
- bm_p = xdr->p;
- res->acl_data_offset = be32_to_cpup(bm_p) + 2;
- res->acl_data_offset <<= 2;
- /* Check if the acl data starts beyond the allocated buffer */
- if (res->acl_data_offset > page_len)
- return -ERANGE;
+ xdr_enter_page(xdr, xdr->buf->page_len);
+
+ /* Calculate the offset of the page data */
+ pg_offset = xdr->buf->head[0].iov_len;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto out;
@@ -5074,23 +5071,20 @@
/* The bitmap (xdr len + bitmaps) and the attr xdr len words
* are stored with the acl data to handle the problem of
* variable length bitmaps.*/
- xdr->p = bm_p;
+ res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
/* We ignore &savep and don't do consistency checks on
* the attr length. Let userspace figure it out.... */
- attrlen += res->acl_data_offset;
- if (attrlen > page_len) {
+ res->acl_len = attrlen;
+ if (attrlen > (xdr->nwords << 2)) {
if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
/* getxattr interface called with a NULL buf */
- res->acl_len = attrlen;
goto out;
}
- dprintk("NFS: acl reply: attrlen %u > page_len %zu\n",
- attrlen, page_len);
+ dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
+ attrlen, xdr->nwords << 2);
return -EINVAL;
}
- xdr_read_pages(xdr, attrlen);
- res->acl_len = attrlen;
} else
status = -EOPNOTSUPP;
diff --git a/fs/nfs/objlayout/objio_osd.c b/fs/nfs/objlayout/objio_osd.c
index f50d3e8..ea6d111 100644
--- a/fs/nfs/objlayout/objio_osd.c
+++ b/fs/nfs/objlayout/objio_osd.c
@@ -570,17 +570,66 @@
return false;
return pgio->pg_count + req->wb_bytes <=
- OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+ (unsigned long)pgio->pg_layout_private;
+}
+
+void objio_init_read(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+ pnfs_generic_pg_init_read(pgio, req);
+ if (unlikely(pgio->pg_lseg == NULL))
+ return; /* Not pNFS */
+
+ pgio->pg_layout_private = (void *)
+ OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+}
+
+static bool aligned_on_raid_stripe(u64 offset, struct ore_layout *layout,
+ unsigned long *stripe_end)
+{
+ u32 stripe_off;
+ unsigned stripe_size;
+
+ if (layout->raid_algorithm == PNFS_OSD_RAID_0)
+ return true;
+
+ stripe_size = layout->stripe_unit *
+ (layout->group_width - layout->parity);
+
+ div_u64_rem(offset, stripe_size, &stripe_off);
+ if (!stripe_off)
+ return true;
+
+ *stripe_end = stripe_size - stripe_off;
+ return false;
+}
+
+void objio_init_write(struct nfs_pageio_descriptor *pgio, struct nfs_page *req)
+{
+ unsigned long stripe_end = 0;
+
+ pnfs_generic_pg_init_write(pgio, req);
+ if (unlikely(pgio->pg_lseg == NULL))
+ return; /* Not pNFS */
+
+ if (req->wb_offset ||
+ !aligned_on_raid_stripe(req->wb_index * PAGE_SIZE,
+ &OBJIO_LSEG(pgio->pg_lseg)->layout,
+ &stripe_end)) {
+ pgio->pg_layout_private = (void *)stripe_end;
+ } else {
+ pgio->pg_layout_private = (void *)
+ OBJIO_LSEG(pgio->pg_lseg)->layout.max_io_length;
+ }
}
static const struct nfs_pageio_ops objio_pg_read_ops = {
- .pg_init = pnfs_generic_pg_init_read,
+ .pg_init = objio_init_read,
.pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_readpages,
};
static const struct nfs_pageio_ops objio_pg_write_ops = {
- .pg_init = pnfs_generic_pg_init_write,
+ .pg_init = objio_init_write,
.pg_test = objio_pg_test,
.pg_doio = pnfs_generic_pg_writepages,
};
diff --git a/fs/nfs/pagelist.c b/fs/nfs/pagelist.c
index 1a6732e..311a796 100644
--- a/fs/nfs/pagelist.c
+++ b/fs/nfs/pagelist.c
@@ -49,6 +49,7 @@
hdr->io_start = req_offset(hdr->req);
hdr->good_bytes = desc->pg_count;
hdr->dreq = desc->pg_dreq;
+ hdr->layout_private = desc->pg_layout_private;
hdr->release = release;
hdr->completion_ops = desc->pg_completion_ops;
if (hdr->completion_ops->init_hdr)
@@ -268,6 +269,7 @@
desc->pg_error = 0;
desc->pg_lseg = NULL;
desc->pg_dreq = NULL;
+ desc->pg_layout_private = NULL;
}
EXPORT_SYMBOL_GPL(nfs_pageio_init);
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index 76875bf..2e00fea 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -583,9 +583,6 @@
struct nfs_server *server = NFS_SERVER(ino);
struct nfs4_layoutget *lgp;
struct pnfs_layout_segment *lseg = NULL;
- struct page **pages = NULL;
- int i;
- u32 max_resp_sz, max_pages;
dprintk("--> %s\n", __func__);
@@ -594,20 +591,6 @@
if (lgp == NULL)
return NULL;
- /* allocate pages for xdr post processing */
- max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz;
- max_pages = nfs_page_array_len(0, max_resp_sz);
-
- pages = kcalloc(max_pages, sizeof(struct page *), gfp_flags);
- if (!pages)
- goto out_err_free;
-
- for (i = 0; i < max_pages; i++) {
- pages[i] = alloc_page(gfp_flags);
- if (!pages[i])
- goto out_err_free;
- }
-
lgp->args.minlength = PAGE_CACHE_SIZE;
if (lgp->args.minlength > range->length)
lgp->args.minlength = range->length;
@@ -616,39 +599,19 @@
lgp->args.type = server->pnfs_curr_ld->id;
lgp->args.inode = ino;
lgp->args.ctx = get_nfs_open_context(ctx);
- lgp->args.layout.pages = pages;
- lgp->args.layout.pglen = max_pages * PAGE_SIZE;
lgp->lsegpp = &lseg;
lgp->gfp_flags = gfp_flags;
/* Synchronously retrieve layout information from server and
* store in lseg.
*/
- nfs4_proc_layoutget(lgp);
+ nfs4_proc_layoutget(lgp, gfp_flags);
if (!lseg) {
/* remember that LAYOUTGET failed and suspend trying */
set_bit(lo_fail_bit(range->iomode), &lo->plh_flags);
}
- /* free xdr pages */
- for (i = 0; i < max_pages; i++)
- __free_page(pages[i]);
- kfree(pages);
-
return lseg;
-
-out_err_free:
- /* free any allocated xdr pages, lgp as it's not used */
- if (pages) {
- for (i = 0; i < max_pages; i++) {
- if (!pages[i])
- break;
- __free_page(pages[i]);
- }
- kfree(pages);
- }
- kfree(lgp);
- return NULL;
}
/*
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 2c6c805..745aa1b 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -172,7 +172,7 @@
struct pnfs_devicelist *devlist);
extern int nfs4_proc_getdeviceinfo(struct nfs_server *server,
struct pnfs_device *dev);
-extern int nfs4_proc_layoutget(struct nfs4_layoutget *lgp);
+extern void nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags);
extern int nfs4_proc_layoutreturn(struct nfs4_layoutreturn *lrp);
/* pnfs.c */
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index ac6a3c5..239aff7 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -319,6 +319,34 @@
static void nfs4_validate_mount_flags(struct nfs_parsed_mount_data *);
static int nfs4_validate_mount_data(void *options,
struct nfs_parsed_mount_data *args, const char *dev_name);
+
+struct file_system_type nfs4_fs_type = {
+ .owner = THIS_MODULE,
+ .name = "nfs4",
+ .mount = nfs_fs_mount,
+ .kill_sb = nfs_kill_super,
+ .fs_flags = FS_RENAME_DOES_D_MOVE|FS_REVAL_DOT|FS_BINARY_MOUNTDATA,
+};
+EXPORT_SYMBOL_GPL(nfs4_fs_type);
+
+static int __init register_nfs4_fs(void)
+{
+ return register_filesystem(&nfs4_fs_type);
+}
+
+static void unregister_nfs4_fs(void)
+{
+ unregister_filesystem(&nfs4_fs_type);
+}
+#else
+static int __init register_nfs4_fs(void)
+{
+ return 0;
+}
+
+static void unregister_nfs4_fs(void)
+{
+}
#endif
static struct shrinker acl_shrinker = {
@@ -337,12 +365,18 @@
if (ret < 0)
goto error_0;
- ret = nfs_register_sysctl();
+ ret = register_nfs4_fs();
if (ret < 0)
goto error_1;
+
+ ret = nfs_register_sysctl();
+ if (ret < 0)
+ goto error_2;
register_shrinker(&acl_shrinker);
return 0;
+error_2:
+ unregister_nfs4_fs();
error_1:
unregister_filesystem(&nfs_fs_type);
error_0:
@@ -356,6 +390,7 @@
{
unregister_shrinker(&acl_shrinker);
nfs_unregister_sysctl();
+ unregister_nfs4_fs();
unregister_filesystem(&nfs_fs_type);
}
@@ -2645,4 +2680,6 @@
module_param(send_implementation_id, ushort, 0644);
MODULE_PARM_DESC(send_implementation_id,
"Send implementation ID with NFSv4.1 exchange_id");
+MODULE_ALIAS("nfs4");
+
#endif /* CONFIG_NFS_V4 */
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 5829d0c..e3b5537 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -1814,19 +1814,19 @@
nfs_wdata_mempool = mempool_create_slab_pool(MIN_POOL_WRITE,
nfs_wdata_cachep);
if (nfs_wdata_mempool == NULL)
- return -ENOMEM;
+ goto out_destroy_write_cache;
nfs_cdata_cachep = kmem_cache_create("nfs_commit_data",
sizeof(struct nfs_commit_data),
0, SLAB_HWCACHE_ALIGN,
NULL);
if (nfs_cdata_cachep == NULL)
- return -ENOMEM;
+ goto out_destroy_write_mempool;
nfs_commit_mempool = mempool_create_slab_pool(MIN_POOL_COMMIT,
nfs_wdata_cachep);
if (nfs_commit_mempool == NULL)
- return -ENOMEM;
+ goto out_destroy_commit_cache;
/*
* NFS congestion size, scale with available memory.
@@ -1849,11 +1849,20 @@
nfs_congestion_kb = 256*1024;
return 0;
+
+out_destroy_commit_cache:
+ kmem_cache_destroy(nfs_cdata_cachep);
+out_destroy_write_mempool:
+ mempool_destroy(nfs_wdata_mempool);
+out_destroy_write_cache:
+ kmem_cache_destroy(nfs_wdata_cachep);
+ return -ENOMEM;
}
void nfs_destroy_writepagecache(void)
{
mempool_destroy(nfs_commit_mempool);
+ kmem_cache_destroy(nfs_cdata_cachep);
mempool_destroy(nfs_wdata_mempool);
kmem_cache_destroy(nfs_wdata_cachep);
}
diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
index 6522cac..6a10812 100644
--- a/fs/nilfs2/super.c
+++ b/fs/nilfs2/super.c
@@ -676,17 +676,13 @@
.alloc_inode = nilfs_alloc_inode,
.destroy_inode = nilfs_destroy_inode,
.dirty_inode = nilfs_dirty_inode,
- /* .write_inode = nilfs_write_inode, */
- /* .drop_inode = nilfs_drop_inode, */
.evict_inode = nilfs_evict_inode,
.put_super = nilfs_put_super,
- /* .write_super = nilfs_write_super, */
.sync_fs = nilfs_sync_fs,
.freeze_fs = nilfs_freeze,
.unfreeze_fs = nilfs_unfreeze,
.statfs = nilfs_statfs,
.remount_fs = nilfs_remount,
- /* .umount_begin */
.show_options = nilfs_show_options
};
diff --git a/fs/nilfs2/the_nilfs.h b/fs/nilfs2/the_nilfs.h
index 6eee417..be1267a 100644
--- a/fs/nilfs2/the_nilfs.h
+++ b/fs/nilfs2/the_nilfs.h
@@ -107,8 +107,6 @@
* used for
* - loading the latest checkpoint exclusively.
* - allocating a new full segment.
- * - protecting s_dirt in the super_block struct
- * (see nilfs_write_super) and the following fields.
*/
struct buffer_head *ns_sbh[2];
struct nilfs_super_block *ns_sbp[2];
diff --git a/fs/open.c b/fs/open.c
index f3d96e7..e1f2cdb 100644
--- a/fs/open.c
+++ b/fs/open.c
@@ -717,7 +717,7 @@
* here, so just reset the state.
*/
file_reset_write(f);
- mnt_drop_write(f->f_path.mnt);
+ __mnt_drop_write(f->f_path.mnt);
}
}
cleanup_file:
@@ -852,9 +852,10 @@
int lookup_flags = 0;
int acc_mode;
- if (!(flags & O_CREAT))
- mode = 0;
- op->mode = mode;
+ if (flags & O_CREAT)
+ op->mode = (mode & S_IALLUGO) | S_IFREG;
+ else
+ op->mode = 0;
/* Must never be set by userspace */
flags &= ~FMODE_NONOTIFY;
diff --git a/fs/super.c b/fs/super.c
index b05cf47..0902cfa 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -537,46 +537,6 @@
EXPORT_SYMBOL(drop_super);
/**
- * sync_supers - helper for periodic superblock writeback
- *
- * Call the write_super method if present on all dirty superblocks in
- * the system. This is for the periodic writeback used by most older
- * filesystems. For data integrity superblock writeback use
- * sync_filesystems() instead.
- *
- * Note: check the dirty flag before waiting, so we don't
- * hold up the sync while mounting a device. (The newly
- * mounted device won't need syncing.)
- */
-void sync_supers(void)
-{
- struct super_block *sb, *p = NULL;
-
- spin_lock(&sb_lock);
- list_for_each_entry(sb, &super_blocks, s_list) {
- if (hlist_unhashed(&sb->s_instances))
- continue;
- if (sb->s_op->write_super && sb->s_dirt) {
- sb->s_count++;
- spin_unlock(&sb_lock);
-
- down_read(&sb->s_umount);
- if (sb->s_root && sb->s_dirt && (sb->s_flags & MS_BORN))
- sb->s_op->write_super(sb);
- up_read(&sb->s_umount);
-
- spin_lock(&sb_lock);
- if (p)
- __put_super(p);
- p = sb;
- }
- }
- if (p)
- __put_super(p);
- spin_unlock(&sb_lock);
-}
-
-/**
* iterate_supers - call function for all active superblocks
* @f: function to call
* @arg: argument to pass to it
diff --git a/fs/ubifs/file.c b/fs/ubifs/file.c
index 35389ca..7bd6e72 100644
--- a/fs/ubifs/file.c
+++ b/fs/ubifs/file.c
@@ -37,11 +37,11 @@
*
* A thing to keep in mind: inode @i_mutex is locked in most VFS operations we
* implement. However, this is not true for 'ubifs_writepage()', which may be
- * called with @i_mutex unlocked. For example, when pdflush is doing background
- * write-back, it calls 'ubifs_writepage()' with unlocked @i_mutex. At "normal"
- * work-paths the @i_mutex is locked in 'ubifs_writepage()', e.g. in the
- * "sys_write -> alloc_pages -> direct reclaim path". So, in 'ubifs_writepage()'
- * we are only guaranteed that the page is locked.
+ * called with @i_mutex unlocked. For example, when flusher thread is doing
+ * background write-back, it calls 'ubifs_writepage()' with unlocked @i_mutex.
+ * At "normal" work-paths the @i_mutex is locked in 'ubifs_writepage()', e.g.
+ * in the "sys_write -> alloc_pages -> direct reclaim path". So, in
+ * 'ubifs_writepage()' we are only guaranteed that the page is locked.
*
* Similarly, @i_mutex is not always locked in 'ubifs_readpage()', e.g., the
* read-ahead path does not lock it ("sys_read -> generic_file_aio_read ->
diff --git a/fs/ubifs/super.c b/fs/ubifs/super.c
index 1c766c3..c3fa6c5 100644
--- a/fs/ubifs/super.c
+++ b/fs/ubifs/super.c
@@ -303,7 +303,7 @@
mutex_lock(&ui->ui_mutex);
/*
* Due to races between write-back forced by budgeting
- * (see 'sync_some_inodes()') and pdflush write-back, the inode may
+ * (see 'sync_some_inodes()') and background write-back, the inode may
* have already been synchronized, do not do this again. This might
* also happen if it was synchronized in an VFS operation, e.g.
* 'ubifs_link()'.
diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h
index 2c744c7..26a92fc 100644
--- a/include/acpi/acpixf.h
+++ b/include/acpi/acpixf.h
@@ -491,11 +491,11 @@
acpi_status acpi_enter_sleep_state_prep(u8 sleep_state);
-acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state, u8 flags);
+acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state);
ACPI_HW_DEPENDENT_RETURN_STATUS(acpi_status asmlinkage acpi_enter_sleep_state_s4bios(void))
-acpi_status acpi_leave_sleep_state_prep(u8 sleep_state, u8 flags);
+acpi_status acpi_leave_sleep_state_prep(u8 sleep_state);
acpi_status acpi_leave_sleep_state(u8 sleep_state);
diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h
index 3af87de..3d00bd5 100644
--- a/include/acpi/actypes.h
+++ b/include/acpi/actypes.h
@@ -803,7 +803,7 @@
/* Sleep function dispatch */
-typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state, u8 flags);
+typedef acpi_status(*ACPI_SLEEP_FUNCTION) (u8 sleep_state);
struct acpi_sleep_functions {
ACPI_SLEEP_FUNCTION legacy_function;
diff --git a/include/asm-generic/mutex-xchg.h b/include/asm-generic/mutex-xchg.h
index 580a6d3..c04e0db 100644
--- a/include/asm-generic/mutex-xchg.h
+++ b/include/asm-generic/mutex-xchg.h
@@ -26,7 +26,13 @@
__mutex_fastpath_lock(atomic_t *count, void (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 0) != 1))
- fail_fn(count);
+ /*
+ * We failed to acquire the lock, so mark it contended
+ * to ensure that any waiting tasks are woken up by the
+ * unlock slow path.
+ */
+ if (likely(atomic_xchg(count, -1) != 1))
+ fail_fn(count);
}
/**
@@ -43,7 +49,8 @@
__mutex_fastpath_lock_retval(atomic_t *count, int (*fail_fn)(atomic_t *))
{
if (unlikely(atomic_xchg(count, 0) != 1))
- return fail_fn(count);
+ if (likely(atomic_xchg(count, -1) != 1))
+ return fail_fn(count);
return 0;
}
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index a1a0386..ced3625 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -166,8 +166,6 @@
int crtc_vsync_start;
int crtc_vsync_end;
int crtc_vtotal;
- int crtc_hadjusted;
- int crtc_vadjusted;
/* Driver private mode info */
int private_size;
diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h
index 7ff5c99..c78bb99 100644
--- a/include/drm/drm_pciids.h
+++ b/include/drm/drm_pciids.h
@@ -213,9 +213,12 @@
{0x1002, 0x6800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6801, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6802, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6806, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6808, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6809, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6810, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6816, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x6817, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6818, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6819, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_PITCAIRN|RADEON_NEW_MEMMAP}, \
{0x1002, 0x6820, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_VERDE|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
index 5805686..dc3a8cd 100644
--- a/include/drm/radeon_drm.h
+++ b/include/drm/radeon_drm.h
@@ -964,6 +964,8 @@
#define RADEON_INFO_IB_VM_MAX_SIZE 0x0f
/* max pipes - needed for compute shaders */
#define RADEON_INFO_MAX_PIPES 0x10
+/* timestamp for GL_ARB_timer_query (OpenGL), returns the current GPU clock */
+#define RADEON_INFO_TIMESTAMP 0x11
struct drm_radeon_info {
uint32_t request;
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index d9a7544..fa21760 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -391,6 +391,7 @@
header-y += v4l2-mediabus.h
header-y += v4l2-subdev.h
header-y += veth.h
+header-y += vfio.h
header-y += vhost.h
header-y += videodev2.h
header-y += virtio_9p.h
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 3ad510b..4f2a762 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -96,7 +96,7 @@
void acpi_numa_slit_init (struct acpi_table_slit *slit);
void acpi_numa_processor_affinity_init (struct acpi_srat_cpu_affinity *pa);
void acpi_numa_x2apic_affinity_init(struct acpi_srat_x2apic_cpu_affinity *pa);
-void acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
+int acpi_numa_memory_affinity_init (struct acpi_srat_mem_affinity *ma);
void acpi_numa_arch_fixup(void);
#ifdef CONFIG_ACPI_HOTPLUG_CPU
diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h
index c97c6b9..2a9a9ab 100644
--- a/include/linux/backing-dev.h
+++ b/include/linux/backing-dev.h
@@ -124,7 +124,6 @@
void bdi_start_background_writeback(struct backing_dev_info *bdi);
int bdi_writeback_thread(void *data);
int bdi_has_dirty_io(struct backing_dev_info *bdi);
-void bdi_arm_supers_timer(void);
void bdi_wakeup_thread_delayed(struct backing_dev_info *bdi);
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2);
diff --git a/include/linux/bcd.h b/include/linux/bcd.h
index 22ea563..18fff11 100644
--- a/include/linux/bcd.h
+++ b/include/linux/bcd.h
@@ -3,7 +3,20 @@
#include <linux/compiler.h>
-unsigned bcd2bin(unsigned char val) __attribute_const__;
-unsigned char bin2bcd(unsigned val) __attribute_const__;
+#define bcd2bin(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bcd2bin(x) : \
+ _bcd2bin(x))
+
+#define bin2bcd(x) \
+ (__builtin_constant_p((u8 )(x)) ? \
+ const_bin2bcd(x) : \
+ _bin2bcd(x))
+
+#define const_bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10)
+#define const_bin2bcd(x) ((((x) / 10) << 4) + (x) % 10)
+
+unsigned _bcd2bin(unsigned char val) __attribute_const__;
+unsigned char _bin2bcd(unsigned val) __attribute_const__;
#endif /* _BCD_H */
diff --git a/include/linux/bcma/bcma_driver_chipcommon.h b/include/linux/bcma/bcma_driver_chipcommon.h
index 3c80885..d323a4b 100644
--- a/include/linux/bcma/bcma_driver_chipcommon.h
+++ b/include/linux/bcma/bcma_driver_chipcommon.h
@@ -89,6 +89,12 @@
#define BCMA_CC_CHIPST_4313_OTP_PRESENT 2
#define BCMA_CC_CHIPST_4331_SPROM_PRESENT 2
#define BCMA_CC_CHIPST_4331_OTP_PRESENT 4
+#define BCMA_CC_CHIPST_43228_ILP_DIV_EN 0x00000001
+#define BCMA_CC_CHIPST_43228_OTP_PRESENT 0x00000002
+#define BCMA_CC_CHIPST_43228_SERDES_REFCLK_PADSEL 0x00000004
+#define BCMA_CC_CHIPST_43228_SDIO_MODE 0x00000008
+#define BCMA_CC_CHIPST_43228_SDIO_OTP_PRESENT 0x00000010
+#define BCMA_CC_CHIPST_43228_SDIO_RESET 0x00000020
#define BCMA_CC_CHIPST_4706_PKG_OPTION BIT(0) /* 0: full-featured package 1: low-cost package */
#define BCMA_CC_CHIPST_4706_SFLASH_PRESENT BIT(1) /* 0: parallel, 1: serial flash is present */
#define BCMA_CC_CHIPST_4706_SFLASH_TYPE BIT(2) /* 0: 8b-p/ST-s flash, 1: 16b-p/Atmal-s flash */
diff --git a/include/linux/can.h b/include/linux/can.h
index 018055e..e52958d 100644
--- a/include/linux/can.h
+++ b/include/linux/can.h
@@ -74,20 +74,21 @@
/*
* defined bits for canfd_frame.flags
*
- * As the default for CAN FD should be to support the high data rate in the
- * payload section of the frame (HDR) and to support up to 64 byte in the
- * data section (EDL) the bits are only set in the non-default case.
- * Btw. as long as there's no real implementation for CAN FD network driver
- * these bits are only preliminary.
+ * The use of struct canfd_frame implies the Extended Data Length (EDL) bit to
+ * be set in the CAN frame bitstream on the wire. The EDL bit switch turns
+ * the CAN controllers bitstream processor into the CAN FD mode which creates
+ * two new options within the CAN FD frame specification:
*
- * RX: NOHDR/NOEDL - info about received CAN FD frame
- * ESI - bit from originating CAN controller
- * TX: NOHDR/NOEDL - control per-frame settings if supported by CAN controller
- * ESI - bit is set by local CAN controller
+ * Bit Rate Switch - to indicate a second bitrate is/was used for the payload
+ * Error State Indicator - represents the error state of the transmitting node
+ *
+ * As the CANFD_ESI bit is internally generated by the transmitting CAN
+ * controller only the CANFD_BRS bit is relevant for real CAN controllers when
+ * building a CAN FD frame for transmission. Setting the CANFD_ESI bit can make
+ * sense for virtual CAN interfaces to test applications with echoed frames.
*/
-#define CANFD_NOHDR 0x01 /* frame without high data rate */
-#define CANFD_NOEDL 0x02 /* frame without extended data length */
-#define CANFD_ESI 0x04 /* error state indicator */
+#define CANFD_BRS 0x01 /* bit rate switch (second bitrate for payload data) */
+#define CANFD_ESI 0x02 /* error state indicator of the transmitting node */
/**
* struct canfd_frame - CAN flexible data rate frame structure
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index 133ddcf..ef65814 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -22,7 +22,7 @@
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *mask,
- bool sync);
+ bool sync, bool *contended);
extern int compact_pgdat(pg_data_t *pgdat, int order);
extern unsigned long compaction_suitable(struct zone *zone, int order);
@@ -64,7 +64,7 @@
#else
static inline unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
return COMPACT_CONTINUE;
}
diff --git a/include/linux/efi.h b/include/linux/efi.h
index 103adc6..ec45ccd 100644
--- a/include/linux/efi.h
+++ b/include/linux/efi.h
@@ -503,6 +503,8 @@
extern int __init efi_uart_console_only (void);
extern void efi_initialize_iomem_resources(struct resource *code_resource,
struct resource *data_resource, struct resource *bss_resource);
+extern unsigned long efi_get_time(void);
+extern int efi_set_rtc_mmss(unsigned long nowtime);
extern void efi_reserve_boot_services(void);
extern struct efi_memory_map memmap;
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 38dba16..aa11047 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1491,7 +1491,6 @@
struct super_block {
struct list_head s_list; /* Keep this first */
dev_t s_dev; /* search index; _not_ kdev_t */
- unsigned char s_dirt;
unsigned char s_blocksize_bits;
unsigned long s_blocksize;
loff_t s_maxbytes; /* Max file size */
@@ -1861,7 +1860,6 @@
int (*drop_inode) (struct inode *);
void (*evict_inode) (struct inode *);
void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
int (*freeze_fs) (struct super_block *);
int (*unfreeze_fs) (struct super_block *);
@@ -2397,7 +2395,6 @@
int datasync);
extern int vfs_fsync(struct file *file, int datasync);
extern int generic_write_sync(struct file *file, loff_t pos, loff_t count);
-extern void sync_supers(void);
extern void emergency_sync(void);
extern void emergency_remount(void);
#ifdef CONFIG_BLOCK
diff --git a/include/linux/fsl_devices.h b/include/linux/fsl_devices.h
index 15be561..ccfc4bb 100644
--- a/include/linux/fsl_devices.h
+++ b/include/linux/fsl_devices.h
@@ -19,6 +19,7 @@
#define FSL_UTMI_PHY_DLY 10 /*As per P1010RM, delay for UTMI
PHY CLK to become stable - 10ms*/
+#define FSL_USB_PHY_CLK_TIMEOUT 1000 /* uSec */
#define FSL_USB_VER_OLD 0
#define FSL_USB_VER_1_6 1
#define FSL_USB_VER_2_2 2
diff --git a/include/linux/ftrace_event.h b/include/linux/ftrace_event.h
index af961d6..642928c 100644
--- a/include/linux/ftrace_event.h
+++ b/include/linux/ftrace_event.h
@@ -306,9 +306,10 @@
static inline void
perf_trace_buf_submit(void *raw_data, int size, int rctx, u64 addr,
- u64 count, struct pt_regs *regs, void *head)
+ u64 count, struct pt_regs *regs, void *head,
+ struct task_struct *task)
{
- perf_tp_event(addr, count, raw_data, size, regs, head, rctx);
+ perf_tp_event(addr, count, raw_data, size, regs, head, rctx, task);
}
#endif
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 9303348..d8c713e 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -57,6 +57,9 @@
*
* 7.19
* - add FUSE_FALLOCATE
+ *
+ * 7.20
+ * - add FUSE_AUTO_INVAL_DATA
*/
#ifndef _LINUX_FUSE_H
@@ -88,7 +91,7 @@
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 19
+#define FUSE_KERNEL_MINOR_VERSION 20
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
@@ -163,10 +166,19 @@
/**
* INIT request/reply flags
*
+ * FUSE_ASYNC_READ: asynchronous read requests
* FUSE_POSIX_LOCKS: remote locking for POSIX file locks
+ * FUSE_FILE_OPS: kernel sends file handle for fstat, etc... (not yet supported)
+ * FUSE_ATOMIC_O_TRUNC: handles the O_TRUNC open flag in the filesystem
* FUSE_EXPORT_SUPPORT: filesystem handles lookups of "." and ".."
+ * FUSE_BIG_WRITES: filesystem can handle write size larger than 4kB
* FUSE_DONT_MASK: don't apply umask to file mode on create operations
+ * FUSE_SPLICE_WRITE: kernel supports splice write on the device
+ * FUSE_SPLICE_MOVE: kernel supports splice move on the device
+ * FUSE_SPLICE_READ: kernel supports splice read on the device
* FUSE_FLOCK_LOCKS: remote locking for BSD style file locks
+ * FUSE_HAS_IOCTL_DIR: kernel supports ioctl on directories
+ * FUSE_AUTO_INVAL_DATA: automatically invalidate cached pages
*/
#define FUSE_ASYNC_READ (1 << 0)
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -175,7 +187,12 @@
#define FUSE_EXPORT_SUPPORT (1 << 4)
#define FUSE_BIG_WRITES (1 << 5)
#define FUSE_DONT_MASK (1 << 6)
+#define FUSE_SPLICE_WRITE (1 << 7)
+#define FUSE_SPLICE_MOVE (1 << 8)
+#define FUSE_SPLICE_READ (1 << 9)
#define FUSE_FLOCK_LOCKS (1 << 10)
+#define FUSE_HAS_IOCTL_DIR (1 << 11)
+#define FUSE_AUTO_INVAL_DATA (1 << 12)
/**
* CUSE INIT request/reply flags
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h
index bb7f309..305f23c 100644
--- a/include/linux/hardirq.h
+++ b/include/linux/hardirq.h
@@ -22,7 +22,7 @@
*
* - bits 16-25 are the hardirq count (max # of nested hardirqs: 1024)
* - bit 26 is the NMI_MASK
- * - bit 28 is the PREEMPT_ACTIVE flag
+ * - bit 27 is the PREEMPT_ACTIVE flag
*
* PREEMPT_MASK: 0x000000ff
* SOFTIRQ_MASK: 0x0000ff00
diff --git a/include/linux/if_team.h b/include/linux/if_team.h
index 6960fc1..aa2e167 100644
--- a/include/linux/if_team.h
+++ b/include/linux/if_team.h
@@ -96,21 +96,6 @@
}
#endif
-static inline int team_dev_queue_xmit(struct team *team, struct team_port *port,
- struct sk_buff *skb)
-{
- BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
- sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
- skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping);
-
- skb->dev = port->dev;
- if (unlikely(netpoll_tx_running(port->dev))) {
- team_netpoll_send_skb(port, skb);
- return 0;
- }
- return dev_queue_xmit(skb);
-}
-
struct team_mode_ops {
int (*init)(struct team *team);
void (*exit)(struct team *team);
@@ -200,6 +185,21 @@
long mode_priv[TEAM_MODE_PRIV_LONGS];
};
+static inline int team_dev_queue_xmit(struct team *team, struct team_port *port,
+ struct sk_buff *skb)
+{
+ BUILD_BUG_ON(sizeof(skb->queue_mapping) !=
+ sizeof(qdisc_skb_cb(skb)->slave_dev_queue_mapping));
+ skb_set_queue_mapping(skb, qdisc_skb_cb(skb)->slave_dev_queue_mapping);
+
+ skb->dev = port->dev;
+ if (unlikely(netpoll_tx_running(team->dev))) {
+ team_netpoll_send_skb(port, skb);
+ return 0;
+ }
+ return dev_queue_xmit(skb);
+}
+
static inline struct hlist_head *team_port_index_hash(struct team *team,
int port_index)
{
diff --git a/include/linux/iio/frequency/adf4350.h b/include/linux/iio/frequency/adf4350.h
index b76b4a8..be91f34 100644
--- a/include/linux/iio/frequency/adf4350.h
+++ b/include/linux/iio/frequency/adf4350.h
@@ -87,6 +87,8 @@
#define ADF4350_MAX_BANDSEL_CLK 125000 /* Hz */
#define ADF4350_MAX_FREQ_REFIN 250000000 /* Hz */
#define ADF4350_MAX_MODULUS 4095
+#define ADF4350_MAX_R_CNT 1023
+
/**
* struct adf4350_platform_data - platform specific information
diff --git a/include/linux/input/eeti_ts.h b/include/linux/input/eeti_ts.h
index f875b31..16625d7 100644
--- a/include/linux/input/eeti_ts.h
+++ b/include/linux/input/eeti_ts.h
@@ -2,6 +2,7 @@
#define LINUX_INPUT_EETI_TS_H
struct eeti_ts_platform_data {
+ int irq_gpio;
unsigned int irq_active_high;
};
diff --git a/include/linux/iommu.h b/include/linux/iommu.h
index 54d6d69..7e83370 100644
--- a/include/linux/iommu.h
+++ b/include/linux/iommu.h
@@ -20,6 +20,7 @@
#define __LINUX_IOMMU_H
#include <linux/errno.h>
+#include <linux/types.h>
#define IOMMU_READ (1)
#define IOMMU_WRITE (2)
@@ -30,6 +31,7 @@
struct bus_type;
struct device;
struct iommu_domain;
+struct notifier_block;
/* iommu fault flags */
#define IOMMU_FAULT_READ 0x0
diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h
index 379e433..879db26 100644
--- a/include/linux/ipv6.h
+++ b/include/linux/ipv6.h
@@ -369,6 +369,7 @@
__u8 rcv_tclass;
__u32 dst_cookie;
+ __u32 rx_dst_cookie;
struct ipv6_mc_socklist __rcu *ipv6_mc_list;
struct ipv6_ac_socklist *ipv6_ac_list;
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 553fb66..216b0ba 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -349,6 +349,7 @@
IRQCHIP_MASK_ON_SUSPEND = (1 << 2),
IRQCHIP_ONOFFLINE_ENABLED = (1 << 3),
IRQCHIP_SKIP_SET_WAKE = (1 << 4),
+ IRQCHIP_ONESHOT_SAFE = (1 << 5),
};
/* This include will go away once we isolated irq_desc usage to core code */
diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h
index f334c7f..3efc43f 100644
--- a/include/linux/jbd2.h
+++ b/include/linux/jbd2.h
@@ -1125,6 +1125,7 @@
extern int jbd2_journal_recover (journal_t *journal);
extern int jbd2_journal_wipe (journal_t *, int);
extern int jbd2_journal_skip_recovery (journal_t *);
+extern void jbd2_journal_update_sb_errno(journal_t *);
extern void jbd2_journal_update_sb_log_tail (journal_t *, tid_t,
unsigned long, int);
extern void __jbd2_journal_abort_hard (journal_t *);
diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 265e2c3..8268054 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -39,9 +39,6 @@
# error Invalid value of HZ.
#endif
-/* LATCH is used in the interval timer and ftape setup. */
-#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
-
/* Suppose we want to divide two numbers NOM and DEN: NOM/DEN, then we can
* improve accuracy by shifting LSH bits, hence calculating:
* (NOM << LSH) / DEN
@@ -54,18 +51,30 @@
#define SH_DIV(NOM,DEN,LSH) ( (((NOM) / (DEN)) << (LSH)) \
+ ((((NOM) % (DEN)) << (LSH)) + (DEN) / 2) / (DEN))
-/* HZ is the requested value. ACTHZ is actual HZ ("<< 8" is for accuracy) */
-#define ACTHZ (SH_DIV (CLOCK_TICK_RATE, LATCH, 8))
+#ifdef CLOCK_TICK_RATE
+/* LATCH is used in the interval timer and ftape setup. */
+# define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
-/* TICK_NSEC is the time between ticks in nsec assuming real ACTHZ */
-#define TICK_NSEC (SH_DIV (1000000UL * 1000, ACTHZ, 8))
+/*
+ * HZ is the requested value. However the CLOCK_TICK_RATE may not allow
+ * for exactly HZ. So SHIFTED_HZ is high res HZ ("<< 8" is for accuracy)
+ */
+# define SHIFTED_HZ (SH_DIV(CLOCK_TICK_RATE, LATCH, 8))
+#else
+# define SHIFTED_HZ (HZ << 8)
+#endif
+
+/* TICK_NSEC is the time between ticks in nsec assuming SHIFTED_HZ */
+#define TICK_NSEC (SH_DIV(1000000UL * 1000, SHIFTED_HZ, 8))
/* TICK_USEC is the time between ticks in usec assuming fake USER_HZ */
#define TICK_USEC ((1000000UL + USER_HZ/2) / USER_HZ)
-/* TICK_USEC_TO_NSEC is the time between ticks in nsec assuming real ACTHZ and */
-/* a value TUSEC for TICK_USEC (can be set bij adjtimex) */
-#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV (TUSEC * USER_HZ * 1000, ACTHZ, 8))
+/*
+ * TICK_USEC_TO_NSEC is the time between ticks in nsec assuming SHIFTED_HZ and
+ * a value TUSEC for TICK_USEC (can be set bij adjtimex)
+ */
+#define TICK_USEC_TO_NSEC(TUSEC) (SH_DIV(TUSEC * USER_HZ * 1000, SHIFTED_HZ, 8))
/* some arch's have a small-data section that can be accessed register-relative
* but that can only take up to, say, 4-byte variables. jiffies being part of
diff --git a/include/linux/kdb.h b/include/linux/kdb.h
index 0647258..42d9e86 100644
--- a/include/linux/kdb.h
+++ b/include/linux/kdb.h
@@ -75,8 +75,6 @@
#define KDB_FLAG_CATASTROPHIC (1 << 1) /* A catastrophic event has occurred */
#define KDB_FLAG_CMD_INTERRUPT (1 << 2) /* Previous command was interrupted */
#define KDB_FLAG_NOIPI (1 << 3) /* Do not send IPIs */
-#define KDB_FLAG_ONLY_DO_DUMP (1 << 4) /* Only do a dump, used when
- * kdb is off */
#define KDB_FLAG_NO_CONSOLE (1 << 5) /* No console is available,
* kdb is disabled */
#define KDB_FLAG_NO_VT_CONSOLE (1 << 6) /* No VT console is available, do
diff --git a/include/linux/kref.h b/include/linux/kref.h
index 9c07dce..65af688 100644
--- a/include/linux/kref.h
+++ b/include/linux/kref.h
@@ -18,6 +18,7 @@
#include <linux/bug.h>
#include <linux/atomic.h>
#include <linux/kernel.h>
+#include <linux/mutex.h>
struct kref {
atomic_t refcount;
@@ -93,4 +94,21 @@
{
return kref_sub(kref, 1, release);
}
+
+static inline int kref_put_mutex(struct kref *kref,
+ void (*release)(struct kref *kref),
+ struct mutex *lock)
+{
+ WARN_ON(release == NULL);
+ if (unlikely(!atomic_add_unless(&kref->refcount, -1, 1))) {
+ mutex_lock(lock);
+ if (unlikely(!atomic_dec_and_test(&kref->refcount))) {
+ mutex_unlock(lock);
+ return 0;
+ }
+ release(kref);
+ return 1;
+ }
+ return 0;
+}
#endif /* _KREF_H_ */
diff --git a/include/linux/mfd/ezx-pcap.h b/include/linux/mfd/ezx-pcap.h
index 40c37216..32a1b5c 100644
--- a/include/linux/mfd/ezx-pcap.h
+++ b/include/linux/mfd/ezx-pcap.h
@@ -16,6 +16,7 @@
struct pcap_platform_data {
unsigned int irq_base;
unsigned int config;
+ int gpio;
void (*init) (void *); /* board specific init */
int num_subdevs;
struct pcap_subdev *subdevs;
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index eb06e58..59dc05f3 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -953,7 +953,8 @@
#ifdef CONFIG_NET_POLL_CONTROLLER
void (*ndo_poll_controller)(struct net_device *dev);
int (*ndo_netpoll_setup)(struct net_device *dev,
- struct netpoll_info *info);
+ struct netpoll_info *info,
+ gfp_t gfp);
void (*ndo_netpoll_cleanup)(struct net_device *dev);
#endif
int (*ndo_set_vf_mac)(struct net_device *dev,
@@ -1300,6 +1301,8 @@
/* for setting kernel sock attribute on TCP connection setup */
#define GSO_MAX_SIZE 65536
unsigned int gso_max_size;
+#define GSO_MAX_SEGS 65535
+ u16 gso_max_segs;
#ifdef CONFIG_DCB
/* Data Center Bridging netlink ops */
@@ -1519,6 +1522,8 @@
struct sk_buff **(*gro_receive)(struct sk_buff **head,
struct sk_buff *skb);
int (*gro_complete)(struct sk_buff *skb);
+ bool (*id_match)(struct packet_type *ptype,
+ struct sock *sk);
void *af_packet_priv;
struct list_head list;
};
diff --git a/include/linux/netfilter/nf_conntrack_sip.h b/include/linux/netfilter/nf_conntrack_sip.h
index 0dfc8b7..89f2a62 100644
--- a/include/linux/netfilter/nf_conntrack_sip.h
+++ b/include/linux/netfilter/nf_conntrack_sip.h
@@ -164,7 +164,7 @@
unsigned int dataoff, unsigned int datalen,
const char *name,
unsigned int *matchoff, unsigned int *matchlen,
- union nf_inet_addr *addr);
+ union nf_inet_addr *addr, bool delim);
extern int ct_sip_parse_numerical_param(const struct nf_conn *ct, const char *dptr,
unsigned int off, unsigned int datalen,
const char *name,
diff --git a/include/linux/netpoll.h b/include/linux/netpoll.h
index 28f5389..66d5379 100644
--- a/include/linux/netpoll.h
+++ b/include/linux/netpoll.h
@@ -23,6 +23,7 @@
u8 remote_mac[ETH_ALEN];
struct list_head rx; /* rx_np list element */
+ struct rcu_head rcu;
};
struct netpoll_info {
@@ -38,28 +39,40 @@
struct delayed_work tx_work;
struct netpoll *netpoll;
+ struct rcu_head rcu;
};
void netpoll_send_udp(struct netpoll *np, const char *msg, int len);
void netpoll_print_options(struct netpoll *np);
int netpoll_parse_options(struct netpoll *np, char *opt);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev);
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp);
int netpoll_setup(struct netpoll *np);
int netpoll_trap(void);
void netpoll_set_trap(int trap);
void __netpoll_cleanup(struct netpoll *np);
+void __netpoll_free_rcu(struct netpoll *np);
void netpoll_cleanup(struct netpoll *np);
-int __netpoll_rx(struct sk_buff *skb);
+int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo);
void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
struct net_device *dev);
static inline void netpoll_send_skb(struct netpoll *np, struct sk_buff *skb)
{
+ unsigned long flags;
+ local_irq_save(flags);
netpoll_send_skb_on_dev(np, skb, np->dev);
+ local_irq_restore(flags);
}
#ifdef CONFIG_NETPOLL
+static inline bool netpoll_rx_on(struct sk_buff *skb)
+{
+ struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo);
+
+ return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags);
+}
+
static inline bool netpoll_rx(struct sk_buff *skb)
{
struct netpoll_info *npinfo;
@@ -67,14 +80,14 @@
bool ret = false;
local_irq_save(flags);
- npinfo = rcu_dereference_bh(skb->dev->npinfo);
- if (!npinfo || (list_empty(&npinfo->rx_np) && !npinfo->rx_flags))
+ if (!netpoll_rx_on(skb))
goto out;
+ npinfo = rcu_dereference_bh(skb->dev->npinfo);
spin_lock(&npinfo->rx_lock);
/* check rx_flags again with the lock held */
- if (npinfo->rx_flags && __netpoll_rx(skb))
+ if (npinfo->rx_flags && __netpoll_rx(skb, npinfo))
ret = true;
spin_unlock(&npinfo->rx_lock);
@@ -83,13 +96,6 @@
return ret;
}
-static inline int netpoll_rx_on(struct sk_buff *skb)
-{
- struct netpoll_info *npinfo = rcu_dereference_bh(skb->dev->npinfo);
-
- return npinfo && (!list_empty(&npinfo->rx_np) || npinfo->rx_flags);
-}
-
static inline int netpoll_receive_skb(struct sk_buff *skb)
{
if (!list_empty(&skb->dev->napi_list))
@@ -119,7 +125,7 @@
}
}
-static inline int netpoll_tx_running(struct net_device *dev)
+static inline bool netpoll_tx_running(struct net_device *dev)
{
return irqs_disabled();
}
@@ -127,11 +133,11 @@
#else
static inline bool netpoll_rx(struct sk_buff *skb)
{
- return 0;
+ return false;
}
-static inline int netpoll_rx_on(struct sk_buff *skb)
+static inline bool netpoll_rx_on(struct sk_buff *skb)
{
- return 0;
+ return false;
}
static inline int netpoll_receive_skb(struct sk_buff *skb)
{
@@ -147,9 +153,9 @@
static inline void netpoll_netdev_init(struct net_device *dev)
{
}
-static inline int netpoll_tx_running(struct net_device *dev)
+static inline bool netpoll_tx_running(struct net_device *dev)
{
- return 0;
+ return false;
}
#endif
diff --git a/include/linux/nfs_page.h b/include/linux/nfs_page.h
index 8808057..92ce578 100644
--- a/include/linux/nfs_page.h
+++ b/include/linux/nfs_page.h
@@ -69,6 +69,7 @@
const struct nfs_pgio_completion_ops *pg_completion_ops;
struct pnfs_layout_segment *pg_lseg;
struct nfs_direct_req *pg_dreq;
+ void *pg_layout_private;
};
#define NFS_WBACK_BUSY(req) (test_bit(PG_BUSY,&(req)->wb_flags))
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 00485e0..ac7c8ae 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -1248,6 +1248,7 @@
void (*release) (struct nfs_pgio_header *hdr);
const struct nfs_pgio_completion_ops *completion_ops;
struct nfs_direct_req *dreq;
+ void *layout_private;
spinlock_t lock;
/* fields protected by lock */
int pnfs_error;
diff --git a/include/linux/of.h b/include/linux/of.h
index 5919ee3..1b11632 100644
--- a/include/linux/of.h
+++ b/include/linux/of.h
@@ -190,10 +190,17 @@
extern struct device_node *of_get_next_parent(struct device_node *node);
extern struct device_node *of_get_next_child(const struct device_node *node,
struct device_node *prev);
+extern struct device_node *of_get_next_available_child(
+ const struct device_node *node, struct device_node *prev);
+
#define for_each_child_of_node(parent, child) \
for (child = of_get_next_child(parent, NULL); child != NULL; \
child = of_get_next_child(parent, child))
+#define for_each_available_child_of_node(parent, child) \
+ for (child = of_get_next_available_child(parent, NULL); child != NULL; \
+ child = of_get_next_available_child(parent, child))
+
static inline int of_get_child_count(const struct device_node *np)
{
struct device_node *child;
diff --git a/include/linux/perf_event.h b/include/linux/perf_event.h
index 76c5c8b..7602ccb 100644
--- a/include/linux/perf_event.h
+++ b/include/linux/perf_event.h
@@ -1272,7 +1272,8 @@
extern void perf_event_init(void);
extern void perf_tp_event(u64 addr, u64 count, void *record,
int entry_size, struct pt_regs *regs,
- struct hlist_head *head, int rctx);
+ struct hlist_head *head, int rctx,
+ struct task_struct *task);
extern void perf_bp_event(struct perf_event *event, void *data);
#ifndef perf_misc_flags
diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h
index 6dd96fb..e9b7f43 100644
--- a/include/linux/pinctrl/consumer.h
+++ b/include/linux/pinctrl/consumer.h
@@ -20,6 +20,7 @@
/* This struct is private to the core and should be regarded as a cookie */
struct pinctrl;
struct pinctrl_state;
+struct device;
#ifdef CONFIG_PINCTRL
diff --git a/include/linux/sched.h b/include/linux/sched.h
index c147e70..b8c8664 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -334,14 +334,6 @@
}
#endif
-#if defined(CONFIG_LOCKUP_DETECTOR) && defined(CONFIG_SUSPEND)
-void lockup_detector_bootcpu_resume(void);
-#else
-static inline void lockup_detector_bootcpu_resume(void)
-{
-}
-#endif
-
#ifdef CONFIG_DETECT_HUNG_TASK
extern unsigned int sysctl_hung_task_panic;
extern unsigned long sysctl_hung_task_check_count;
diff --git a/include/linux/security.h b/include/linux/security.h
index 4e5a73c..3dea6a9 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1242,8 +1242,6 @@
* Check that the @parent process has sufficient permission to trace the
* current process before allowing the current process to present itself
* to the @parent process for tracing.
- * The parent process will still have to undergo the ptrace_access_check
- * checks before it is allowed to trace this one.
* @parent contains the task_struct structure for debugger process.
* Return 0 if permission is granted.
* @capget:
diff --git a/include/linux/string.h b/include/linux/string.h
index ffe0442..b917881 100644
--- a/include/linux/string.h
+++ b/include/linux/string.h
@@ -144,8 +144,8 @@
{
return strncmp(str, prefix, strlen(prefix)) == 0;
}
-#endif
extern size_t memweight(const void *ptr, size_t bytes);
+#endif /* __KERNEL__ */
#endif /* _LINUX_STRING_H_ */
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 99bc88b..7c5ceb2 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -232,7 +232,7 @@
* estimated error = NTP dispersion.
*/
extern unsigned long tick_usec; /* USER_HZ period (usec) */
-extern unsigned long tick_nsec; /* ACTHZ period (nsec) */
+extern unsigned long tick_nsec; /* SHIFTED_HZ period (nsec) */
extern void ntp_init(void);
extern void ntp_clear(void);
diff --git a/include/linux/topology.h b/include/linux/topology.h
index e91cd43..fec12d6 100644
--- a/include/linux/topology.h
+++ b/include/linux/topology.h
@@ -164,6 +164,7 @@
| 0*SD_SHARE_CPUPOWER \
| 0*SD_SHARE_PKG_RESOURCES \
| 0*SD_SERIALIZE \
+ | 1*SD_PREFER_SIBLING \
, \
.last_balance = jiffies, \
.balance_interval = 1, \
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 30d1ae3..07915a3 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -384,6 +384,13 @@
USB_DEVICE_FIXED,
};
+enum usb_port_connect_type {
+ USB_PORT_CONNECT_TYPE_UNKNOWN = 0,
+ USB_PORT_CONNECT_TYPE_HOT_PLUG,
+ USB_PORT_CONNECT_TYPE_HARD_WIRED,
+ USB_PORT_NOT_USED,
+};
+
/*
* USB 3.0 Link Power Management (LPM) parameters.
*
@@ -469,7 +476,6 @@
* access from userspace
* @usbfs_dentry: usbfs dentry entry for the device
* @maxchild: number of ports if hub
- * @children: child devices - USB devices that are attached to this hub
* @quirks: quirks of the whole device
* @urbnum: number of URBs submitted for the whole device
* @active_duration: total time device is not suspended
@@ -543,7 +549,6 @@
struct list_head filelist;
int maxchild;
- struct usb_device **children;
u32 quirks;
atomic_t urbnum;
@@ -572,6 +577,19 @@
extern struct usb_device *usb_get_dev(struct usb_device *dev);
extern void usb_put_dev(struct usb_device *dev);
+extern struct usb_device *usb_hub_find_child(struct usb_device *hdev,
+ int port1);
+
+/**
+ * usb_hub_for_each_child - iterate over all child devices on the hub
+ * @hdev: USB device belonging to the usb hub
+ * @port1: portnum associated with child device
+ * @child: child device pointer
+ */
+#define usb_hub_for_each_child(hdev, port1, child) \
+ for (port1 = 1, child = usb_hub_find_child(hdev, port1); \
+ port1 <= hdev->maxchild; \
+ child = usb_hub_find_child(hdev, ++port1))
/* USB device locking */
#define usb_lock_device(udev) device_lock(&(udev)->dev)
@@ -584,6 +602,16 @@
extern int usb_reset_device(struct usb_device *dev);
extern void usb_queue_reset_device(struct usb_interface *dev);
+#ifdef CONFIG_ACPI
+extern int usb_acpi_set_power_state(struct usb_device *hdev, int index,
+ bool enable);
+extern bool usb_acpi_power_manageable(struct usb_device *hdev, int index);
+#else
+static inline int usb_acpi_set_power_state(struct usb_device *hdev, int index,
+ bool enable) { return 0; }
+static inline bool usb_acpi_power_manageable(struct usb_device *hdev, int index)
+ { return true; }
+#endif
/* USB autosuspend and autoresume */
#ifdef CONFIG_USB_SUSPEND
diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
index b6c2863..7692dc6 100644
--- a/include/linux/usb/ch11.h
+++ b/include/linux/usb/ch11.h
@@ -236,8 +236,8 @@
struct {
__u8 bHubHdrDecLat;
- __u16 wHubDelay;
- __u16 DeviceRemovable;
+ __le16 wHubDelay;
+ __le16 DeviceRemovable;
} __attribute__ ((packed)) ss;
} u;
} __attribute__ ((packed));
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 9d8c3b6..f8dda06 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -34,6 +34,8 @@
* the composite model the host can use both functions at the same time.
*/
+#include <linux/bcd.h>
+#include <linux/version.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -46,6 +48,9 @@
*/
#define USB_GADGET_DELAYED_STATUS 0x7fff /* Impossibly large value */
+/* big enough to hold our biggest descriptor */
+#define USB_COMP_EP0_BUFSIZ 1024
+
struct usb_configuration;
/**
@@ -245,24 +250,31 @@
void usb_remove_config(struct usb_composite_dev *,
struct usb_configuration *);
+/* predefined index for usb_composite_driver */
+enum {
+ USB_GADGET_MANUFACTURER_IDX = 0,
+ USB_GADGET_PRODUCT_IDX,
+ USB_GADGET_SERIAL_IDX,
+ USB_GADGET_FIRST_AVAIL_IDX,
+};
+
/**
* struct usb_composite_driver - groups configurations into a gadget
* @name: For diagnostics, identifies the driver.
- * @iProduct: Used as iProduct override if @dev->iProduct is not set.
- * If NULL value of @name is taken.
- * @iManufacturer: Used as iManufacturer override if @dev->iManufacturer is
- * not set. If NULL a default "<system> <release> with <udc>" value
- * will be used.
- * @iSerialNumber: Used as iSerialNumber override if @dev->iSerialNumber is
- * not set.
* @dev: Template descriptor for the device, including default device
* identifiers.
- * @strings: tables of strings, keyed by identifiers assigned during bind()
- * and language IDs provided in control requests
+ * @strings: tables of strings, keyed by identifiers assigned during @bind
+ * and language IDs provided in control requests. Note: The first entries
+ * are predefined. The first entry that may be used is
+ * USB_GADGET_FIRST_AVAIL_IDX
* @max_speed: Highest speed the driver supports.
* @needs_serial: set to 1 if the gadget needs userspace to provide
* a serial number. If one is not provided, warning will be printed.
- * @unbind: Reverses bind; called as a side effect of unregistering
+ * @bind: (REQUIRED) Used to allocate resources that are shared across the
+ * whole device, such as string IDs, and add its configurations using
+ * @usb_add_config(). This may fail by returning a negative errno
+ * value; it should return zero on successful initialization.
+ * @unbind: Reverses @bind; called as a side effect of unregistering
* this driver.
* @disconnect: optional driver disconnect method
* @suspend: Notifies when the host stops sending USB traffic,
@@ -271,9 +283,9 @@
* before function notifications
*
* Devices default to reporting self powered operation. Devices which rely
- * on bus powered operation should report this in their @bind() method.
+ * on bus powered operation should report this in their @bind method.
*
- * Before returning from bind, various fields in the template descriptor
+ * Before returning from @bind, various fields in the template descriptor
* may be overridden. These include the idVendor/idProduct/bcdDevice values
* normally to bind the appropriate host side driver, and the three strings
* (iManufacturer, iProduct, iSerialNumber) normally used to provide user
@@ -283,14 +295,12 @@
*/
struct usb_composite_driver {
const char *name;
- const char *iProduct;
- const char *iManufacturer;
- const char *iSerialNumber;
const struct usb_device_descriptor *dev;
struct usb_gadget_strings **strings;
enum usb_device_speed max_speed;
unsigned needs_serial:1;
+ int (*bind)(struct usb_composite_dev *cdev);
int (*unbind)(struct usb_composite_dev *);
void (*disconnect)(struct usb_composite_dev *);
@@ -298,10 +308,10 @@
/* global suspend hooks */
void (*suspend)(struct usb_composite_dev *);
void (*resume)(struct usb_composite_dev *);
+ struct usb_gadget_driver gadget_driver;
};
-extern int usb_composite_probe(struct usb_composite_driver *driver,
- int (*bind)(struct usb_composite_dev *cdev));
+extern int usb_composite_probe(struct usb_composite_driver *driver);
extern void usb_composite_unregister(struct usb_composite_driver *driver);
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
@@ -310,7 +320,6 @@
* struct usb_composite_device - represents one composite usb gadget
* @gadget: read-only, abstracts the gadget's usb peripheral controller
* @req: used for control responses; buffer is pre-allocated
- * @bufsiz: size of buffer pre-allocated in @req
* @config: the currently active configuration
*
* One of these devices is allocated and initialized before the
@@ -341,7 +350,6 @@
struct usb_composite_dev {
struct usb_gadget *gadget;
struct usb_request *req;
- unsigned bufsiz;
struct usb_configuration *config;
@@ -352,9 +360,7 @@
struct list_head configs;
struct usb_composite_driver *driver;
u8 next_string_id;
- u8 manufacturer_override;
- u8 product_override;
- u8 serial_override;
+ char *def_manufacturer;
/* the gadget driver won't enable the data pullup
* while the deactivation count is nonzero.
@@ -375,6 +381,53 @@
struct usb_string *str);
extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
+/*
+ * Some systems will need runtime overrides for the product identifiers
+ * published in the device descriptor, either numbers or strings or both.
+ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+struct usb_composite_overwrite {
+ u16 idVendor;
+ u16 idProduct;
+ u16 bcdDevice;
+ char *serial_number;
+ char *manufacturer;
+ char *product;
+};
+#define USB_GADGET_COMPOSITE_OPTIONS() \
+ static struct usb_composite_overwrite coverwrite; \
+ \
+ module_param_named(idVendor, coverwrite.idVendor, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(idVendor, "USB Vendor ID"); \
+ \
+ module_param_named(idProduct, coverwrite.idProduct, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(idProduct, "USB Product ID"); \
+ \
+ module_param_named(bcdDevice, coverwrite.bcdDevice, ushort, S_IRUGO); \
+ MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); \
+ \
+ module_param_named(iSerialNumber, coverwrite.serial_number, charp, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(iSerialNumber, "SerialNumber string"); \
+ \
+ module_param_named(iManufacturer, coverwrite.manufacturer, charp, \
+ S_IRUGO); \
+ MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); \
+ \
+ module_param_named(iProduct, coverwrite.product, charp, S_IRUGO); \
+ MODULE_PARM_DESC(iProduct, "USB Product string")
+
+void usb_composite_overwrite_options(struct usb_composite_dev *cdev,
+ struct usb_composite_overwrite *covr);
+
+static inline u16 get_default_bcdDevice(void)
+{
+ u16 bcdDevice;
+
+ bcdDevice = bin2bcd((LINUX_VERSION_CODE >> 16 & 0xff)) << 8;
+ bcdDevice |= bin2bcd((LINUX_VERSION_CODE >> 8 & 0xff));
+ return bcdDevice;
+}
/* messaging utils */
#define DBG(d, fmt, args...) \
diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h
index 1894f42..c9d09f8 100644
--- a/include/linux/usb/ehci_pdriver.h
+++ b/include/linux/usb/ehci_pdriver.h
@@ -41,6 +41,14 @@
unsigned big_endian_mmio:1;
unsigned port_power_on:1;
unsigned port_power_off:1;
+
+ /* Turn on all power and clocks */
+ int (*power_on)(struct platform_device *pdev);
+ /* Turn off all power and clocks */
+ void (*power_off)(struct platform_device *pdev);
+ /* Turn on only VBUS suspend power and hotplug detection,
+ * turn off everything else */
+ void (*power_suspend)(struct platform_device *pdev);
};
#endif /* __USB_CORE_EHCI_PDRIVER_H */
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 9517466..5b6e508 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -474,7 +474,8 @@
/* Those two are deprecated */
int (*start)(struct usb_gadget_driver *,
- int (*bind)(struct usb_gadget *));
+ int (*bind)(struct usb_gadget *,
+ struct usb_gadget_driver *driver));
int (*stop)(struct usb_gadget_driver *);
};
@@ -502,6 +503,8 @@
* @name: Identifies the controller hardware type. Used in diagnostics
* and sometimes configuration.
* @dev: Driver model state for this abstract device.
+ * @out_epnum: last used out ep number
+ * @in_epnum: last used in ep number
*
* Gadgets have a mostly-portable "gadget driver" implementing device
* functions, handling all usb configurations and interfaces. Gadget
@@ -536,6 +539,8 @@
unsigned a_alt_hnp_support:1;
const char *name;
struct device dev;
+ unsigned out_epnum;
+ unsigned in_epnum;
};
static inline void set_gadget_data(struct usb_gadget *gadget, void *data)
@@ -558,14 +563,7 @@
*/
static inline int gadget_is_dualspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_DUALSPEED
- /* runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_HIGH;
}
/**
@@ -575,15 +573,7 @@
*/
static inline int gadget_is_superspeed(struct usb_gadget *g)
{
-#ifdef CONFIG_USB_GADGET_SUPERSPEED
- /*
- * runtime test would check "g->max_speed" ... that might be
- * useful to work around hardware bugs, but is mostly pointless
- */
- return 1;
-#else
- return 0;
-#endif
+ return g->max_speed >= USB_SPEED_SUPER;
}
/**
@@ -781,6 +771,7 @@
* when the host is disconnected. May be called in_interrupt; this
* may not sleep. Some devices can't detect disconnect, so this might
* not be called except as part of controller shutdown.
+ * @bind: the driver's bind callback
* @unbind: Invoked when the driver is unbound from a gadget,
* usually from rmmod (after a disconnect is reported).
* Called in a context that permits sleeping.
@@ -835,6 +826,8 @@
struct usb_gadget_driver {
char *function;
enum usb_device_speed max_speed;
+ int (*bind)(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver);
void (*unbind)(struct usb_gadget *);
int (*setup)(struct usb_gadget *,
const struct usb_ctrlrequest *);
@@ -860,7 +853,6 @@
/**
* usb_gadget_probe_driver - probe a gadget driver
* @driver: the driver being registered
- * @bind: the driver's bind callback
* Context: can sleep
*
* Call this in your gadget driver's module initialization function,
@@ -869,8 +861,7 @@
* registration call returns. It's expected that the @bind() function will
* be in init sections.
*/
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
- int (*bind)(struct usb_gadget *));
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
/**
* usb_gadget_unregister_driver - unregister a gadget driver
diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h
index c5fdb14..608050b 100644
--- a/include/linux/usb/hcd.h
+++ b/include/linux/usb/hcd.h
@@ -135,8 +135,8 @@
unsigned int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
- u64 rsrc_start; /* memory/io resource start */
- u64 rsrc_len; /* memory/io resource length */
+ resource_size_t rsrc_start; /* memory/io resource start */
+ resource_size_t rsrc_len; /* memory/io resource length */
unsigned power_budget; /* in mA, 0 = no limit */
/* bandwidth_mutex should be taken before adding or removing
diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h
index 2808f2a..74e7755 100644
--- a/include/linux/usb/ohci_pdriver.h
+++ b/include/linux/usb/ohci_pdriver.h
@@ -33,6 +33,14 @@
unsigned big_endian_desc:1;
unsigned big_endian_mmio:1;
unsigned no_big_frame_no:1;
+
+ /* Turn on all power and clocks */
+ int (*power_on)(struct platform_device *pdev);
+ /* Turn off all power and clocks */
+ void (*power_off)(struct platform_device *pdev);
+ /* Turn on only VBUS suspend power and hotplug detection,
+ * turn off everything else */
+ void (*power_suspend)(struct platform_device *pdev);
};
#endif /* __USB_CORE_OHCI_PDRIVER_H */
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 3e93de7..52f944d 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -19,8 +19,8 @@
/* device can't handle its Configuration or Interface strings */
#define USB_QUIRK_CONFIG_INTF_STRINGS 0x00000008
-/*device will morph if reset, don't use reset for handling errors */
-#define USB_QUIRK_RESET_MORPHS 0x00000010
+/* device can't be reset(e.g morph devices), don't use reset */
+#define USB_QUIRK_RESET 0x00000010
/* device has more interface descriptions than the bNumInterfaces count,
and can't handle talking to these interfaces */
diff --git a/include/linux/usb_usual.h b/include/linux/usb_usual.h
index e84e769..bf99cd0 100644
--- a/include/linux/usb_usual.h
+++ b/include/linux/usb_usual.h
@@ -72,33 +72,9 @@
enum { US_DO_ALL_FLAGS };
#undef US_FLAG
-/*
- * The bias field for libusual and friends.
- */
-#define USB_US_TYPE_NONE 0
-#define USB_US_TYPE_STOR 1 /* usb-storage */
-#define USB_US_TYPE_UB 2 /* ub */
-
-#define USB_US_TYPE(flags) (((flags) >> 24) & 0xFF)
-#define USB_US_ORIG_FLAGS(flags) ((flags) & 0x00FFFFFF)
-
#include <linux/usb/storage.h>
-/*
- */
extern int usb_usual_ignore_device(struct usb_interface *intf);
extern struct usb_device_id usb_storage_usb_ids[];
-#ifdef CONFIG_USB_LIBUSUAL
-
-extern void usb_usual_set_present(int type);
-extern void usb_usual_clear_present(int type);
-extern int usb_usual_check_type(const struct usb_device_id *, int type);
-#else
-
-#define usb_usual_set_present(t) do { } while(0)
-#define usb_usual_clear_present(t) do { } while(0)
-#define usb_usual_check_type(id, t) (0)
-#endif /* CONFIG_USB_LIBUSUAL */
-
#endif /* __LINUX_USB_USUAL_H */
diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h
index 3b74666..4abe28e4 100644
--- a/include/linux/usbdevice_fs.h
+++ b/include/linux/usbdevice_fs.h
@@ -131,6 +131,19 @@
#define USBDEVFS_CAP_NO_PACKET_SIZE_LIM 0x04
#define USBDEVFS_CAP_BULK_SCATTER_GATHER 0x08
+/* USBDEVFS_DISCONNECT_CLAIM flags & struct */
+
+/* disconnect-and-claim if the driver matches the driver field */
+#define USBDEVFS_DISCONNECT_CLAIM_IF_DRIVER 0x01
+/* disconnect-and-claim except when the driver matches the driver field */
+#define USBDEVFS_DISCONNECT_CLAIM_EXCEPT_DRIVER 0x02
+
+struct usbdevfs_disconnect_claim {
+ unsigned int interface;
+ unsigned int flags;
+ char driver[USBDEVFS_MAXDRIVERNAME + 1];
+};
+
#ifdef __KERNEL__
#ifdef CONFIG_COMPAT
#include <linux/compat.h>
@@ -211,5 +224,6 @@
#define USBDEVFS_CLAIM_PORT _IOR('U', 24, unsigned int)
#define USBDEVFS_RELEASE_PORT _IOR('U', 25, unsigned int)
#define USBDEVFS_GET_CAPABILITIES _IOR('U', 26, __u32)
+#define USBDEVFS_DISCONNECT_CLAIM _IOR('U', 27, struct usbdevfs_disconnect_claim)
#endif /* _LINUX_USBDEVICE_FS_H */
diff --git a/include/linux/writeback.h b/include/linux/writeback.h
index c66fe33..50c3e8f 100644
--- a/include/linux/writeback.h
+++ b/include/linux/writeback.h
@@ -104,7 +104,6 @@
wait_on_bit(&inode->i_state, __I_NEW, inode_wait, TASK_UNINTERRUPTIBLE);
}
-
/*
* mm/page-writeback.c
*/
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 493fa0c..3d254e1 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -96,6 +96,7 @@
* is not permitted.
* @IEEE80211_CHAN_NO_HT40MINUS: extension channel below this channel
* is not permitted.
+ * @IEEE80211_CHAN_NO_OFDM: OFDM is not allowed on this channel.
*/
enum ieee80211_channel_flags {
IEEE80211_CHAN_DISABLED = 1<<0,
@@ -104,6 +105,7 @@
IEEE80211_CHAN_RADAR = 1<<3,
IEEE80211_CHAN_NO_HT40PLUS = 1<<4,
IEEE80211_CHAN_NO_HT40MINUS = 1<<5,
+ IEEE80211_CHAN_NO_OFDM = 1<<6,
};
#define IEEE80211_CHAN_NO_HT40 \
diff --git a/include/net/codel.h b/include/net/codel.h
index 550debf..389cf62 100644
--- a/include/net/codel.h
+++ b/include/net/codel.h
@@ -305,6 +305,8 @@
}
}
} else if (drop) {
+ u32 delta;
+
if (params->ecn && INET_ECN_set_ce(skb)) {
stats->ecn_mark++;
} else {
@@ -320,9 +322,11 @@
* assume that the drop rate that controlled the queue on the
* last cycle is a good starting point to control it now.
*/
- if (codel_time_before(now - vars->drop_next,
+ delta = vars->count - vars->lastcount;
+ if (delta > 1 &&
+ codel_time_before(now - vars->drop_next,
16 * params->interval)) {
- vars->count = (vars->count - vars->lastcount) | 1;
+ vars->count = delta;
/* we dont care if rec_inv_sqrt approximation
* is not very precise :
* Next Newton steps will correct it quadratically.
diff --git a/include/net/dst.h b/include/net/dst.h
index baf5978..621e351 100644
--- a/include/net/dst.h
+++ b/include/net/dst.h
@@ -110,7 +110,7 @@
};
extern u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old);
-extern const u32 dst_default_metrics[RTAX_MAX];
+extern const u32 dst_default_metrics[];
#define DST_METRICS_READ_ONLY 0x1UL
#define __DST_METRICS_PTR(Y) \
diff --git a/include/net/inet_connection_sock.h b/include/net/inet_connection_sock.h
index 5ee66f5..ba1d361 100644
--- a/include/net/inet_connection_sock.h
+++ b/include/net/inet_connection_sock.h
@@ -39,6 +39,7 @@
int (*queue_xmit)(struct sk_buff *skb, struct flowi *fl);
void (*send_check)(struct sock *sk, struct sk_buff *skb);
int (*rebuild_header)(struct sock *sk);
+ void (*sk_rx_dst_set)(struct sock *sk, const struct sk_buff *skb);
int (*conn_request)(struct sock *sk, struct sk_buff *skb);
struct sock *(*syn_recv_sock)(struct sock *sk, struct sk_buff *skb,
struct request_sock *req,
diff --git a/include/net/inet_sock.h b/include/net/inet_sock.h
index 83b567f..613cfa4 100644
--- a/include/net/inet_sock.h
+++ b/include/net/inet_sock.h
@@ -249,13 +249,4 @@
return flags;
}
-static inline void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
-{
- struct dst_entry *dst = skb_dst(skb);
-
- dst_hold(dst);
- sk->sk_rx_dst = dst;
- inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
-}
-
#endif /* _INET_SOCK_H */
diff --git a/include/net/ip.h b/include/net/ip.h
index bd5e444a..5a5d84d 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -120,7 +120,7 @@
struct flowi4 *fl4,
struct sk_buff_head *queue,
struct inet_cork *cork);
-extern int ip_send_skb(struct sk_buff *skb);
+extern int ip_send_skb(struct net *net, struct sk_buff *skb);
extern int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
extern void ip_flush_pending_frames(struct sock *sk);
extern struct sk_buff *ip_make_skb(struct sock *sk,
diff --git a/include/net/llc.h b/include/net/llc.h
index 226c846..f2d0fc5 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -133,7 +133,7 @@
extern void llc_sap_handler(struct llc_sap *sap, struct sk_buff *skb);
extern void llc_conn_handler(struct llc_sap *sap, struct sk_buff *skb);
-extern int llc_station_init(void);
+extern void llc_station_init(void);
extern void llc_station_exit(void);
#ifdef CONFIG_PROC_FS
diff --git a/include/net/scm.h b/include/net/scm.h
index 079d788..7dc0854 100644
--- a/include/net/scm.h
+++ b/include/net/scm.h
@@ -70,9 +70,11 @@
}
static __inline__ int scm_send(struct socket *sock, struct msghdr *msg,
- struct scm_cookie *scm)
+ struct scm_cookie *scm, bool forcecreds)
{
memset(scm, 0, sizeof(*scm));
+ if (forcecreds)
+ scm_set_cred(scm, task_tgid(current), current_cred());
unix_get_peersec_dgram(sock, scm);
if (msg->msg_controllen <= 0)
return 0;
diff --git a/include/net/sock.h b/include/net/sock.h
index b373023..72132ae 100644
--- a/include/net/sock.h
+++ b/include/net/sock.h
@@ -218,6 +218,7 @@
* @sk_route_nocaps: forbidden route capabilities (e.g NETIF_F_GSO_MASK)
* @sk_gso_type: GSO type (e.g. %SKB_GSO_TCPV4)
* @sk_gso_max_size: Maximum GSO segment size to build
+ * @sk_gso_max_segs: Maximum number of GSO segments
* @sk_lingertime: %SO_LINGER l_linger setting
* @sk_backlog: always used with the per-socket spinlock held
* @sk_callback_lock: used with the callbacks in the end of this struct
@@ -338,6 +339,7 @@
netdev_features_t sk_route_nocaps;
int sk_gso_type;
unsigned int sk_gso_max_size;
+ u16 sk_gso_max_segs;
int sk_rcvlowat;
unsigned long sk_lingertime;
struct sk_buff_head sk_error_queue;
diff --git a/include/net/tcp.h b/include/net/tcp.h
index e19124b..1f000ff 100644
--- a/include/net/tcp.h
+++ b/include/net/tcp.h
@@ -464,6 +464,7 @@
void tcp_connect_init(struct sock *sk);
void tcp_finish_connect(struct sock *sk, struct sk_buff *skb);
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size);
+void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb);
/* From syncookies.c */
extern __u32 syncookie_secret[2][16-4+SHA_DIGEST_WORDS];
diff --git a/include/net/xfrm.h b/include/net/xfrm.h
index d9509eb..976a81a 100644
--- a/include/net/xfrm.h
+++ b/include/net/xfrm.h
@@ -213,6 +213,9 @@
struct xfrm_lifetime_cur curlft;
struct tasklet_hrtimer mtimer;
+ /* used to fix curlft->add_time when changing date */
+ long saved_tmo;
+
/* Last used time */
unsigned long lastused;
@@ -238,6 +241,7 @@
/* xflags - make enum if more show up */
#define XFRM_TIME_DEFER 1
+#define XFRM_SOFT_EXPIRE 2
enum {
XFRM_STATE_VOID,
@@ -288,6 +292,8 @@
struct flowi *fl,
int reverse);
int (*get_tos)(const struct flowi *fl);
+ void (*init_dst)(struct net *net,
+ struct xfrm_dst *dst);
int (*init_path)(struct xfrm_dst *path,
struct dst_entry *dst,
int nfheader_len);
diff --git a/include/sound/pcm.h b/include/sound/pcm.h
index c75c0d1..cdca2ab 100644
--- a/include/sound/pcm.h
+++ b/include/sound/pcm.h
@@ -1075,7 +1075,8 @@
const char *snd_pcm_format_name(snd_pcm_format_t format);
/**
- * Get a string naming the direction of a stream
+ * snd_pcm_stream_str - Get a string naming the direction of a stream
+ * @substream: the pcm substream instance
*/
static inline const char *snd_pcm_stream_str(struct snd_pcm_substream *substream)
{
diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h
index 128ce46..015cea0 100644
--- a/include/target/target_core_base.h
+++ b/include/target/target_core_base.h
@@ -503,8 +503,6 @@
u32 se_ordered_id;
/* Total size in bytes associated with command */
u32 data_length;
- /* SCSI Presented Data Transfer Length */
- u32 cmd_spdtl;
u32 residual_count;
u32 orig_fe_lun;
/* Persistent Reservation key */
diff --git a/include/trace/events/sched.h b/include/trace/events/sched.h
index ea7a203..5a8671e 100644
--- a/include/trace/events/sched.h
+++ b/include/trace/events/sched.h
@@ -73,6 +73,9 @@
__entry->prio = p->prio;
__entry->success = success;
__entry->target_cpu = task_cpu(p);
+ )
+ TP_perf_assign(
+ __perf_task(p);
),
TP_printk("comm=%s pid=%d prio=%d success=%d target_cpu=%03d",
@@ -325,6 +328,7 @@
)
TP_perf_assign(
__perf_count(delay);
+ __perf_task(tsk);
),
TP_printk("comm=%s pid=%d delay=%Lu [ns]",
diff --git a/include/trace/ftrace.h b/include/trace/ftrace.h
index c6bc2fa..a763888 100644
--- a/include/trace/ftrace.h
+++ b/include/trace/ftrace.h
@@ -712,6 +712,9 @@
#undef __perf_count
#define __perf_count(c) __count = (c)
+#undef __perf_task
+#define __perf_task(t) __task = (t)
+
#undef TP_perf_assign
#define TP_perf_assign(args...) args
@@ -725,6 +728,7 @@
struct ftrace_raw_##call *entry; \
struct pt_regs __regs; \
u64 __addr = 0, __count = 1; \
+ struct task_struct *__task = NULL; \
struct hlist_head *head; \
int __entry_size; \
int __data_size; \
@@ -752,7 +756,7 @@
\
head = this_cpu_ptr(event_call->perf_events); \
perf_trace_buf_submit(entry, __entry_size, rctx, __addr, \
- __count, &__regs, head); \
+ __count, &__regs, head, __task); \
}
/*
diff --git a/init/main.c b/init/main.c
index e60679d..b286730 100644
--- a/init/main.c
+++ b/init/main.c
@@ -461,10 +461,6 @@
percpu_init_late();
pgtable_cache_init();
vmalloc_init();
-#ifdef CONFIG_X86
- if (efi_enabled)
- efi_enter_virtual_mode();
-#endif
}
asmlinkage void __init start_kernel(void)
@@ -606,6 +602,10 @@
calibrate_delay();
pidmap_init();
anon_vma_init();
+#ifdef CONFIG_X86
+ if (efi_enabled)
+ efi_enter_virtual_mode();
+#endif
thread_info_cache_init();
cred_init();
fork_init(totalram_pages);
diff --git a/ipc/mqueue.c b/ipc/mqueue.c
index f8e54f5..9a08acc 100644
--- a/ipc/mqueue.c
+++ b/ipc/mqueue.c
@@ -726,7 +726,6 @@
struct mq_attr *attr)
{
const struct cred *cred = current_cred();
- struct file *result;
int ret;
if (attr) {
@@ -748,21 +747,11 @@
}
mode &= ~current_umask();
- ret = mnt_want_write(path->mnt);
- if (ret)
- return ERR_PTR(ret);
ret = vfs_create(dir, path->dentry, mode, true);
path->dentry->d_fsdata = NULL;
- if (!ret)
- result = dentry_open(path, oflag, cred);
- else
- result = ERR_PTR(ret);
- /*
- * dentry_open() took a persistent mnt_want_write(),
- * so we can now drop this one.
- */
- mnt_drop_write(path->mnt);
- return result;
+ if (ret)
+ return ERR_PTR(ret);
+ return dentry_open(path, oflag, cred);
}
/* Opens existing queue */
@@ -788,7 +777,9 @@
struct mq_attr attr;
int fd, error;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
- struct dentry *root = ipc_ns->mq_mnt->mnt_root;
+ struct vfsmount *mnt = ipc_ns->mq_mnt;
+ struct dentry *root = mnt->mnt_root;
+ int ro;
if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
return -EFAULT;
@@ -802,6 +793,7 @@
if (fd < 0)
goto out_putname;
+ ro = mnt_want_write(mnt); /* we'll drop it in any case */
error = 0;
mutex_lock(&root->d_inode->i_mutex);
path.dentry = lookup_one_len(name, root, strlen(name));
@@ -809,7 +801,7 @@
error = PTR_ERR(path.dentry);
goto out_putfd;
}
- path.mnt = mntget(ipc_ns->mq_mnt);
+ path.mnt = mntget(mnt);
if (oflag & O_CREAT) {
if (path.dentry->d_inode) { /* entry already exists */
@@ -820,6 +812,10 @@
}
filp = do_open(&path, oflag);
} else {
+ if (ro) {
+ error = ro;
+ goto out;
+ }
filp = do_create(ipc_ns, root->d_inode,
&path, oflag, mode,
u_attr ? &attr : NULL);
@@ -845,6 +841,7 @@
fd = error;
}
mutex_unlock(&root->d_inode->i_mutex);
+ mnt_drop_write(mnt);
out_putname:
putname(name);
return fd;
@@ -857,40 +854,38 @@
struct dentry *dentry;
struct inode *inode = NULL;
struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
+ struct vfsmount *mnt = ipc_ns->mq_mnt;
name = getname(u_name);
if (IS_ERR(name))
return PTR_ERR(name);
- mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex,
- I_MUTEX_PARENT);
- dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
+ err = mnt_want_write(mnt);
+ if (err)
+ goto out_name;
+ mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
+ dentry = lookup_one_len(name, mnt->mnt_root, strlen(name));
if (IS_ERR(dentry)) {
err = PTR_ERR(dentry);
goto out_unlock;
}
- if (!dentry->d_inode) {
- err = -ENOENT;
- goto out_err;
- }
-
inode = dentry->d_inode;
- if (inode)
+ if (!inode) {
+ err = -ENOENT;
+ } else {
ihold(inode);
- err = mnt_want_write(ipc_ns->mq_mnt);
- if (err)
- goto out_err;
- err = vfs_unlink(dentry->d_parent->d_inode, dentry);
- mnt_drop_write(ipc_ns->mq_mnt);
-out_err:
+ err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+ }
dput(dentry);
out_unlock:
- mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
- putname(name);
+ mutex_unlock(&mnt->mnt_root->d_inode->i_mutex);
if (inode)
iput(inode);
+ mnt_drop_write(mnt);
+out_name:
+ putname(name);
return err;
}
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c
index 3a5ca58..ed206fd 100644
--- a/kernel/audit_tree.c
+++ b/kernel/audit_tree.c
@@ -250,7 +250,6 @@
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
fsnotify_destroy_mark(entry);
- fsnotify_put_mark(entry);
goto out;
}
@@ -259,7 +258,7 @@
fsnotify_duplicate_mark(&new->mark, entry);
if (fsnotify_add_mark(&new->mark, new->mark.group, new->mark.i.inode, NULL, 1)) {
- free_chunk(new);
+ fsnotify_put_mark(&new->mark);
goto Fallback;
}
@@ -293,7 +292,7 @@
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
fsnotify_destroy_mark(entry);
- fsnotify_put_mark(entry);
+ fsnotify_put_mark(&new->mark); /* drop initial reference */
goto out;
Fallback:
@@ -322,7 +321,7 @@
entry = &chunk->mark;
if (fsnotify_add_mark(entry, audit_tree_group, inode, NULL, 0)) {
- free_chunk(chunk);
+ fsnotify_put_mark(entry);
return -ENOSPC;
}
@@ -347,6 +346,7 @@
insert_hash(chunk);
spin_unlock(&hash_lock);
spin_unlock(&entry->lock);
+ fsnotify_put_mark(entry); /* drop initial reference */
return 0;
}
@@ -396,7 +396,7 @@
fsnotify_duplicate_mark(chunk_entry, old_entry);
if (fsnotify_add_mark(chunk_entry, chunk_entry->group, chunk_entry->i.inode, NULL, 1)) {
spin_unlock(&old_entry->lock);
- free_chunk(chunk);
+ fsnotify_put_mark(chunk_entry);
fsnotify_put_mark(old_entry);
return -ENOSPC;
}
@@ -444,8 +444,8 @@
spin_unlock(&chunk_entry->lock);
spin_unlock(&old_entry->lock);
fsnotify_destroy_mark(old_entry);
+ fsnotify_put_mark(chunk_entry); /* drop initial reference */
fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */
- fsnotify_put_mark(old_entry); /* and kill it */
return 0;
}
@@ -916,7 +916,12 @@
struct audit_chunk *chunk = container_of(entry, struct audit_chunk, mark);
evict_chunk(chunk);
- fsnotify_put_mark(entry);
+
+ /*
+ * We are guaranteed to have at least one reference to the mark from
+ * either the inode or the caller of fsnotify_destroy_mark().
+ */
+ BUG_ON(atomic_read(&entry->refcnt) < 1);
}
static bool audit_tree_send_event(struct fsnotify_group *group, struct inode *inode,
diff --git a/kernel/debug/kdb/kdb_debugger.c b/kernel/debug/kdb/kdb_debugger.c
index 8b68ce7..be7b33b 100644
--- a/kernel/debug/kdb/kdb_debugger.c
+++ b/kernel/debug/kdb/kdb_debugger.c
@@ -12,6 +12,7 @@
#include <linux/kdb.h>
#include <linux/kdebug.h>
#include <linux/export.h>
+#include <linux/hardirq.h>
#include "kdb_private.h"
#include "../debug_core.h"
@@ -52,6 +53,9 @@
if (atomic_read(&kgdb_setting_breakpoint))
reason = KDB_REASON_KEYBOARD;
+ if (in_nmi())
+ reason = KDB_REASON_NMI;
+
for (i = 0, bp = kdb_breakpoints; i < KDB_MAXBPT; i++, bp++) {
if ((bp->bp_enabled) && (bp->bp_addr == addr)) {
reason = KDB_REASON_BREAK;
diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c
index bb9520f..0a69d2a 100644
--- a/kernel/debug/kdb/kdb_io.c
+++ b/kernel/debug/kdb/kdb_io.c
@@ -715,9 +715,6 @@
/* check for having reached the LINES number of printed lines */
if (kdb_nextline == linecount) {
char buf1[16] = "";
-#if defined(CONFIG_SMP)
- char buf2[32];
-#endif
/* Watch out for recursion here. Any routine that calls
* kdb_printf will come back through here. And kdb_read
@@ -732,14 +729,6 @@
if (moreprompt == NULL)
moreprompt = "more> ";
-#if defined(CONFIG_SMP)
- if (strchr(moreprompt, '%')) {
- sprintf(buf2, moreprompt, get_cpu());
- put_cpu();
- moreprompt = buf2;
- }
-#endif
-
kdb_input_flush();
c = console_drivers;
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
index 1f91413e..31df170 100644
--- a/kernel/debug/kdb/kdb_main.c
+++ b/kernel/debug/kdb/kdb_main.c
@@ -139,11 +139,10 @@
static char *__env[] = {
#if defined(CONFIG_SMP)
"PROMPT=[%d]kdb> ",
- "MOREPROMPT=[%d]more> ",
#else
"PROMPT=kdb> ",
- "MOREPROMPT=more> ",
#endif
+ "MOREPROMPT=more> ",
"RADIX=16",
"MDCOUNT=8", /* lines of md output */
KDB_PLATFORM_ENV,
@@ -1236,18 +1235,6 @@
*cmdbuf = '\0';
*(cmd_hist[cmd_head]) = '\0';
- if (KDB_FLAG(ONLY_DO_DUMP)) {
- /* kdb is off but a catastrophic error requires a dump.
- * Take the dump and reboot.
- * Turn on logging so the kdb output appears in the log
- * buffer in the dump.
- */
- const char *setargs[] = { "set", "LOGGING", "1" };
- kdb_set(2, setargs);
- kdb_reboot(0, NULL);
- /*NOTREACHED*/
- }
-
do_full_getstr:
#if defined(CONFIG_SMP)
snprintf(kdb_prompt_str, CMD_BUFLEN, kdbgetenv("PROMPT"),
diff --git a/kernel/events/callchain.c b/kernel/events/callchain.c
index 6581a04..98d4597 100644
--- a/kernel/events/callchain.c
+++ b/kernel/events/callchain.c
@@ -153,7 +153,8 @@
put_recursion_context(__get_cpu_var(callchain_recursion), rctx);
}
-struct perf_callchain_entry *perf_callchain(struct pt_regs *regs)
+struct perf_callchain_entry *
+perf_callchain(struct perf_event *event, struct pt_regs *regs)
{
int rctx;
struct perf_callchain_entry *entry;
@@ -178,6 +179,12 @@
}
if (regs) {
+ /*
+ * Disallow cross-task user callchains.
+ */
+ if (event->ctx->task && event->ctx->task != current)
+ goto exit_put;
+
perf_callchain_store(entry, PERF_CONTEXT_USER);
perf_callchain_user(entry, regs);
}
diff --git a/kernel/events/core.c b/kernel/events/core.c
index f1cf0ed..b7935fc 100644
--- a/kernel/events/core.c
+++ b/kernel/events/core.c
@@ -4039,7 +4039,7 @@
if (sample_type & PERF_SAMPLE_CALLCHAIN) {
int size = 1;
- data->callchain = perf_callchain(regs);
+ data->callchain = perf_callchain(event, regs);
if (data->callchain)
size += data->callchain->nr;
@@ -5209,7 +5209,8 @@
}
void perf_tp_event(u64 addr, u64 count, void *record, int entry_size,
- struct pt_regs *regs, struct hlist_head *head, int rctx)
+ struct pt_regs *regs, struct hlist_head *head, int rctx,
+ struct task_struct *task)
{
struct perf_sample_data data;
struct perf_event *event;
@@ -5228,6 +5229,31 @@
perf_swevent_event(event, count, &data, regs);
}
+ /*
+ * If we got specified a target task, also iterate its context and
+ * deliver this event there too.
+ */
+ if (task && task != current) {
+ struct perf_event_context *ctx;
+ struct trace_entry *entry = record;
+
+ rcu_read_lock();
+ ctx = rcu_dereference(task->perf_event_ctxp[perf_sw_context]);
+ if (!ctx)
+ goto unlock;
+
+ list_for_each_entry_rcu(event, &ctx->event_list, event_entry) {
+ if (event->attr.type != PERF_TYPE_TRACEPOINT)
+ continue;
+ if (event->attr.config != entry->type)
+ continue;
+ if (perf_tp_event_match(event, &data, regs))
+ perf_swevent_event(event, count, &data, regs);
+ }
+unlock:
+ rcu_read_unlock();
+ }
+
perf_swevent_put_recursion_context(rctx);
}
EXPORT_SYMBOL_GPL(perf_tp_event);
diff --git a/kernel/events/internal.h b/kernel/events/internal.h
index b0b107f..a096c19 100644
--- a/kernel/events/internal.h
+++ b/kernel/events/internal.h
@@ -101,7 +101,8 @@
}
/* Callchain handling */
-extern struct perf_callchain_entry *perf_callchain(struct pt_regs *regs);
+extern struct perf_callchain_entry *
+perf_callchain(struct perf_event *event, struct pt_regs *regs);
extern int get_callchain_buffers(void);
extern void put_callchain_buffers(void);
diff --git a/kernel/futex.c b/kernel/futex.c
index e2b0fb9..3717e7b 100644
--- a/kernel/futex.c
+++ b/kernel/futex.c
@@ -2231,11 +2231,11 @@
* @uaddr2: the pi futex we will take prior to returning to user-space
*
* The caller will wait on uaddr and will be requeued by futex_requeue() to
- * uaddr2 which must be PI aware. Normal wakeup will wake on uaddr2 and
- * complete the acquisition of the rt_mutex prior to returning to userspace.
- * This ensures the rt_mutex maintains an owner when it has waiters; without
- * one, the pi logic wouldn't know which task to boost/deboost, if there was a
- * need to.
+ * uaddr2 which must be PI aware and unique from uaddr. Normal wakeup will wake
+ * on uaddr2 and complete the acquisition of the rt_mutex prior to returning to
+ * userspace. This ensures the rt_mutex maintains an owner when it has waiters;
+ * without one, the pi logic would not know which task to boost/deboost, if
+ * there was a need to.
*
* We call schedule in futex_wait_queue_me() when we enqueue and return there
* via the following:
@@ -2272,6 +2272,9 @@
struct futex_q q = futex_q_init;
int res, ret;
+ if (uaddr == uaddr2)
+ return -EINVAL;
+
if (!bitset)
return -EINVAL;
@@ -2343,7 +2346,7 @@
* signal. futex_unlock_pi() will not destroy the lock_ptr nor
* the pi_state.
*/
- WARN_ON(!&q.pi_state);
+ WARN_ON(!q.pi_state);
pi_mutex = &q.pi_state->pi_mutex;
ret = rt_mutex_finish_proxy_lock(pi_mutex, to, &rt_waiter, 1);
debug_rt_mutex_free_waiter(&rt_waiter);
@@ -2370,7 +2373,7 @@
* fault, unlock the rt_mutex and return the fault to userspace.
*/
if (ret == -EFAULT) {
- if (rt_mutex_owner(pi_mutex) == current)
+ if (pi_mutex && rt_mutex_owner(pi_mutex) == current)
rt_mutex_unlock(pi_mutex);
} else if (ret == -EINTR) {
/*
diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c
index 0a8e8f0..4c69326 100644
--- a/kernel/irq/manage.c
+++ b/kernel/irq/manage.c
@@ -944,6 +944,18 @@
}
/*
+ * Drivers are often written to work w/o knowledge about the
+ * underlying irq chip implementation, so a request for a
+ * threaded irq without a primary hard irq context handler
+ * requires the ONESHOT flag to be set. Some irq chips like
+ * MSI based interrupts are per se one shot safe. Check the
+ * chip flags, so we can avoid the unmask dance at the end of
+ * the threaded handler for those.
+ */
+ if (desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)
+ new->flags &= ~IRQF_ONESHOT;
+
+ /*
* The following block of code has to be executed atomically
*/
raw_spin_lock_irqsave(&desc->lock, flags);
@@ -1017,7 +1029,8 @@
*/
new->thread_mask = 1 << ffz(thread_mask);
- } else if (new->handler == irq_default_primary_handler) {
+ } else if (new->handler == irq_default_primary_handler &&
+ !(desc->irq_data.chip->flags & IRQCHIP_ONESHOT_SAFE)) {
/*
* The interrupt was requested with handler = NULL, so
* we use the default primary handler for it. But it
diff --git a/kernel/power/suspend.c b/kernel/power/suspend.c
index 1da39ea..c8b7446 100644
--- a/kernel/power/suspend.c
+++ b/kernel/power/suspend.c
@@ -178,9 +178,6 @@
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
- /* Kick the lockup detector */
- lockup_detector_bootcpu_resume();
-
Enable_cpus:
enable_nonboot_cpus();
diff --git a/kernel/printk.c b/kernel/printk.c
index 6a76ab9..66a2ea3 100644
--- a/kernel/printk.c
+++ b/kernel/printk.c
@@ -1034,6 +1034,7 @@
struct log *msg = log_from_idx(idx);
len += msg_print_text(msg, prev, true, NULL, 0);
+ prev = msg->flags;
idx = log_next(idx);
seq++;
}
@@ -1046,6 +1047,7 @@
struct log *msg = log_from_idx(idx);
len -= msg_print_text(msg, prev, true, NULL, 0);
+ prev = msg->flags;
idx = log_next(idx);
seq++;
}
diff --git a/kernel/sched/core.c b/kernel/sched/core.c
index d325c4b..fbf1fd0 100644
--- a/kernel/sched/core.c
+++ b/kernel/sched/core.c
@@ -3142,6 +3142,20 @@
# define nsecs_to_cputime(__nsecs) nsecs_to_jiffies(__nsecs)
#endif
+static cputime_t scale_utime(cputime_t utime, cputime_t rtime, cputime_t total)
+{
+ u64 temp = (__force u64) rtime;
+
+ temp *= (__force u64) utime;
+
+ if (sizeof(cputime_t) == 4)
+ temp = div_u64(temp, (__force u32) total);
+ else
+ temp = div64_u64(temp, (__force u64) total);
+
+ return (__force cputime_t) temp;
+}
+
void task_times(struct task_struct *p, cputime_t *ut, cputime_t *st)
{
cputime_t rtime, utime = p->utime, total = utime + p->stime;
@@ -3151,13 +3165,9 @@
*/
rtime = nsecs_to_cputime(p->se.sum_exec_runtime);
- if (total) {
- u64 temp = (__force u64) rtime;
-
- temp *= (__force u64) utime;
- do_div(temp, (__force u32) total);
- utime = (__force cputime_t) temp;
- } else
+ if (total)
+ utime = scale_utime(utime, rtime, total);
+ else
utime = rtime;
/*
@@ -3184,13 +3194,9 @@
total = cputime.utime + cputime.stime;
rtime = nsecs_to_cputime(cputime.sum_exec_runtime);
- if (total) {
- u64 temp = (__force u64) rtime;
-
- temp *= (__force u64) cputime.utime;
- do_div(temp, (__force u32) total);
- utime = (__force cputime_t) temp;
- } else
+ if (total)
+ utime = scale_utime(cputime.utime, rtime, total);
+ else
utime = rtime;
sig->prev_utime = max(sig->prev_utime, utime);
@@ -4340,9 +4346,7 @@
*/
if (unlikely(policy == p->policy && (!rt_policy(policy) ||
param->sched_priority == p->rt_priority))) {
-
- __task_rq_unlock(rq);
- raw_spin_unlock_irqrestore(&p->pi_lock, flags);
+ task_rq_unlock(rq, p, &flags);
return 0;
}
@@ -7248,6 +7252,7 @@
#ifdef CONFIG_CGROUP_SCHED
struct task_group root_task_group;
+LIST_HEAD(task_groups);
#endif
DECLARE_PER_CPU(cpumask_var_t, load_balance_tmpmask);
diff --git a/kernel/sched/cpupri.c b/kernel/sched/cpupri.c
index d72586f..23aa789 100644
--- a/kernel/sched/cpupri.c
+++ b/kernel/sched/cpupri.c
@@ -65,8 +65,8 @@
int cpupri_find(struct cpupri *cp, struct task_struct *p,
struct cpumask *lowest_mask)
{
- int idx = 0;
- int task_pri = convert_prio(p->prio);
+ int idx = 0;
+ int task_pri = convert_prio(p->prio);
if (task_pri >= MAX_RT_PRIO)
return 0;
@@ -137,9 +137,9 @@
*/
void cpupri_set(struct cpupri *cp, int cpu, int newpri)
{
- int *currpri = &cp->cpu_to_pri[cpu];
- int oldpri = *currpri;
- int do_mb = 0;
+ int *currpri = &cp->cpu_to_pri[cpu];
+ int oldpri = *currpri;
+ int do_mb = 0;
newpri = convert_prio(newpri);
diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c
index 22321db..c219bf8 100644
--- a/kernel/sched/fair.c
+++ b/kernel/sched/fair.c
@@ -3069,6 +3069,9 @@
int new_dst_cpu;
enum cpu_idle_type idle;
long imbalance;
+ /* The set of CPUs under consideration for load-balancing */
+ struct cpumask *cpus;
+
unsigned int flags;
unsigned int loop;
@@ -3384,6 +3387,14 @@
static void update_h_load(long cpu)
{
+ struct rq *rq = cpu_rq(cpu);
+ unsigned long now = jiffies;
+
+ if (rq->h_load_throttle == now)
+ return;
+
+ rq->h_load_throttle = now;
+
rcu_read_lock();
walk_tg_tree(tg_load_down, tg_nop, (void *)cpu);
rcu_read_unlock();
@@ -3653,8 +3664,7 @@
*/
static inline void update_sg_lb_stats(struct lb_env *env,
struct sched_group *group, int load_idx,
- int local_group, const struct cpumask *cpus,
- int *balance, struct sg_lb_stats *sgs)
+ int local_group, int *balance, struct sg_lb_stats *sgs)
{
unsigned long nr_running, max_nr_running, min_nr_running;
unsigned long load, max_cpu_load, min_cpu_load;
@@ -3671,7 +3681,7 @@
max_nr_running = 0;
min_nr_running = ~0UL;
- for_each_cpu_and(i, sched_group_cpus(group), cpus) {
+ for_each_cpu_and(i, sched_group_cpus(group), env->cpus) {
struct rq *rq = cpu_rq(i);
nr_running = rq->nr_running;
@@ -3800,8 +3810,7 @@
* @sds: variable to hold the statistics for this sched_domain.
*/
static inline void update_sd_lb_stats(struct lb_env *env,
- const struct cpumask *cpus,
- int *balance, struct sd_lb_stats *sds)
+ int *balance, struct sd_lb_stats *sds)
{
struct sched_domain *child = env->sd->child;
struct sched_group *sg = env->sd->groups;
@@ -3818,8 +3827,7 @@
local_group = cpumask_test_cpu(env->dst_cpu, sched_group_cpus(sg));
memset(&sgs, 0, sizeof(sgs));
- update_sg_lb_stats(env, sg, load_idx, local_group,
- cpus, balance, &sgs);
+ update_sg_lb_stats(env, sg, load_idx, local_group, balance, &sgs);
if (local_group && !(*balance))
return;
@@ -4055,7 +4063,6 @@
* to restore balance.
*
* @env: The load balancing environment.
- * @cpus: The set of CPUs under consideration for load-balancing.
* @balance: Pointer to a variable indicating if this_cpu
* is the appropriate cpu to perform load balancing at this_level.
*
@@ -4065,7 +4072,7 @@
* put to idle by rebalancing its tasks onto our group.
*/
static struct sched_group *
-find_busiest_group(struct lb_env *env, const struct cpumask *cpus, int *balance)
+find_busiest_group(struct lb_env *env, int *balance)
{
struct sd_lb_stats sds;
@@ -4075,7 +4082,7 @@
* Compute the various statistics relavent for load balancing at
* this level.
*/
- update_sd_lb_stats(env, cpus, balance, &sds);
+ update_sd_lb_stats(env, balance, &sds);
/*
* this_cpu is not the appropriate cpu to perform load balancing at
@@ -4155,8 +4162,7 @@
* find_busiest_queue - find the busiest runqueue among the cpus in group.
*/
static struct rq *find_busiest_queue(struct lb_env *env,
- struct sched_group *group,
- const struct cpumask *cpus)
+ struct sched_group *group)
{
struct rq *busiest = NULL, *rq;
unsigned long max_load = 0;
@@ -4171,7 +4177,7 @@
if (!capacity)
capacity = fix_small_capacity(env->sd, group);
- if (!cpumask_test_cpu(i, cpus))
+ if (!cpumask_test_cpu(i, env->cpus))
continue;
rq = cpu_rq(i);
@@ -4252,6 +4258,7 @@
.dst_grpmask = sched_group_cpus(sd->groups),
.idle = idle,
.loop_break = sched_nr_migrate_break,
+ .cpus = cpus,
};
cpumask_copy(cpus, cpu_active_mask);
@@ -4260,7 +4267,7 @@
schedstat_inc(sd, lb_count[idle]);
redo:
- group = find_busiest_group(&env, cpus, balance);
+ group = find_busiest_group(&env, balance);
if (*balance == 0)
goto out_balanced;
@@ -4270,7 +4277,7 @@
goto out_balanced;
}
- busiest = find_busiest_queue(&env, group, cpus);
+ busiest = find_busiest_queue(&env, group);
if (!busiest) {
schedstat_inc(sd, lb_nobusyq[idle]);
goto out_balanced;
@@ -4294,11 +4301,10 @@
env.src_rq = busiest;
env.loop_max = min(sysctl_sched_nr_migrate, busiest->nr_running);
+ update_h_load(env.src_cpu);
more_balance:
local_irq_save(flags);
double_rq_lock(this_rq, busiest);
- if (!env.loop)
- update_h_load(env.src_cpu);
/*
* cur_ld_moved - load moved in current iteration
diff --git a/kernel/sched/rt.c b/kernel/sched/rt.c
index 573e1ca..944cb68 100644
--- a/kernel/sched/rt.c
+++ b/kernel/sched/rt.c
@@ -788,6 +788,19 @@
const struct cpumask *span;
span = sched_rt_period_mask();
+#ifdef CONFIG_RT_GROUP_SCHED
+ /*
+ * FIXME: isolated CPUs should really leave the root task group,
+ * whether they are isolcpus or were isolated via cpusets, lest
+ * the timer run on a CPU which does not service all runqueues,
+ * potentially leaving other CPUs indefinitely throttled. If
+ * isolation is really required, the user will turn the throttle
+ * off to kill the perturbations it causes anyway. Meanwhile,
+ * this maintains functionality for boot and/or troubleshooting.
+ */
+ if (rt_b == &root_task_group.rt_bandwidth)
+ span = cpu_online_mask;
+#endif
for_each_cpu(i, span) {
int enqueue = 0;
struct rt_rq *rt_rq = sched_rt_period_rt_rq(rt_b, i);
diff --git a/kernel/sched/sched.h b/kernel/sched/sched.h
index c35a1a7..f6714d0 100644
--- a/kernel/sched/sched.h
+++ b/kernel/sched/sched.h
@@ -80,7 +80,7 @@
struct cfs_rq;
struct rt_rq;
-static LIST_HEAD(task_groups);
+extern struct list_head task_groups;
struct cfs_bandwidth {
#ifdef CONFIG_CFS_BANDWIDTH
@@ -374,7 +374,11 @@
#ifdef CONFIG_FAIR_GROUP_SCHED
/* list of leaf cfs_rq on this cpu: */
struct list_head leaf_cfs_rq_list;
-#endif
+#ifdef CONFIG_SMP
+ unsigned long h_load_throttle;
+#endif /* CONFIG_SMP */
+#endif /* CONFIG_FAIR_GROUP_SCHED */
+
#ifdef CONFIG_RT_GROUP_SCHED
struct list_head leaf_rt_rq_list;
#endif
diff --git a/kernel/sched/stop_task.c b/kernel/sched/stop_task.c
index 7b386e8..da5eb5be 100644
--- a/kernel/sched/stop_task.c
+++ b/kernel/sched/stop_task.c
@@ -27,8 +27,10 @@
{
struct task_struct *stop = rq->stop;
- if (stop && stop->on_rq)
+ if (stop && stop->on_rq) {
+ stop->se.exec_start = rq->clock_task;
return stop;
+ }
return NULL;
}
@@ -52,6 +54,21 @@
static void put_prev_task_stop(struct rq *rq, struct task_struct *prev)
{
+ struct task_struct *curr = rq->curr;
+ u64 delta_exec;
+
+ delta_exec = rq->clock_task - curr->se.exec_start;
+ if (unlikely((s64)delta_exec < 0))
+ delta_exec = 0;
+
+ schedstat_set(curr->se.statistics.exec_max,
+ max(curr->se.statistics.exec_max, delta_exec));
+
+ curr->se.sum_exec_runtime += delta_exec;
+ account_group_exec_runtime(curr, delta_exec);
+
+ curr->se.exec_start = rq->clock_task;
+ cpuacct_charge(curr, delta_exec);
}
static void task_tick_stop(struct rq *rq, struct task_struct *curr, int queued)
@@ -60,6 +77,9 @@
static void set_curr_task_stop(struct rq *rq)
{
+ struct task_struct *stop = rq->stop;
+
+ stop->se.exec_start = rq->clock_task;
}
static void switched_to_stop(struct rq *rq, struct task_struct *p)
diff --git a/kernel/task_work.c b/kernel/task_work.c
index 91d4e17..d320d44 100644
--- a/kernel/task_work.c
+++ b/kernel/task_work.c
@@ -75,6 +75,7 @@
p = q->next;
q->func(q);
q = p;
+ cond_resched();
}
}
}
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
index a470154..46da053 100644
--- a/kernel/time/jiffies.c
+++ b/kernel/time/jiffies.c
@@ -37,7 +37,7 @@
* requested HZ value. It is also not recommended
* for "tick-less" systems.
*/
-#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ))
+#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/SHIFTED_HZ))
/* Since jiffies uses a simple NSEC_PER_JIFFY multiplier
* conversion, the .shift value could be zero. However
diff --git a/kernel/time/ntp.c b/kernel/time/ntp.c
index b7fbadc..24174b4 100644
--- a/kernel/time/ntp.c
+++ b/kernel/time/ntp.c
@@ -28,7 +28,7 @@
/* USER_HZ period (usecs): */
unsigned long tick_usec = TICK_USEC;
-/* ACTHZ period (nsecs): */
+/* SHIFTED_HZ period (nsecs): */
unsigned long tick_nsec;
static u64 tick_length;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index f045cc5..e16af19 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -65,14 +65,14 @@
* used instead.
*/
struct timespec wall_to_monotonic;
- /* time spent in suspend */
- struct timespec total_sleep_time;
- /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
- struct timespec raw_time;
/* Offset clock monotonic -> clock realtime */
ktime_t offs_real;
+ /* time spent in suspend */
+ struct timespec total_sleep_time;
/* Offset clock monotonic -> clock boottime */
ktime_t offs_boot;
+ /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
+ struct timespec raw_time;
/* Seqlock for all timekeeper values */
seqlock_t lock;
};
@@ -108,13 +108,38 @@
static void tk_set_xtime(struct timekeeper *tk, const struct timespec *ts)
{
tk->xtime_sec = ts->tv_sec;
- tk->xtime_nsec = ts->tv_nsec << tk->shift;
+ tk->xtime_nsec = (u64)ts->tv_nsec << tk->shift;
}
static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts)
{
tk->xtime_sec += ts->tv_sec;
- tk->xtime_nsec += ts->tv_nsec << tk->shift;
+ tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift;
+}
+
+static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm)
+{
+ struct timespec tmp;
+
+ /*
+ * Verify consistency of: offset_real = -wall_to_monotonic
+ * before modifying anything
+ */
+ set_normalized_timespec(&tmp, -tk->wall_to_monotonic.tv_sec,
+ -tk->wall_to_monotonic.tv_nsec);
+ WARN_ON_ONCE(tk->offs_real.tv64 != timespec_to_ktime(tmp).tv64);
+ tk->wall_to_monotonic = wtm;
+ set_normalized_timespec(&tmp, -wtm.tv_sec, -wtm.tv_nsec);
+ tk->offs_real = timespec_to_ktime(tmp);
+}
+
+static void tk_set_sleep_time(struct timekeeper *tk, struct timespec t)
+{
+ /* Verify consistency before modifying */
+ WARN_ON_ONCE(tk->offs_boot.tv64 != timespec_to_ktime(tk->total_sleep_time).tv64);
+
+ tk->total_sleep_time = t;
+ tk->offs_boot = timespec_to_ktime(t);
}
/**
@@ -217,14 +242,6 @@
return nsec + arch_gettimeoffset();
}
-static void update_rt_offset(struct timekeeper *tk)
-{
- struct timespec tmp, *wtm = &tk->wall_to_monotonic;
-
- set_normalized_timespec(&tmp, -wtm->tv_sec, -wtm->tv_nsec);
- tk->offs_real = timespec_to_ktime(tmp);
-}
-
/* must hold write on timekeeper.lock */
static void timekeeping_update(struct timekeeper *tk, bool clearntp)
{
@@ -234,12 +251,10 @@
tk->ntp_error = 0;
ntp_clear();
}
- update_rt_offset(tk);
xt = tk_xtime(tk);
update_vsyscall(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult);
}
-
/**
* timekeeping_forward_now - update clock to the current time
*
@@ -277,18 +292,19 @@
*/
void getnstimeofday(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs = 0;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
+ ts->tv_sec = tk->xtime_sec;
+ ts->tv_nsec = timekeeping_get_ns(tk);
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
timespec_add_ns(ts, nsecs);
}
@@ -296,19 +312,18 @@
ktime_t ktime_get(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned int seq;
s64 secs, nsecs;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- secs = timekeeper.xtime_sec +
- timekeeper.wall_to_monotonic.tv_sec;
- nsecs = timekeeping_get_ns(&timekeeper) +
- timekeeper.wall_to_monotonic.tv_nsec;
+ seq = read_seqbegin(&tk->lock);
+ secs = tk->xtime_sec + tk->wall_to_monotonic.tv_sec;
+ nsecs = timekeeping_get_ns(tk) + tk->wall_to_monotonic.tv_nsec;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
/*
* Use ktime_set/ktime_add_ns to create a proper ktime on
* 32-bit architectures without CONFIG_KTIME_SCALAR.
@@ -327,18 +342,19 @@
*/
void ktime_get_ts(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec tomono;
unsigned int seq;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
- tomono = timekeeper.wall_to_monotonic;
+ seq = read_seqbegin(&tk->lock);
+ ts->tv_sec = tk->xtime_sec;
+ ts->tv_nsec = timekeeping_get_ns(tk);
+ tomono = tk->wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec,
ts->tv_nsec + tomono.tv_nsec);
@@ -358,22 +374,23 @@
*/
void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs_raw, nsecs_real;
WARN_ON_ONCE(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- *ts_raw = timekeeper.raw_time;
- ts_real->tv_sec = timekeeper.xtime_sec;
+ *ts_raw = tk->raw_time;
+ ts_real->tv_sec = tk->xtime_sec;
ts_real->tv_nsec = 0;
- nsecs_raw = timekeeping_get_ns_raw(&timekeeper);
- nsecs_real = timekeeping_get_ns(&timekeeper);
+ nsecs_raw = timekeeping_get_ns_raw(tk);
+ nsecs_real = timekeeping_get_ns(tk);
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
timespec_add_ns(ts_raw, nsecs_raw);
timespec_add_ns(ts_real, nsecs_real);
@@ -406,28 +423,28 @@
*/
int do_settimeofday(const struct timespec *tv)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec ts_delta, xt;
unsigned long flags;
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
- xt = tk_xtime(&timekeeper);
+ xt = tk_xtime(tk);
ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
- timekeeper.wall_to_monotonic =
- timespec_sub(timekeeper.wall_to_monotonic, ts_delta);
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, ts_delta));
- tk_set_xtime(&timekeeper, tv);
+ tk_set_xtime(tk, tv);
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
@@ -436,7 +453,6 @@
}
EXPORT_SYMBOL(do_settimeofday);
-
/**
* timekeeping_inject_offset - Adds or subtracts from the current time.
* @tv: pointer to the timespec variable containing the offset
@@ -445,23 +461,23 @@
*/
int timekeeping_inject_offset(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
if ((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
- tk_xtime_add(&timekeeper, ts);
- timekeeper.wall_to_monotonic =
- timespec_sub(timekeeper.wall_to_monotonic, *ts);
+ tk_xtime_add(tk, ts);
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *ts));
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
@@ -477,23 +493,24 @@
*/
static int change_clocksource(void *data)
{
+ struct timekeeper *tk = &timekeeper;
struct clocksource *new, *old;
unsigned long flags;
new = (struct clocksource *) data;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
if (!new->enable || new->enable(new) == 0) {
- old = timekeeper.clock;
- tk_setup_internals(&timekeeper, new);
+ old = tk->clock;
+ tk_setup_internals(tk, new);
if (old->disable)
old->disable(old);
}
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
return 0;
}
@@ -507,7 +524,9 @@
*/
void timekeeping_notify(struct clocksource *clock)
{
- if (timekeeper.clock == clock)
+ struct timekeeper *tk = &timekeeper;
+
+ if (tk->clock == clock)
return;
stop_machine(change_clocksource, clock, NULL);
tick_clock_notify();
@@ -536,35 +555,36 @@
*/
void getrawmonotonic(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
s64 nsecs;
do {
- seq = read_seqbegin(&timekeeper.lock);
- nsecs = timekeeping_get_ns_raw(&timekeeper);
- *ts = timekeeper.raw_time;
+ seq = read_seqbegin(&tk->lock);
+ nsecs = timekeeping_get_ns_raw(tk);
+ *ts = tk->raw_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
timespec_add_ns(ts, nsecs);
}
EXPORT_SYMBOL(getrawmonotonic);
-
/**
* timekeeping_valid_for_hres - Check if timekeeping is suitable for hres
*/
int timekeeping_valid_for_hres(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
int ret;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ret = timekeeper.clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
+ ret = tk->clock->flags & CLOCK_SOURCE_VALID_FOR_HRES;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
return ret;
}
@@ -574,15 +594,16 @@
*/
u64 timekeeping_max_deferment(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
u64 ret;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- ret = timekeeper.clock->max_idle_ns;
+ ret = tk->clock->max_idle_ns;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
return ret;
}
@@ -622,46 +643,43 @@
*/
void __init timekeeping_init(void)
{
+ struct timekeeper *tk = &timekeeper;
struct clocksource *clock;
unsigned long flags;
- struct timespec now, boot;
+ struct timespec now, boot, tmp;
read_persistent_clock(&now);
read_boot_clock(&boot);
- seqlock_init(&timekeeper.lock);
+ seqlock_init(&tk->lock);
ntp_init();
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
clock = clocksource_default_clock();
if (clock->enable)
clock->enable(clock);
- tk_setup_internals(&timekeeper, clock);
+ tk_setup_internals(tk, clock);
- tk_set_xtime(&timekeeper, &now);
- timekeeper.raw_time.tv_sec = 0;
- timekeeper.raw_time.tv_nsec = 0;
+ tk_set_xtime(tk, &now);
+ tk->raw_time.tv_sec = 0;
+ tk->raw_time.tv_nsec = 0;
if (boot.tv_sec == 0 && boot.tv_nsec == 0)
- boot = tk_xtime(&timekeeper);
+ boot = tk_xtime(tk);
- set_normalized_timespec(&timekeeper.wall_to_monotonic,
- -boot.tv_sec, -boot.tv_nsec);
- update_rt_offset(&timekeeper);
- timekeeper.total_sleep_time.tv_sec = 0;
- timekeeper.total_sleep_time.tv_nsec = 0;
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ set_normalized_timespec(&tmp, -boot.tv_sec, -boot.tv_nsec);
+ tk_set_wall_to_mono(tk, tmp);
+
+ tmp.tv_sec = 0;
+ tmp.tv_nsec = 0;
+ tk_set_sleep_time(tk, tmp);
+
+ write_sequnlock_irqrestore(&tk->lock, flags);
}
/* time in seconds when suspend began */
static struct timespec timekeeping_suspend_time;
-static void update_sleep_time(struct timespec t)
-{
- timekeeper.total_sleep_time = t;
- timekeeper.offs_boot = timespec_to_ktime(t);
-}
-
/**
* __timekeeping_inject_sleeptime - Internal function to add sleep interval
* @delta: pointer to a timespec delta value
@@ -677,13 +695,11 @@
"sleep delta value!\n");
return;
}
-
tk_xtime_add(tk, delta);
- tk->wall_to_monotonic = timespec_sub(tk->wall_to_monotonic, *delta);
- update_sleep_time(timespec_add(tk->total_sleep_time, *delta));
+ tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta));
+ tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta));
}
-
/**
* timekeeping_inject_sleeptime - Adds suspend interval to timeekeeping values
* @delta: pointer to a timespec delta value
@@ -696,6 +712,7 @@
*/
void timekeeping_inject_sleeptime(struct timespec *delta)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec ts;
@@ -704,21 +721,20 @@
if (!(ts.tv_sec == 0 && ts.tv_nsec == 0))
return;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
- timekeeping_forward_now(&timekeeper);
+ timekeeping_forward_now(tk);
- __timekeeping_inject_sleeptime(&timekeeper, delta);
+ __timekeeping_inject_sleeptime(tk, delta);
- timekeeping_update(&timekeeper, true);
+ timekeeping_update(tk, true);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
/* signal hrtimers about time change */
clock_was_set();
}
-
/**
* timekeeping_resume - Resumes the generic timekeeping subsystem.
*
@@ -728,6 +744,7 @@
*/
static void timekeeping_resume(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec ts;
@@ -735,18 +752,18 @@
clocksource_resume();
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
ts = timespec_sub(ts, timekeeping_suspend_time);
- __timekeeping_inject_sleeptime(&timekeeper, &ts);
+ __timekeeping_inject_sleeptime(tk, &ts);
}
/* re-base the last cycle value */
- timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock);
- timekeeper.ntp_error = 0;
+ tk->clock->cycle_last = tk->clock->read(tk->clock);
+ tk->ntp_error = 0;
timekeeping_suspended = 0;
- timekeeping_update(&timekeeper, false);
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ timekeeping_update(tk, false);
+ write_sequnlock_irqrestore(&tk->lock, flags);
touch_softlockup_watchdog();
@@ -758,14 +775,15 @@
static int timekeeping_suspend(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long flags;
struct timespec delta, delta_delta;
static struct timespec old_delta;
read_persistent_clock(&timekeeping_suspend_time);
- write_seqlock_irqsave(&timekeeper.lock, flags);
- timekeeping_forward_now(&timekeeper);
+ write_seqlock_irqsave(&tk->lock, flags);
+ timekeeping_forward_now(tk);
timekeeping_suspended = 1;
/*
@@ -774,7 +792,7 @@
* try to compensate so the difference in system time
* and persistent_clock time stays close to constant.
*/
- delta = timespec_sub(tk_xtime(&timekeeper), timekeeping_suspend_time);
+ delta = timespec_sub(tk_xtime(tk), timekeeping_suspend_time);
delta_delta = timespec_sub(delta, old_delta);
if (abs(delta_delta.tv_sec) >= 2) {
/*
@@ -787,7 +805,7 @@
timekeeping_suspend_time =
timespec_add(timekeeping_suspend_time, delta_delta);
}
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
clocksource_suspend();
@@ -898,27 +916,29 @@
* the error. This causes the likely below to be unlikely.
*
* The proper fix is to avoid rounding up by using
- * the high precision timekeeper.xtime_nsec instead of
+ * the high precision tk->xtime_nsec instead of
* xtime.tv_nsec everywhere. Fixing this will take some
* time.
*/
if (likely(error <= interval))
adj = 1;
else
- adj = timekeeping_bigadjust(tk, error, &interval,
- &offset);
- } else if (error < -interval) {
- /* See comment above, this is just switched for the negative */
- error >>= 2;
- if (likely(error >= -interval)) {
- adj = -1;
- interval = -interval;
- offset = -offset;
- } else
- adj = timekeeping_bigadjust(tk, error, &interval,
- &offset);
- } else
- return;
+ adj = timekeeping_bigadjust(tk, error, &interval, &offset);
+ } else {
+ if (error < -interval) {
+ /* See comment above, this is just switched for the negative */
+ error >>= 2;
+ if (likely(error >= -interval)) {
+ adj = -1;
+ interval = -interval;
+ offset = -offset;
+ } else {
+ adj = timekeeping_bigadjust(tk, error, &interval, &offset);
+ }
+ } else {
+ goto out_adjust;
+ }
+ }
if (unlikely(tk->clock->maxadj &&
(tk->mult + adj > tk->clock->mult + tk->clock->maxadj))) {
@@ -981,6 +1001,7 @@
tk->xtime_nsec -= offset;
tk->ntp_error -= (interval - offset) << tk->ntp_error_shift;
+out_adjust:
/*
* It may be possible that when we entered this function, xtime_nsec
* was very small. Further, if we're slightly speeding the clocksource
@@ -1003,7 +1024,6 @@
}
-
/**
* accumulate_nsecs_to_secs - Accumulates nsecs into secs
*
@@ -1024,15 +1044,21 @@
/* Figure out if its a leap sec and apply if needed */
leap = second_overflow(tk->xtime_sec);
- tk->xtime_sec += leap;
- tk->wall_to_monotonic.tv_sec -= leap;
- if (leap)
- clock_was_set_delayed();
+ if (unlikely(leap)) {
+ struct timespec ts;
+ tk->xtime_sec += leap;
+
+ ts.tv_sec = leap;
+ ts.tv_nsec = 0;
+ tk_set_wall_to_mono(tk,
+ timespec_sub(tk->wall_to_monotonic, ts));
+
+ clock_was_set_delayed();
+ }
}
}
-
/**
* logarithmic_accumulation - shifted accumulation of cycles
*
@@ -1076,7 +1102,6 @@
return offset;
}
-
/**
* update_wall_time - Uses the current clocksource to increment the wall time
*
@@ -1084,21 +1109,22 @@
static void update_wall_time(void)
{
struct clocksource *clock;
+ struct timekeeper *tk = &timekeeper;
cycle_t offset;
int shift = 0, maxshift;
unsigned long flags;
s64 remainder;
- write_seqlock_irqsave(&timekeeper.lock, flags);
+ write_seqlock_irqsave(&tk->lock, flags);
/* Make sure we're fully resumed: */
if (unlikely(timekeeping_suspended))
goto out;
- clock = timekeeper.clock;
+ clock = tk->clock;
#ifdef CONFIG_ARCH_USES_GETTIMEOFFSET
- offset = timekeeper.cycle_interval;
+ offset = tk->cycle_interval;
#else
offset = (clock->read(clock) - clock->cycle_last) & clock->mask;
#endif
@@ -1111,19 +1137,19 @@
* chunk in one go, and then try to consume the next smaller
* doubled multiple.
*/
- shift = ilog2(offset) - ilog2(timekeeper.cycle_interval);
+ shift = ilog2(offset) - ilog2(tk->cycle_interval);
shift = max(0, shift);
/* Bound shift to one less than what overflows tick_length */
maxshift = (64 - (ilog2(ntp_tick_length())+1)) - 1;
shift = min(shift, maxshift);
- while (offset >= timekeeper.cycle_interval) {
- offset = logarithmic_accumulation(&timekeeper, offset, shift);
- if(offset < timekeeper.cycle_interval<<shift)
+ while (offset >= tk->cycle_interval) {
+ offset = logarithmic_accumulation(tk, offset, shift);
+ if (offset < tk->cycle_interval<<shift)
shift--;
}
/* correct the clock when NTP error is too big */
- timekeeping_adjust(&timekeeper, offset);
+ timekeeping_adjust(tk, offset);
/*
@@ -1135,21 +1161,21 @@
* the vsyscall implementations are converted to use xtime_nsec
* (shifted nanoseconds), this can be killed.
*/
- remainder = timekeeper.xtime_nsec & ((1 << timekeeper.shift) - 1);
- timekeeper.xtime_nsec -= remainder;
- timekeeper.xtime_nsec += 1 << timekeeper.shift;
- timekeeper.ntp_error += remainder << timekeeper.ntp_error_shift;
+ remainder = tk->xtime_nsec & ((1 << tk->shift) - 1);
+ tk->xtime_nsec -= remainder;
+ tk->xtime_nsec += 1 << tk->shift;
+ tk->ntp_error += remainder << tk->ntp_error_shift;
/*
* Finally, make sure that after the rounding
* xtime_nsec isn't larger than NSEC_PER_SEC
*/
- accumulate_nsecs_to_secs(&timekeeper);
+ accumulate_nsecs_to_secs(tk);
- timekeeping_update(&timekeeper, false);
+ timekeeping_update(tk, false);
out:
- write_sequnlock_irqrestore(&timekeeper.lock, flags);
+ write_sequnlock_irqrestore(&tk->lock, flags);
}
@@ -1166,18 +1192,18 @@
*/
void getboottime(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec boottime = {
- .tv_sec = timekeeper.wall_to_monotonic.tv_sec +
- timekeeper.total_sleep_time.tv_sec,
- .tv_nsec = timekeeper.wall_to_monotonic.tv_nsec +
- timekeeper.total_sleep_time.tv_nsec
+ .tv_sec = tk->wall_to_monotonic.tv_sec +
+ tk->total_sleep_time.tv_sec,
+ .tv_nsec = tk->wall_to_monotonic.tv_nsec +
+ tk->total_sleep_time.tv_nsec
};
set_normalized_timespec(ts, -boottime.tv_sec, -boottime.tv_nsec);
}
EXPORT_SYMBOL_GPL(getboottime);
-
/**
* get_monotonic_boottime - Returns monotonic time since boot
* @ts: pointer to the timespec to be set
@@ -1189,19 +1215,20 @@
*/
void get_monotonic_boottime(struct timespec *ts)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec tomono, sleep;
unsigned int seq;
WARN_ON(timekeeping_suspended);
do {
- seq = read_seqbegin(&timekeeper.lock);
- ts->tv_sec = timekeeper.xtime_sec;
- ts->tv_nsec = timekeeping_get_ns(&timekeeper);
- tomono = timekeeper.wall_to_monotonic;
- sleep = timekeeper.total_sleep_time;
+ seq = read_seqbegin(&tk->lock);
+ ts->tv_sec = tk->xtime_sec;
+ ts->tv_nsec = timekeeping_get_ns(tk);
+ tomono = tk->wall_to_monotonic;
+ sleep = tk->total_sleep_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec);
@@ -1231,31 +1258,38 @@
*/
void monotonic_to_bootbased(struct timespec *ts)
{
- *ts = timespec_add(*ts, timekeeper.total_sleep_time);
+ struct timekeeper *tk = &timekeeper;
+
+ *ts = timespec_add(*ts, tk->total_sleep_time);
}
EXPORT_SYMBOL_GPL(monotonic_to_bootbased);
unsigned long get_seconds(void)
{
- return timekeeper.xtime_sec;
+ struct timekeeper *tk = &timekeeper;
+
+ return tk->xtime_sec;
}
EXPORT_SYMBOL(get_seconds);
struct timespec __current_kernel_time(void)
{
- return tk_xtime(&timekeeper);
+ struct timekeeper *tk = &timekeeper;
+
+ return tk_xtime(tk);
}
struct timespec current_kernel_time(void)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec now;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- now = tk_xtime(&timekeeper);
- } while (read_seqretry(&timekeeper.lock, seq));
+ now = tk_xtime(tk);
+ } while (read_seqretry(&tk->lock, seq));
return now;
}
@@ -1263,15 +1297,16 @@
struct timespec get_monotonic_coarse(void)
{
+ struct timekeeper *tk = &timekeeper;
struct timespec now, mono;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- now = tk_xtime(&timekeeper);
- mono = timekeeper.wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ now = tk_xtime(tk);
+ mono = tk->wall_to_monotonic;
+ } while (read_seqretry(&tk->lock, seq));
set_normalized_timespec(&now, now.tv_sec + mono.tv_sec,
now.tv_nsec + mono.tv_nsec);
@@ -1300,14 +1335,15 @@
void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
struct timespec *wtom, struct timespec *sleep)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
do {
- seq = read_seqbegin(&timekeeper.lock);
- *xtim = tk_xtime(&timekeeper);
- *wtom = timekeeper.wall_to_monotonic;
- *sleep = timekeeper.total_sleep_time;
- } while (read_seqretry(&timekeeper.lock, seq));
+ seq = read_seqbegin(&tk->lock);
+ *xtim = tk_xtime(tk);
+ *wtom = tk->wall_to_monotonic;
+ *sleep = tk->total_sleep_time;
+ } while (read_seqretry(&tk->lock, seq));
}
#ifdef CONFIG_HIGH_RES_TIMERS
@@ -1321,19 +1357,20 @@
*/
ktime_t ktime_get_update_offsets(ktime_t *offs_real, ktime_t *offs_boot)
{
+ struct timekeeper *tk = &timekeeper;
ktime_t now;
unsigned int seq;
u64 secs, nsecs;
do {
- seq = read_seqbegin(&timekeeper.lock);
+ seq = read_seqbegin(&tk->lock);
- secs = timekeeper.xtime_sec;
- nsecs = timekeeping_get_ns(&timekeeper);
+ secs = tk->xtime_sec;
+ nsecs = timekeeping_get_ns(tk);
- *offs_real = timekeeper.offs_real;
- *offs_boot = timekeeper.offs_boot;
- } while (read_seqretry(&timekeeper.lock, seq));
+ *offs_real = tk->offs_real;
+ *offs_boot = tk->offs_boot;
+ } while (read_seqretry(&tk->lock, seq));
now = ktime_add_ns(ktime_set(secs, 0), nsecs);
now = ktime_sub(now, *offs_real);
@@ -1346,19 +1383,19 @@
*/
ktime_t ktime_get_monotonic_offset(void)
{
+ struct timekeeper *tk = &timekeeper;
unsigned long seq;
struct timespec wtom;
do {
- seq = read_seqbegin(&timekeeper.lock);
- wtom = timekeeper.wall_to_monotonic;
- } while (read_seqretry(&timekeeper.lock, seq));
+ seq = read_seqbegin(&tk->lock);
+ wtom = tk->wall_to_monotonic;
+ } while (read_seqretry(&tk->lock, seq));
return timespec_to_ktime(wtom);
}
EXPORT_SYMBOL_GPL(ktime_get_monotonic_offset);
-
/**
* xtime_update() - advances the timekeeping infrastructure
* @ticks: number of ticks, that have elapsed since the last call.
diff --git a/kernel/timer.c b/kernel/timer.c
index a61c093..8c5e7b9 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -1407,13 +1407,6 @@
#endif
-#ifndef __alpha__
-
-/*
- * The Alpha uses getxpid, getxuid, and getxgid instead. Maybe this
- * should be moved into arch/i386 instead?
- */
-
/**
* sys_getpid - return the thread group id of the current process
*
@@ -1469,8 +1462,6 @@
return from_kgid_munged(current_user_ns(), current_egid());
}
-#endif
-
static void process_timeout(unsigned long __data)
{
wake_up_process((struct task_struct *)__data);
diff --git a/kernel/trace/trace_event_perf.c b/kernel/trace/trace_event_perf.c
index fee3752..8a6d2ee 100644
--- a/kernel/trace/trace_event_perf.c
+++ b/kernel/trace/trace_event_perf.c
@@ -281,7 +281,7 @@
head = this_cpu_ptr(event_function.perf_events);
perf_trace_buf_submit(entry, ENTRY_SIZE, rctx, 0,
- 1, ®s, head);
+ 1, ®s, head, NULL);
#undef ENTRY_SIZE
}
diff --git a/kernel/trace/trace_kprobe.c b/kernel/trace/trace_kprobe.c
index b31d3d5..1a21170 100644
--- a/kernel/trace/trace_kprobe.c
+++ b/kernel/trace/trace_kprobe.c
@@ -1002,7 +1002,8 @@
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx,
+ entry->ip, 1, regs, head, NULL);
}
/* Kretprobe profile handler */
@@ -1033,7 +1034,8 @@
store_trace_args(sizeof(*entry), tp, regs, (u8 *)&entry[1], dsize);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ret_ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx,
+ entry->ret_ip, 1, regs, head, NULL);
}
#endif /* CONFIG_PERF_EVENTS */
diff --git a/kernel/trace/trace_syscalls.c b/kernel/trace/trace_syscalls.c
index 96fc733..60e4d78 100644
--- a/kernel/trace/trace_syscalls.c
+++ b/kernel/trace/trace_syscalls.c
@@ -532,7 +532,7 @@
(unsigned long *)&rec->args);
head = this_cpu_ptr(sys_data->enter_event->perf_events);
- perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
+ perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
}
int perf_sysenter_enable(struct ftrace_event_call *call)
@@ -608,7 +608,7 @@
rec->ret = syscall_get_return_value(current, regs);
head = this_cpu_ptr(sys_data->exit_event->perf_events);
- perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head);
+ perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
}
int perf_sysexit_enable(struct ftrace_event_call *call)
diff --git a/kernel/trace/trace_uprobe.c b/kernel/trace/trace_uprobe.c
index 2b36ac6..03003cd 100644
--- a/kernel/trace/trace_uprobe.c
+++ b/kernel/trace/trace_uprobe.c
@@ -670,7 +670,7 @@
call_fetch(&tu->args[i].fetch, regs, data + tu->args[i].offset);
head = this_cpu_ptr(call->perf_events);
- perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head);
+ perf_trace_buf_submit(entry, size, rctx, entry->ip, 1, regs, head, NULL);
out:
preempt_enable();
diff --git a/kernel/watchdog.c b/kernel/watchdog.c
index 69add8a..4b1dfba 100644
--- a/kernel/watchdog.c
+++ b/kernel/watchdog.c
@@ -575,7 +575,7 @@
/*
* Create/destroy watchdog threads as CPUs come and go:
*/
-static int
+static int __cpuinit
cpu_callback(struct notifier_block *nfb, unsigned long action, void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
@@ -610,27 +610,10 @@
return NOTIFY_OK;
}
-static struct notifier_block cpu_nfb = {
+static struct notifier_block __cpuinitdata cpu_nfb = {
.notifier_call = cpu_callback
};
-#ifdef CONFIG_SUSPEND
-/*
- * On exit from suspend we force an offline->online transition on the boot CPU
- * so that the PMU state that was lost while in suspended state gets set up
- * properly for the boot CPU. This information is required for restarting the
- * NMI watchdog.
- */
-void lockup_detector_bootcpu_resume(void)
-{
- void *cpu = (void *)(long)smp_processor_id();
-
- cpu_callback(&cpu_nfb, CPU_DEAD_FROZEN, cpu);
- cpu_callback(&cpu_nfb, CPU_UP_PREPARE_FROZEN, cpu);
- cpu_callback(&cpu_nfb, CPU_ONLINE_FROZEN, cpu);
-}
-#endif
-
void __init lockup_detector_init(void)
{
void *cpu = (void *)(long)smp_processor_id();
diff --git a/lib/bcd.c b/lib/bcd.c
index 55efaf7..40d304e 100644
--- a/lib/bcd.c
+++ b/lib/bcd.c
@@ -1,14 +1,14 @@
#include <linux/bcd.h>
#include <linux/export.h>
-unsigned bcd2bin(unsigned char val)
+unsigned _bcd2bin(unsigned char val)
{
return (val & 0x0f) + (val >> 4) * 10;
}
-EXPORT_SYMBOL(bcd2bin);
+EXPORT_SYMBOL(_bcd2bin);
-unsigned char bin2bcd(unsigned val)
+unsigned char _bin2bcd(unsigned val)
{
return ((val / 10) << 4) + val % 10;
}
-EXPORT_SYMBOL(bin2bcd);
+EXPORT_SYMBOL(_bin2bcd);
diff --git a/mm/backing-dev.c b/mm/backing-dev.c
index 6b4718e..b41823c 100644
--- a/mm/backing-dev.c
+++ b/mm/backing-dev.c
@@ -39,12 +39,6 @@
LIST_HEAD(bdi_list);
LIST_HEAD(bdi_pending_list);
-static struct task_struct *sync_supers_tsk;
-static struct timer_list sync_supers_timer;
-
-static int bdi_sync_supers(void *);
-static void sync_supers_timer_fn(unsigned long);
-
void bdi_lock_two(struct bdi_writeback *wb1, struct bdi_writeback *wb2)
{
if (wb1 < wb2) {
@@ -250,12 +244,6 @@
{
int err;
- sync_supers_tsk = kthread_run(bdi_sync_supers, NULL, "sync_supers");
- BUG_ON(IS_ERR(sync_supers_tsk));
-
- setup_timer(&sync_supers_timer, sync_supers_timer_fn, 0);
- bdi_arm_supers_timer();
-
err = bdi_init(&default_backing_dev_info);
if (!err)
bdi_register(&default_backing_dev_info, NULL, "default");
@@ -270,46 +258,6 @@
return wb_has_dirty_io(&bdi->wb);
}
-/*
- * kupdated() used to do this. We cannot do it from the bdi_forker_thread()
- * or we risk deadlocking on ->s_umount. The longer term solution would be
- * to implement sync_supers_bdi() or similar and simply do it from the
- * bdi writeback thread individually.
- */
-static int bdi_sync_supers(void *unused)
-{
- set_user_nice(current, 0);
-
- while (!kthread_should_stop()) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
-
- /*
- * Do this periodically, like kupdated() did before.
- */
- sync_supers();
- }
-
- return 0;
-}
-
-void bdi_arm_supers_timer(void)
-{
- unsigned long next;
-
- if (!dirty_writeback_interval)
- return;
-
- next = msecs_to_jiffies(dirty_writeback_interval * 10) + jiffies;
- mod_timer(&sync_supers_timer, round_jiffies_up(next));
-}
-
-static void sync_supers_timer_fn(unsigned long unused)
-{
- wake_up_process(sync_supers_tsk);
- bdi_arm_supers_timer();
-}
-
static void wakeup_timer_fn(unsigned long data)
{
struct backing_dev_info *bdi = (struct backing_dev_info *)data;
diff --git a/mm/compaction.c b/mm/compaction.c
index e78cb96..7fcd3a5 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -51,6 +51,47 @@
}
/*
+ * Compaction requires the taking of some coarse locks that are potentially
+ * very heavily contended. Check if the process needs to be scheduled or
+ * if the lock is contended. For async compaction, back out in the event
+ * if contention is severe. For sync compaction, schedule.
+ *
+ * Returns true if the lock is held.
+ * Returns false if the lock is released and compaction should abort
+ */
+static bool compact_checklock_irqsave(spinlock_t *lock, unsigned long *flags,
+ bool locked, struct compact_control *cc)
+{
+ if (need_resched() || spin_is_contended(lock)) {
+ if (locked) {
+ spin_unlock_irqrestore(lock, *flags);
+ locked = false;
+ }
+
+ /* async aborts if taking too long or contended */
+ if (!cc->sync) {
+ if (cc->contended)
+ *cc->contended = true;
+ return false;
+ }
+
+ cond_resched();
+ if (fatal_signal_pending(current))
+ return false;
+ }
+
+ if (!locked)
+ spin_lock_irqsave(lock, *flags);
+ return true;
+}
+
+static inline bool compact_trylock_irqsave(spinlock_t *lock,
+ unsigned long *flags, struct compact_control *cc)
+{
+ return compact_checklock_irqsave(lock, flags, false, cc);
+}
+
+/*
* Isolate free pages onto a private freelist. Caller must hold zone->lock.
* If @strict is true, will abort returning 0 on any invalid PFNs or non-free
* pages inside of the pageblock (even though it may still end up isolating
@@ -173,7 +214,7 @@
}
/* Update the number of anon and file isolated pages in the zone */
-static void acct_isolated(struct zone *zone, struct compact_control *cc)
+static void acct_isolated(struct zone *zone, bool locked, struct compact_control *cc)
{
struct page *page;
unsigned int count[2] = { 0, };
@@ -181,8 +222,14 @@
list_for_each_entry(page, &cc->migratepages, lru)
count[!!page_is_file_cache(page)]++;
- __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
- __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ /* If locked we can use the interrupt unsafe versions */
+ if (locked) {
+ __mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ __mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ } else {
+ mod_zone_page_state(zone, NR_ISOLATED_ANON, count[0]);
+ mod_zone_page_state(zone, NR_ISOLATED_FILE, count[1]);
+ }
}
/* Similar to reclaim, but different enough that they don't share logic */
@@ -228,6 +275,8 @@
struct list_head *migratelist = &cc->migratepages;
isolate_mode_t mode = 0;
struct lruvec *lruvec;
+ unsigned long flags;
+ bool locked;
/*
* Ensure that there are not too many pages isolated from the LRU
@@ -247,25 +296,22 @@
/* Time to isolate some pages for migration */
cond_resched();
- spin_lock_irq(&zone->lru_lock);
+ spin_lock_irqsave(&zone->lru_lock, flags);
+ locked = true;
for (; low_pfn < end_pfn; low_pfn++) {
struct page *page;
- bool locked = true;
/* give a chance to irqs before checking need_resched() */
if (!((low_pfn+1) % SWAP_CLUSTER_MAX)) {
- spin_unlock_irq(&zone->lru_lock);
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
locked = false;
}
- if (need_resched() || spin_is_contended(&zone->lru_lock)) {
- if (locked)
- spin_unlock_irq(&zone->lru_lock);
- cond_resched();
- spin_lock_irq(&zone->lru_lock);
- if (fatal_signal_pending(current))
- break;
- } else if (!locked)
- spin_lock_irq(&zone->lru_lock);
+
+ /* Check if it is ok to still hold the lock */
+ locked = compact_checklock_irqsave(&zone->lru_lock, &flags,
+ locked, cc);
+ if (!locked)
+ break;
/*
* migrate_pfn does not necessarily start aligned to a
@@ -349,9 +395,10 @@
}
}
- acct_isolated(zone, cc);
+ acct_isolated(zone, locked, cc);
- spin_unlock_irq(&zone->lru_lock);
+ if (locked)
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
trace_mm_compaction_isolate_migratepages(nr_scanned, nr_isolated);
@@ -384,6 +431,20 @@
}
/*
+ * Returns the start pfn of the last page block in a zone. This is the starting
+ * point for full compaction of a zone. Compaction searches for free pages from
+ * the end of each zone, while isolate_freepages_block scans forward inside each
+ * page block.
+ */
+static unsigned long start_free_pfn(struct zone *zone)
+{
+ unsigned long free_pfn;
+ free_pfn = zone->zone_start_pfn + zone->spanned_pages;
+ free_pfn &= ~(pageblock_nr_pages-1);
+ return free_pfn;
+}
+
+/*
* Based on information in the current compact_control, find blocks
* suitable for isolating free pages from and then isolate them.
*/
@@ -422,17 +483,6 @@
pfn -= pageblock_nr_pages) {
unsigned long isolated;
- /*
- * Skip ahead if another thread is compacting in the area
- * simultaneously. If we wrapped around, we can only skip
- * ahead if zone->compact_cached_free_pfn also wrapped to
- * above our starting point.
- */
- if (cc->order > 0 && (!cc->wrapped ||
- zone->compact_cached_free_pfn >
- cc->start_free_pfn))
- pfn = min(pfn, zone->compact_cached_free_pfn);
-
if (!pfn_valid(pfn))
continue;
@@ -458,7 +508,16 @@
* are disabled
*/
isolated = 0;
- spin_lock_irqsave(&zone->lock, flags);
+
+ /*
+ * The zone lock must be held to isolate freepages. This
+ * unfortunately this is a very coarse lock and can be
+ * heavily contended if there are parallel allocations
+ * or parallel compactions. For async compaction do not
+ * spin on the lock
+ */
+ if (!compact_trylock_irqsave(&zone->lock, &flags, cc))
+ break;
if (suitable_migration_target(page)) {
end_pfn = min(pfn + pageblock_nr_pages, zone_end_pfn);
isolated = isolate_freepages_block(pfn, end_pfn,
@@ -474,7 +533,15 @@
*/
if (isolated) {
high_pfn = max(high_pfn, pfn);
- if (cc->order > 0)
+
+ /*
+ * If the free scanner has wrapped, update
+ * compact_cached_free_pfn to point to the highest
+ * pageblock with free pages. This reduces excessive
+ * scanning of full pageblocks near the end of the
+ * zone
+ */
+ if (cc->order > 0 && cc->wrapped)
zone->compact_cached_free_pfn = high_pfn;
}
}
@@ -484,6 +551,11 @@
cc->free_pfn = high_pfn;
cc->nr_freepages = nr_freepages;
+
+ /* If compact_cached_free_pfn is reset then set it now */
+ if (cc->order > 0 && !cc->wrapped &&
+ zone->compact_cached_free_pfn == start_free_pfn(zone))
+ zone->compact_cached_free_pfn = high_pfn;
}
/*
@@ -570,20 +642,6 @@
return ISOLATE_SUCCESS;
}
-/*
- * Returns the start pfn of the last page block in a zone. This is the starting
- * point for full compaction of a zone. Compaction searches for free pages from
- * the end of each zone, while isolate_freepages_block scans forward inside each
- * page block.
- */
-static unsigned long start_free_pfn(struct zone *zone)
-{
- unsigned long free_pfn;
- free_pfn = zone->zone_start_pfn + zone->spanned_pages;
- free_pfn &= ~(pageblock_nr_pages-1);
- return free_pfn;
-}
-
static int compact_finished(struct zone *zone,
struct compact_control *cc)
{
@@ -771,7 +829,7 @@
static unsigned long compact_zone_order(struct zone *zone,
int order, gfp_t gfp_mask,
- bool sync)
+ bool sync, bool *contended)
{
struct compact_control cc = {
.nr_freepages = 0,
@@ -780,6 +838,7 @@
.migratetype = allocflags_to_migratetype(gfp_mask),
.zone = zone,
.sync = sync,
+ .contended = contended,
};
INIT_LIST_HEAD(&cc.freepages);
INIT_LIST_HEAD(&cc.migratepages);
@@ -801,7 +860,7 @@
*/
unsigned long try_to_compact_pages(struct zonelist *zonelist,
int order, gfp_t gfp_mask, nodemask_t *nodemask,
- bool sync)
+ bool sync, bool *contended)
{
enum zone_type high_zoneidx = gfp_zone(gfp_mask);
int may_enter_fs = gfp_mask & __GFP_FS;
@@ -825,7 +884,8 @@
nodemask) {
int status;
- status = compact_zone_order(zone, order, gfp_mask, sync);
+ status = compact_zone_order(zone, order, gfp_mask, sync,
+ contended);
rc = max(status, rc);
/* If a normal allocation would succeed, stop compacting */
@@ -861,7 +921,7 @@
if (cc->order > 0) {
int ok = zone_watermark_ok(zone, cc->order,
low_wmark_pages(zone), 0, 0);
- if (ok && cc->order > zone->compact_order_failed)
+ if (ok && cc->order >= zone->compact_order_failed)
zone->compact_order_failed = cc->order + 1;
/* Currently async compaction is never deferred. */
else if (!ok && cc->sync)
diff --git a/mm/internal.h b/mm/internal.h
index 3314f79..b8c91b3 100644
--- a/mm/internal.h
+++ b/mm/internal.h
@@ -130,6 +130,7 @@
int order; /* order a direct compactor needs */
int migratetype; /* MOVABLE, RECLAIMABLE etc */
struct zone *zone;
+ bool *contended; /* True if a lock was contended */
};
unsigned long
diff --git a/mm/mmap.c b/mm/mmap.c
index e3e8691..9adee9f 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -2309,7 +2309,7 @@
}
vm_unacct_memory(nr_accounted);
- BUG_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
+ WARN_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT);
}
/* Insert vm structure into process list sorted by address
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index e5363f3..5ad5ce2 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -1532,7 +1532,6 @@
void __user *buffer, size_t *length, loff_t *ppos)
{
proc_dointvec(table, write, buffer, length, ppos);
- bdi_arm_supers_timer();
return 0;
}
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 009ac28..c66fb87 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1928,6 +1928,17 @@
zlc_active = 0;
goto zonelist_scan;
}
+
+ if (page)
+ /*
+ * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was
+ * necessary to allocate the page. The expectation is
+ * that the caller is taking steps that will free more
+ * memory. The caller should avoid the page being used
+ * for !PFMEMALLOC purposes.
+ */
+ page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS);
+
return page;
}
@@ -2091,7 +2102,7 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
struct page *page;
@@ -2106,7 +2117,8 @@
current->flags |= PF_MEMALLOC;
*did_some_progress = try_to_compact_pages(zonelist, order, gfp_mask,
- nodemask, sync_migration);
+ nodemask, sync_migration,
+ contended_compaction);
current->flags &= ~PF_MEMALLOC;
if (*did_some_progress != COMPACT_SKIPPED) {
@@ -2152,7 +2164,7 @@
struct zonelist *zonelist, enum zone_type high_zoneidx,
nodemask_t *nodemask, int alloc_flags, struct zone *preferred_zone,
int migratetype, bool sync_migration,
- bool *deferred_compaction,
+ bool *contended_compaction, bool *deferred_compaction,
unsigned long *did_some_progress)
{
return NULL;
@@ -2325,6 +2337,7 @@
unsigned long did_some_progress;
bool sync_migration = false;
bool deferred_compaction = false;
+ bool contended_compaction = false;
/*
* In the slowpath, we sanity check order to avoid ever trying to
@@ -2389,14 +2402,6 @@
zonelist, high_zoneidx, nodemask,
preferred_zone, migratetype);
if (page) {
- /*
- * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was
- * necessary to allocate the page. The expectation is
- * that the caller is taking steps that will free more
- * memory. The caller should avoid the page being used
- * for !PFMEMALLOC purposes.
- */
- page->pfmemalloc = true;
goto got_pg;
}
}
@@ -2422,6 +2427,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2431,10 +2437,11 @@
/*
* If compaction is deferred for high-order allocations, it is because
* sync compaction recently failed. In this is the case and the caller
- * has requested the system not be heavily disrupted, fail the
- * allocation now instead of entering direct reclaim
+ * requested a movable allocation that does not heavily disrupt the
+ * system then fail the allocation instead of entering direct reclaim.
*/
- if (deferred_compaction && (gfp_mask & __GFP_NO_KSWAPD))
+ if ((deferred_compaction || contended_compaction) &&
+ (gfp_mask & __GFP_NO_KSWAPD))
goto nopage;
/* Try direct reclaim and then allocating */
@@ -2505,6 +2512,7 @@
nodemask,
alloc_flags, preferred_zone,
migratetype, sync_migration,
+ &contended_compaction,
&deferred_compaction,
&did_some_progress);
if (page)
@@ -2569,8 +2577,6 @@
page = __alloc_pages_slowpath(gfp_mask, order,
zonelist, high_zoneidx, nodemask,
preferred_zone, migratetype);
- else
- page->pfmemalloc = false;
trace_mm_page_alloc(page, order, gfp_mask, migratetype);
diff --git a/net/8021q/vlan_dev.c b/net/8021q/vlan_dev.c
index 73a2a83..4024424 100644
--- a/net/8021q/vlan_dev.c
+++ b/net/8021q/vlan_dev.c
@@ -137,9 +137,21 @@
return rc;
}
+static inline netdev_tx_t vlan_netpoll_send_skb(struct vlan_dev_priv *vlan, struct sk_buff *skb)
+{
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ if (vlan->netpoll)
+ netpoll_send_skb(vlan->netpoll, skb);
+#else
+ BUG();
+#endif
+ return NETDEV_TX_OK;
+}
+
static netdev_tx_t vlan_dev_hard_start_xmit(struct sk_buff *skb,
struct net_device *dev)
{
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
struct vlan_ethhdr *veth = (struct vlan_ethhdr *)(skb->data);
unsigned int len;
int ret;
@@ -150,29 +162,30 @@
* OTHER THINGS LIKE FDDI/TokenRing/802.3 SNAPs...
*/
if (veth->h_vlan_proto != htons(ETH_P_8021Q) ||
- vlan_dev_priv(dev)->flags & VLAN_FLAG_REORDER_HDR) {
+ vlan->flags & VLAN_FLAG_REORDER_HDR) {
u16 vlan_tci;
- vlan_tci = vlan_dev_priv(dev)->vlan_id;
+ vlan_tci = vlan->vlan_id;
vlan_tci |= vlan_dev_get_egress_qos_mask(dev, skb);
skb = __vlan_hwaccel_put_tag(skb, vlan_tci);
}
- skb->dev = vlan_dev_priv(dev)->real_dev;
+ skb->dev = vlan->real_dev;
len = skb->len;
- if (netpoll_tx_running(dev))
- return skb->dev->netdev_ops->ndo_start_xmit(skb, skb->dev);
+ if (unlikely(netpoll_tx_running(dev)))
+ return vlan_netpoll_send_skb(vlan, skb);
+
ret = dev_queue_xmit(skb);
if (likely(ret == NET_XMIT_SUCCESS || ret == NET_XMIT_CN)) {
struct vlan_pcpu_stats *stats;
- stats = this_cpu_ptr(vlan_dev_priv(dev)->vlan_pcpu_stats);
+ stats = this_cpu_ptr(vlan->vlan_pcpu_stats);
u64_stats_update_begin(&stats->syncp);
stats->tx_packets++;
stats->tx_bytes += len;
u64_stats_update_end(&stats->syncp);
} else {
- this_cpu_inc(vlan_dev_priv(dev)->vlan_pcpu_stats->tx_dropped);
+ this_cpu_inc(vlan->vlan_pcpu_stats->tx_dropped);
}
return ret;
@@ -669,25 +682,26 @@
return;
}
-static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo)
+static int vlan_dev_netpoll_setup(struct net_device *dev, struct netpoll_info *npinfo,
+ gfp_t gfp)
{
- struct vlan_dev_priv *info = vlan_dev_priv(dev);
- struct net_device *real_dev = info->real_dev;
+ struct vlan_dev_priv *vlan = vlan_dev_priv(dev);
+ struct net_device *real_dev = vlan->real_dev;
struct netpoll *netpoll;
int err = 0;
- netpoll = kzalloc(sizeof(*netpoll), GFP_KERNEL);
+ netpoll = kzalloc(sizeof(*netpoll), gfp);
err = -ENOMEM;
if (!netpoll)
goto out;
- err = __netpoll_setup(netpoll, real_dev);
+ err = __netpoll_setup(netpoll, real_dev, gfp);
if (err) {
kfree(netpoll);
goto out;
}
- info->netpoll = netpoll;
+ vlan->netpoll = netpoll;
out:
return err;
@@ -695,19 +709,15 @@
static void vlan_dev_netpoll_cleanup(struct net_device *dev)
{
- struct vlan_dev_priv *info = vlan_dev_priv(dev);
- struct netpoll *netpoll = info->netpoll;
+ struct vlan_dev_priv *vlan= vlan_dev_priv(dev);
+ struct netpoll *netpoll = vlan->netpoll;
if (!netpoll)
return;
- info->netpoll = NULL;
+ vlan->netpoll = NULL;
- /* Wait for transmitting packets to finish before freeing. */
- synchronize_rcu_bh();
-
- __netpoll_cleanup(netpoll);
- kfree(netpoll);
+ __netpoll_free_rcu(netpoll);
}
#endif /* CONFIG_NET_POLL_CONTROLLER */
diff --git a/net/atm/common.c b/net/atm/common.c
index b4b44db..0c0ad93 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -812,6 +812,7 @@
if (!vcc->dev || !test_bit(ATM_VF_ADDR, &vcc->flags))
return -ENOTCONN;
+ memset(&pvc, 0, sizeof(pvc));
pvc.sap_family = AF_ATMPVC;
pvc.sap_addr.itf = vcc->dev->number;
pvc.sap_addr.vpi = vcc->vpi;
diff --git a/net/atm/pvc.c b/net/atm/pvc.c
index 3a73491..ae03240 100644
--- a/net/atm/pvc.c
+++ b/net/atm/pvc.c
@@ -95,6 +95,7 @@
return -ENOTCONN;
*sockaddr_len = sizeof(struct sockaddr_atmpvc);
addr = (struct sockaddr_atmpvc *)sockaddr;
+ memset(addr, 0, sizeof(*addr));
addr->sap_family = AF_ATMPVC;
addr->sap_addr.itf = vcc->dev->number;
addr->sap_addr.vpi = vcc->vpi;
diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c
index b421cc4..fc866f2 100644
--- a/net/batman-adv/gateway_client.c
+++ b/net/batman-adv/gateway_client.c
@@ -200,11 +200,11 @@
if (atomic_read(&bat_priv->gw_mode) != BATADV_GW_MODE_CLIENT)
goto out;
- if (!batadv_atomic_dec_not_zero(&bat_priv->gw_reselect))
- goto out;
-
curr_gw = batadv_gw_get_selected_gw_node(bat_priv);
+ if (!batadv_atomic_dec_not_zero(&bat_priv->gw_reselect) && curr_gw)
+ goto out;
+
next_gw = batadv_gw_get_best_gw_node(bat_priv);
if (curr_gw == next_gw)
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index a438f4b..99dd8f7 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -197,6 +197,7 @@
del:
list_del(&entry->list);
kfree(entry);
+ kfree(tt_change_node);
event_removed = true;
goto unlock;
}
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 41ff978..715d7e3 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -1365,6 +1365,9 @@
return false;
e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_NEEDED);
+ if (!e)
+ return false;
+
if (hci_resolve_name(hdev, e) == 0) {
e->name_state = NAME_PENDING;
return true;
@@ -1393,12 +1396,20 @@
return;
e = hci_inquiry_cache_lookup_resolve(hdev, bdaddr, NAME_PENDING);
- if (e) {
+ /* If the device was not found in a list of found devices names of which
+ * are pending. there is no need to continue resolving a next name as it
+ * will be done upon receiving another Remote Name Request Complete
+ * Event */
+ if (!e)
+ return;
+
+ list_del(&e->list);
+ if (name) {
e->name_state = NAME_KNOWN;
- list_del(&e->list);
- if (name)
- mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
- e->data.rssi, name, name_len);
+ mgmt_remote_name(hdev, bdaddr, ACL_LINK, 0x00,
+ e->data.rssi, name, name_len);
+ } else {
+ e->name_state = NAME_NOT_KNOWN;
}
if (hci_resolve_next_name(hdev))
@@ -1762,7 +1773,12 @@
if (conn->type == ACL_LINK) {
conn->state = BT_CONFIG;
hci_conn_hold(conn);
- conn->disc_timeout = HCI_DISCONN_TIMEOUT;
+
+ if (!conn->out && !hci_conn_ssp_enabled(conn) &&
+ !hci_find_link_key(hdev, &ev->bdaddr))
+ conn->disc_timeout = HCI_PAIRING_TIMEOUT;
+ else
+ conn->disc_timeout = HCI_DISCONN_TIMEOUT;
} else
conn->state = BT_CONNECTED;
diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c
index a7f04de..19fdac7 100644
--- a/net/bluetooth/hci_sock.c
+++ b/net/bluetooth/hci_sock.c
@@ -694,6 +694,7 @@
*addr_len = sizeof(*haddr);
haddr->hci_family = AF_BLUETOOTH;
haddr->hci_dev = hdev->id;
+ haddr->hci_channel= 0;
release_sock(sk);
return 0;
@@ -1009,6 +1010,7 @@
{
struct hci_filter *f = &hci_pi(sk)->filter;
+ memset(&uf, 0, sizeof(uf));
uf.type_mask = f->type_mask;
uf.opcode = f->opcode;
uf.event_mask[0] = *((u32 *) f->event_mask + 0);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index a8964db..daa149b 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1181,6 +1181,7 @@
sk = chan->sk;
hci_conn_hold(conn->hcon);
+ conn->hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
bacpy(&bt_sk(sk)->src, conn->src);
bacpy(&bt_sk(sk)->dst, conn->dst);
diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c
index a4bb27e..1497edd 100644
--- a/net/bluetooth/l2cap_sock.c
+++ b/net/bluetooth/l2cap_sock.c
@@ -245,6 +245,7 @@
BT_DBG("sock %p, sk %p", sock, sk);
+ memset(la, 0, sizeof(struct sockaddr_l2));
addr->sa_family = AF_BLUETOOTH;
*len = sizeof(struct sockaddr_l2);
@@ -1174,7 +1175,7 @@
chan = l2cap_chan_create();
if (!chan) {
- l2cap_sock_kill(sk);
+ sk_free(sk);
return NULL;
}
diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
index 7e1e596..1a17850 100644
--- a/net/bluetooth/rfcomm/sock.c
+++ b/net/bluetooth/rfcomm/sock.c
@@ -528,6 +528,7 @@
BT_DBG("sock %p, sk %p", sock, sk);
+ memset(sa, 0, sizeof(*sa));
sa->rc_family = AF_BLUETOOTH;
sa->rc_channel = rfcomm_pi(sk)->channel;
if (peer)
@@ -822,6 +823,7 @@
}
sec.level = rfcomm_pi(sk)->sec_level;
+ sec.key_size = 0;
len = min_t(unsigned int, len, sizeof(sec));
if (copy_to_user(optval, (char *) &sec, len))
diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c
index cb96077..56f1823 100644
--- a/net/bluetooth/rfcomm/tty.c
+++ b/net/bluetooth/rfcomm/tty.c
@@ -456,7 +456,7 @@
size = sizeof(*dl) + dev_num * sizeof(*di);
- dl = kmalloc(size, GFP_KERNEL);
+ dl = kzalloc(size, GFP_KERNEL);
if (!dl)
return -ENOMEM;
diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
index 40bbe25..3589e21 100644
--- a/net/bluetooth/sco.c
+++ b/net/bluetooth/sco.c
@@ -131,6 +131,15 @@
sco_sock_clear_timer(sk);
sco_chan_del(sk, err);
bh_unlock_sock(sk);
+
+ sco_conn_lock(conn);
+ conn->sk = NULL;
+ sco_pi(sk)->conn = NULL;
+ sco_conn_unlock(conn);
+
+ if (conn->hcon)
+ hci_conn_put(conn->hcon);
+
sco_sock_kill(sk);
}
@@ -821,16 +830,6 @@
BT_DBG("sk %p, conn %p, err %d", sk, conn, err);
- if (conn) {
- sco_conn_lock(conn);
- conn->sk = NULL;
- sco_pi(sk)->conn = NULL;
- sco_conn_unlock(conn);
-
- if (conn->hcon)
- hci_conn_put(conn->hcon);
- }
-
sk->sk_state = BT_CLOSED;
sk->sk_err = err;
sk->sk_state_change(sk);
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index 16ef0dc..901a616 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -579,8 +579,11 @@
if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags))
smp = smp_chan_create(conn);
+ else
+ smp = conn->smp_chan;
- smp = conn->smp_chan;
+ if (!smp)
+ return SMP_UNSPECIFIED;
smp->preq[0] = SMP_CMD_PAIRING_REQ;
memcpy(&smp->preq[1], req, sizeof(*req));
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c
index 3334845..070e8a6 100644
--- a/net/bridge/br_device.c
+++ b/net/bridge/br_device.c
@@ -31,9 +31,11 @@
struct net_bridge_mdb_entry *mdst;
struct br_cpu_netstats *brstats = this_cpu_ptr(br->stats);
+ rcu_read_lock();
#ifdef CONFIG_BRIDGE_NETFILTER
if (skb->nf_bridge && (skb->nf_bridge->mask & BRNF_BRIDGED_DNAT)) {
br_nf_pre_routing_finish_bridge_slow(skb);
+ rcu_read_unlock();
return NETDEV_TX_OK;
}
#endif
@@ -48,7 +50,6 @@
skb_reset_mac_header(skb);
skb_pull(skb, ETH_HLEN);
- rcu_read_lock();
if (is_broadcast_ether_addr(dest))
br_flood_deliver(br, skb);
else if (is_multicast_ether_addr(dest)) {
@@ -206,24 +207,23 @@
static void br_netpoll_cleanup(struct net_device *dev)
{
struct net_bridge *br = netdev_priv(dev);
- struct net_bridge_port *p, *n;
+ struct net_bridge_port *p;
- list_for_each_entry_safe(p, n, &br->port_list, list) {
+ list_for_each_entry(p, &br->port_list, list)
br_netpoll_disable(p);
- }
}
-static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni)
+static int br_netpoll_setup(struct net_device *dev, struct netpoll_info *ni,
+ gfp_t gfp)
{
struct net_bridge *br = netdev_priv(dev);
- struct net_bridge_port *p, *n;
+ struct net_bridge_port *p;
int err = 0;
- list_for_each_entry_safe(p, n, &br->port_list, list) {
+ list_for_each_entry(p, &br->port_list, list) {
if (!p->dev)
continue;
-
- err = br_netpoll_enable(p);
+ err = br_netpoll_enable(p, gfp);
if (err)
goto fail;
}
@@ -236,17 +236,17 @@
goto out;
}
-int br_netpoll_enable(struct net_bridge_port *p)
+int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
{
struct netpoll *np;
int err = 0;
- np = kzalloc(sizeof(*p->np), GFP_KERNEL);
+ np = kzalloc(sizeof(*p->np), gfp);
err = -ENOMEM;
if (!np)
goto out;
- err = __netpoll_setup(np, p->dev);
+ err = __netpoll_setup(np, p->dev, gfp);
if (err) {
kfree(np);
goto out;
@@ -267,11 +267,7 @@
p->np = NULL;
- /* Wait for transmitting packets to finish before freeing. */
- synchronize_rcu_bh();
-
- __netpoll_cleanup(np);
- kfree(np);
+ __netpoll_free_rcu(np);
}
#endif
diff --git a/net/bridge/br_forward.c b/net/bridge/br_forward.c
index e9466d4..02015a5 100644
--- a/net/bridge/br_forward.c
+++ b/net/bridge/br_forward.c
@@ -65,7 +65,7 @@
{
skb->dev = to->dev;
- if (unlikely(netpoll_tx_running(to->dev))) {
+ if (unlikely(netpoll_tx_running(to->br->dev))) {
if (packet_length(skb) > skb->dev->mtu && !skb_is_gso(skb))
kfree_skb(skb);
else {
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c
index e1144e1..1c8fdc3 100644
--- a/net/bridge/br_if.c
+++ b/net/bridge/br_if.c
@@ -361,7 +361,7 @@
if (err)
goto err2;
- if (br_netpoll_info(br) && ((err = br_netpoll_enable(p))))
+ if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL))))
goto err3;
err = netdev_set_master(dev, br->dev);
@@ -427,6 +427,10 @@
if (!p || p->br != br)
return -EINVAL;
+ /* Since more than one interface can be attached to a bridge,
+ * there still maybe an alternate path for netconsole to use;
+ * therefore there is no reason for a NETDEV_RELEASE event.
+ */
del_nbp(p);
spin_lock_bh(&br->lock);
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h
index a768b24..f507d2a 100644
--- a/net/bridge/br_private.h
+++ b/net/bridge/br_private.h
@@ -316,7 +316,7 @@
netpoll_send_skb(np, skb);
}
-extern int br_netpoll_enable(struct net_bridge_port *p);
+extern int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp);
extern void br_netpoll_disable(struct net_bridge_port *p);
#else
static inline struct netpoll_info *br_netpoll_info(struct net_bridge *br)
@@ -329,7 +329,7 @@
{
}
-static inline int br_netpoll_enable(struct net_bridge_port *p)
+static inline int br_netpoll_enable(struct net_bridge_port *p, gfp_t gfp)
{
return 0;
}
diff --git a/net/caif/chnl_net.c b/net/caif/chnl_net.c
index 69771c0..e597733 100644
--- a/net/caif/chnl_net.c
+++ b/net/caif/chnl_net.c
@@ -94,6 +94,10 @@
/* check the version of IP */
ip_version = skb_header_pointer(skb, 0, 1, &buf);
+ if (!ip_version) {
+ kfree_skb(skb);
+ return -EINVAL;
+ }
switch (*ip_version >> 4) {
case 4:
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index 69e38db..a802029 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -84,7 +84,6 @@
return -1;
}
} else {
- pr_info("client%lld fsid %pU\n", ceph_client_id(client), fsid);
memcpy(&client->fsid, fsid, sizeof(*fsid));
}
return 0;
diff --git a/net/ceph/debugfs.c b/net/ceph/debugfs.c
index 54b531a..38b5dc1 100644
--- a/net/ceph/debugfs.c
+++ b/net/ceph/debugfs.c
@@ -189,6 +189,9 @@
snprintf(name, sizeof(name), "%pU.client%lld", &client->fsid,
client->monc.auth->global_id);
+ dout("ceph_debugfs_client_init %p %s\n", client, name);
+
+ BUG_ON(client->debugfs_dir);
client->debugfs_dir = debugfs_create_dir(name, ceph_debugfs_dir);
if (!client->debugfs_dir)
goto out;
@@ -234,6 +237,7 @@
void ceph_debugfs_client_cleanup(struct ceph_client *client)
{
+ dout("ceph_debugfs_client_cleanup %p\n", client);
debugfs_remove(client->debugfs_osdmap);
debugfs_remove(client->debugfs_monmap);
debugfs_remove(client->osdc.debugfs_file);
diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c
index b979675..24c5eea 100644
--- a/net/ceph/messenger.c
+++ b/net/ceph/messenger.c
@@ -915,7 +915,6 @@
con->out_connect.authorizer_len = auth ?
cpu_to_le32(auth->authorizer_buf_len) : 0;
- con_out_kvec_reset(con);
con_out_kvec_add(con, sizeof (con->out_connect),
&con->out_connect);
if (auth && auth->authorizer_buf_len)
@@ -1557,6 +1556,7 @@
return -1;
}
con->auth_retry = 1;
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1577,6 +1577,7 @@
ENTITY_NAME(con->peer_name),
ceph_pr_addr(&con->peer_addr.in_addr));
reset_connection(con);
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1601,6 +1602,7 @@
le32_to_cpu(con->out_connect.connect_seq),
le32_to_cpu(con->in_reply.connect_seq));
con->connect_seq = le32_to_cpu(con->in_reply.connect_seq);
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -1617,6 +1619,7 @@
le32_to_cpu(con->in_reply.global_seq));
get_global_seq(con->msgr,
le32_to_cpu(con->in_reply.global_seq));
+ con_out_kvec_reset(con);
ret = prepare_write_connect(con);
if (ret < 0)
return ret;
@@ -2135,7 +2138,11 @@
BUG_ON(con->state != CON_STATE_CONNECTING);
con->state = CON_STATE_NEGOTIATING;
- /* Banner is good, exchange connection info */
+ /*
+ * Received banner is good, exchange connection info.
+ * Do not reset out_kvec, as sending our banner raced
+ * with receiving peer banner after connect completed.
+ */
ret = prepare_write_connect(con);
if (ret < 0)
goto out;
diff --git a/net/ceph/mon_client.c b/net/ceph/mon_client.c
index 105d533..900ea0f 100644
--- a/net/ceph/mon_client.c
+++ b/net/ceph/mon_client.c
@@ -311,6 +311,17 @@
EXPORT_SYMBOL(ceph_monc_open_session);
/*
+ * We require the fsid and global_id in order to initialize our
+ * debugfs dir.
+ */
+static bool have_debugfs_info(struct ceph_mon_client *monc)
+{
+ dout("have_debugfs_info fsid %d globalid %lld\n",
+ (int)monc->client->have_fsid, monc->auth->global_id);
+ return monc->client->have_fsid && monc->auth->global_id > 0;
+}
+
+/*
* The monitor responds with mount ack indicate mount success. The
* included client ticket allows the client to talk to MDSs and OSDs.
*/
@@ -320,9 +331,12 @@
struct ceph_client *client = monc->client;
struct ceph_monmap *monmap = NULL, *old = monc->monmap;
void *p, *end;
+ int had_debugfs_info, init_debugfs = 0;
mutex_lock(&monc->mutex);
+ had_debugfs_info = have_debugfs_info(monc);
+
dout("handle_monmap\n");
p = msg->front.iov_base;
end = p + msg->front.iov_len;
@@ -344,12 +358,22 @@
if (!client->have_fsid) {
client->have_fsid = true;
+ if (!had_debugfs_info && have_debugfs_info(monc)) {
+ pr_info("client%lld fsid %pU\n",
+ ceph_client_id(monc->client),
+ &monc->client->fsid);
+ init_debugfs = 1;
+ }
mutex_unlock(&monc->mutex);
- /*
- * do debugfs initialization without mutex to avoid
- * creating a locking dependency
- */
- ceph_debugfs_client_init(client);
+
+ if (init_debugfs) {
+ /*
+ * do debugfs initialization without mutex to avoid
+ * creating a locking dependency
+ */
+ ceph_debugfs_client_init(monc->client);
+ }
+
goto out_unlocked;
}
out:
@@ -865,8 +889,10 @@
{
int ret;
int was_auth = 0;
+ int had_debugfs_info, init_debugfs = 0;
mutex_lock(&monc->mutex);
+ had_debugfs_info = have_debugfs_info(monc);
if (monc->auth->ops)
was_auth = monc->auth->ops->is_authenticated(monc->auth);
monc->pending_auth = 0;
@@ -889,7 +915,22 @@
__send_subscribe(monc);
__resend_generic_request(monc);
}
+
+ if (!had_debugfs_info && have_debugfs_info(monc)) {
+ pr_info("client%lld fsid %pU\n",
+ ceph_client_id(monc->client),
+ &monc->client->fsid);
+ init_debugfs = 1;
+ }
mutex_unlock(&monc->mutex);
+
+ if (init_debugfs) {
+ /*
+ * do debugfs initialization without mutex to avoid
+ * creating a locking dependency
+ */
+ ceph_debugfs_client_init(monc->client);
+ }
}
static int __validate_auth(struct ceph_mon_client *monc)
diff --git a/net/core/dev.c b/net/core/dev.c
index 0cb3fe8..8398836 100644
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1055,6 +1055,8 @@
*/
int dev_set_alias(struct net_device *dev, const char *alias, size_t len)
{
+ char *new_ifalias;
+
ASSERT_RTNL();
if (len >= IFALIASZ)
@@ -1068,9 +1070,10 @@
return 0;
}
- dev->ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
- if (!dev->ifalias)
+ new_ifalias = krealloc(dev->ifalias, len + 1, GFP_KERNEL);
+ if (!new_ifalias)
return -ENOMEM;
+ dev->ifalias = new_ifalias;
strlcpy(dev->ifalias, alias, len+1);
return len;
@@ -1639,6 +1642,19 @@
return pt_prev->func(skb, skb->dev, pt_prev, orig_dev);
}
+static inline bool skb_loop_sk(struct packet_type *ptype, struct sk_buff *skb)
+{
+ if (ptype->af_packet_priv == NULL)
+ return false;
+
+ if (ptype->id_match)
+ return ptype->id_match(ptype, skb->sk);
+ else if ((struct sock *)ptype->af_packet_priv == skb->sk)
+ return true;
+
+ return false;
+}
+
/*
* Support routine. Sends outgoing frames to any network
* taps currently in use.
@@ -1656,8 +1672,7 @@
* they originated from - MvS (miquels@drinkel.ow.org)
*/
if ((ptype->dev == dev || !ptype->dev) &&
- (ptype->af_packet_priv == NULL ||
- (struct sock *)ptype->af_packet_priv != skb->sk)) {
+ (!skb_loop_sk(ptype, skb))) {
if (pt_prev) {
deliver_skb(skb2, pt_prev, skb->dev);
pt_prev = ptype;
@@ -2134,6 +2149,9 @@
__be16 protocol = skb->protocol;
netdev_features_t features = skb->dev->features;
+ if (skb_shinfo(skb)->gso_segs > skb->dev->gso_max_segs)
+ features &= ~NETIF_F_GSO_MASK;
+
if (protocol == htons(ETH_P_8021Q)) {
struct vlan_ethhdr *veh = (struct vlan_ethhdr *)skb->data;
protocol = veh->h_vlan_encapsulated_proto;
@@ -5726,6 +5744,7 @@
/**
* netdev_wait_allrefs - wait until all references are gone.
+ * @dev: target net_device
*
* This is called when unregistering network devices.
*
@@ -5986,6 +6005,7 @@
dev_net_set(dev, &init_net);
dev->gso_max_size = GSO_MAX_SIZE;
+ dev->gso_max_segs = GSO_MAX_SEGS;
INIT_LIST_HEAD(&dev->napi_list);
INIT_LIST_HEAD(&dev->unreg_list);
diff --git a/net/core/dst.c b/net/core/dst.c
index 069d51d..56d6361 100644
--- a/net/core/dst.c
+++ b/net/core/dst.c
@@ -149,7 +149,15 @@
}
EXPORT_SYMBOL(dst_discard);
-const u32 dst_default_metrics[RTAX_MAX];
+const u32 dst_default_metrics[RTAX_MAX + 1] = {
+ /* This initializer is needed to force linker to place this variable
+ * into const section. Otherwise it might end into bss section.
+ * We really want to avoid false sharing on this variable, and catch
+ * any writes on it.
+ */
+ [RTAX_MAX] = 0xdeadbeef,
+};
+
void *dst_alloc(struct dst_ops *ops, struct net_device *dev,
int initial_ref, int initial_obsolete, unsigned short flags)
diff --git a/net/core/netpoll.c b/net/core/netpoll.c
index b4c90e4..346b1eb 100644
--- a/net/core/netpoll.c
+++ b/net/core/netpoll.c
@@ -26,6 +26,7 @@
#include <linux/workqueue.h>
#include <linux/slab.h>
#include <linux/export.h>
+#include <linux/if_vlan.h>
#include <net/tcp.h>
#include <net/udp.h>
#include <asm/unaligned.h>
@@ -54,7 +55,7 @@
MAX_UDP_CHUNK)
static void zap_completion_queue(void);
-static void arp_reply(struct sk_buff *skb);
+static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo);
static unsigned int carrier_timeout = 4;
module_param(carrier_timeout, uint, 0644);
@@ -167,15 +168,24 @@
struct napi_struct *napi;
int budget = 16;
+ WARN_ON_ONCE(!irqs_disabled());
+
list_for_each_entry(napi, &dev->napi_list, dev_list) {
+ local_irq_enable();
if (napi->poll_owner != smp_processor_id() &&
spin_trylock(&napi->poll_lock)) {
- budget = poll_one_napi(dev->npinfo, napi, budget);
+ rcu_read_lock_bh();
+ budget = poll_one_napi(rcu_dereference_bh(dev->npinfo),
+ napi, budget);
+ rcu_read_unlock_bh();
spin_unlock(&napi->poll_lock);
- if (!budget)
+ if (!budget) {
+ local_irq_disable();
break;
+ }
}
+ local_irq_disable();
}
}
@@ -185,13 +195,14 @@
struct sk_buff *skb;
while ((skb = skb_dequeue(&npi->arp_tx)))
- arp_reply(skb);
+ netpoll_arp_reply(skb, npi);
}
}
static void netpoll_poll_dev(struct net_device *dev)
{
const struct net_device_ops *ops;
+ struct netpoll_info *ni = rcu_dereference_bh(dev->npinfo);
if (!dev || !netif_running(dev))
return;
@@ -206,17 +217,18 @@
poll_napi(dev);
if (dev->flags & IFF_SLAVE) {
- if (dev->npinfo) {
+ if (ni) {
struct net_device *bond_dev = dev->master;
struct sk_buff *skb;
- while ((skb = skb_dequeue(&dev->npinfo->arp_tx))) {
+ struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo);
+ while ((skb = skb_dequeue(&ni->arp_tx))) {
skb->dev = bond_dev;
- skb_queue_tail(&bond_dev->npinfo->arp_tx, skb);
+ skb_queue_tail(&bond_ni->arp_tx, skb);
}
}
}
- service_arp_queue(dev->npinfo);
+ service_arp_queue(ni);
zap_completion_queue();
}
@@ -302,6 +314,7 @@
return 0;
}
+/* call with IRQ disabled */
void netpoll_send_skb_on_dev(struct netpoll *np, struct sk_buff *skb,
struct net_device *dev)
{
@@ -309,8 +322,11 @@
unsigned long tries;
const struct net_device_ops *ops = dev->netdev_ops;
/* It is up to the caller to keep npinfo alive. */
- struct netpoll_info *npinfo = np->dev->npinfo;
+ struct netpoll_info *npinfo;
+ WARN_ON_ONCE(!irqs_disabled());
+
+ npinfo = rcu_dereference_bh(np->dev->npinfo);
if (!npinfo || !netif_running(dev) || !netif_device_present(dev)) {
__kfree_skb(skb);
return;
@@ -319,16 +335,22 @@
/* don't get messages out of order, and no recursion */
if (skb_queue_len(&npinfo->txq) == 0 && !netpoll_owner_active(dev)) {
struct netdev_queue *txq;
- unsigned long flags;
txq = netdev_get_tx_queue(dev, skb_get_queue_mapping(skb));
- local_irq_save(flags);
/* try until next clock tick */
for (tries = jiffies_to_usecs(1)/USEC_PER_POLL;
tries > 0; --tries) {
if (__netif_tx_trylock(txq)) {
if (!netif_xmit_stopped(txq)) {
+ if (vlan_tx_tag_present(skb) &&
+ !(netif_skb_features(skb) & NETIF_F_HW_VLAN_TX)) {
+ skb = __vlan_put_tag(skb, vlan_tx_tag_get(skb));
+ if (unlikely(!skb))
+ break;
+ skb->vlan_tci = 0;
+ }
+
status = ops->ndo_start_xmit(skb, dev);
if (status == NETDEV_TX_OK)
txq_trans_update(txq);
@@ -347,10 +369,9 @@
}
WARN_ONCE(!irqs_disabled(),
- "netpoll_send_skb(): %s enabled interrupts in poll (%pF)\n",
+ "netpoll_send_skb_on_dev(): %s enabled interrupts in poll (%pF)\n",
dev->name, ops->ndo_start_xmit);
- local_irq_restore(flags);
}
if (status != NETDEV_TX_OK) {
@@ -423,9 +444,8 @@
}
EXPORT_SYMBOL(netpoll_send_udp);
-static void arp_reply(struct sk_buff *skb)
+static void netpoll_arp_reply(struct sk_buff *skb, struct netpoll_info *npinfo)
{
- struct netpoll_info *npinfo = skb->dev->npinfo;
struct arphdr *arp;
unsigned char *arp_ptr;
int size, type = ARPOP_REPLY, ptype = ETH_P_ARP;
@@ -543,13 +563,12 @@
spin_unlock_irqrestore(&npinfo->rx_lock, flags);
}
-int __netpoll_rx(struct sk_buff *skb)
+int __netpoll_rx(struct sk_buff *skb, struct netpoll_info *npinfo)
{
int proto, len, ulen;
int hits = 0;
const struct iphdr *iph;
struct udphdr *uh;
- struct netpoll_info *npinfo = skb->dev->npinfo;
struct netpoll *np, *tmp;
if (list_empty(&npinfo->rx_np))
@@ -565,6 +584,12 @@
return 1;
}
+ if (skb->protocol == cpu_to_be16(ETH_P_8021Q)) {
+ skb = vlan_untag(skb);
+ if (unlikely(!skb))
+ goto out;
+ }
+
proto = ntohs(eth_hdr(skb)->h_proto);
if (proto != ETH_P_IP)
goto out;
@@ -715,7 +740,7 @@
}
EXPORT_SYMBOL(netpoll_parse_options);
-int __netpoll_setup(struct netpoll *np, struct net_device *ndev)
+int __netpoll_setup(struct netpoll *np, struct net_device *ndev, gfp_t gfp)
{
struct netpoll_info *npinfo;
const struct net_device_ops *ops;
@@ -734,7 +759,7 @@
}
if (!ndev->npinfo) {
- npinfo = kmalloc(sizeof(*npinfo), GFP_KERNEL);
+ npinfo = kmalloc(sizeof(*npinfo), gfp);
if (!npinfo) {
err = -ENOMEM;
goto out;
@@ -752,7 +777,7 @@
ops = np->dev->netdev_ops;
if (ops->ndo_netpoll_setup) {
- err = ops->ndo_netpoll_setup(ndev, npinfo);
+ err = ops->ndo_netpoll_setup(ndev, npinfo, gfp);
if (err)
goto free_npinfo;
}
@@ -857,7 +882,7 @@
refill_skbs();
rtnl_lock();
- err = __netpoll_setup(np, ndev);
+ err = __netpoll_setup(np, ndev, GFP_KERNEL);
rtnl_unlock();
if (err)
@@ -878,6 +903,24 @@
}
core_initcall(netpoll_init);
+static void rcu_cleanup_netpoll_info(struct rcu_head *rcu_head)
+{
+ struct netpoll_info *npinfo =
+ container_of(rcu_head, struct netpoll_info, rcu);
+
+ skb_queue_purge(&npinfo->arp_tx);
+ skb_queue_purge(&npinfo->txq);
+
+ /* we can't call cancel_delayed_work_sync here, as we are in softirq */
+ cancel_delayed_work(&npinfo->tx_work);
+
+ /* clean after last, unfinished work */
+ __skb_queue_purge(&npinfo->txq);
+ /* now cancel it again */
+ cancel_delayed_work(&npinfo->tx_work);
+ kfree(npinfo);
+}
+
void __netpoll_cleanup(struct netpoll *np)
{
struct netpoll_info *npinfo;
@@ -903,21 +946,25 @@
ops->ndo_netpoll_cleanup(np->dev);
RCU_INIT_POINTER(np->dev->npinfo, NULL);
-
- /* avoid racing with NAPI reading npinfo */
- synchronize_rcu_bh();
-
- skb_queue_purge(&npinfo->arp_tx);
- skb_queue_purge(&npinfo->txq);
- cancel_delayed_work_sync(&npinfo->tx_work);
-
- /* clean after last, unfinished work */
- __skb_queue_purge(&npinfo->txq);
- kfree(npinfo);
+ call_rcu_bh(&npinfo->rcu, rcu_cleanup_netpoll_info);
}
}
EXPORT_SYMBOL_GPL(__netpoll_cleanup);
+static void rcu_cleanup_netpoll(struct rcu_head *rcu_head)
+{
+ struct netpoll *np = container_of(rcu_head, struct netpoll, rcu);
+
+ __netpoll_cleanup(np);
+ kfree(np);
+}
+
+void __netpoll_free_rcu(struct netpoll *np)
+{
+ call_rcu_bh(&np->rcu, rcu_cleanup_netpoll);
+}
+EXPORT_SYMBOL_GPL(__netpoll_free_rcu);
+
void netpoll_cleanup(struct netpoll *np)
{
if (!np->dev)
diff --git a/net/core/netprio_cgroup.c b/net/core/netprio_cgroup.c
index ed0c043..c75e3f9 100644
--- a/net/core/netprio_cgroup.c
+++ b/net/core/netprio_cgroup.c
@@ -101,12 +101,10 @@
u32 max_len;
struct netprio_map *map;
- rtnl_lock();
max_len = atomic_read(&max_prioidx) + 1;
map = rtnl_dereference(dev->priomap);
if (!map || map->priomap_len < max_len)
ret = extend_netdev_table(dev, max_len);
- rtnl_unlock();
return ret;
}
@@ -256,17 +254,17 @@
if (!dev)
goto out_free_devname;
+ rtnl_lock();
ret = write_update_netdev_table(dev);
if (ret < 0)
goto out_put_dev;
- rcu_read_lock();
- map = rcu_dereference(dev->priomap);
+ map = rtnl_dereference(dev->priomap);
if (map)
map->priomap[prioidx] = priority;
- rcu_read_unlock();
out_put_dev:
+ rtnl_unlock();
dev_put(dev);
out_free_devname:
@@ -277,12 +275,6 @@
void net_prio_attach(struct cgroup *cgrp, struct cgroup_taskset *tset)
{
struct task_struct *p;
- char *tmp = kzalloc(sizeof(char) * PATH_MAX, GFP_KERNEL);
-
- if (!tmp) {
- pr_warn("Unable to attach cgrp due to alloc failure!\n");
- return;
- }
cgroup_taskset_for_each(p, cgrp, tset) {
unsigned int fd;
@@ -296,32 +288,24 @@
continue;
}
- rcu_read_lock();
+ spin_lock(&files->file_lock);
fdt = files_fdtable(files);
for (fd = 0; fd < fdt->max_fds; fd++) {
- char *path;
struct file *file;
struct socket *sock;
- unsigned long s;
- int rv, err = 0;
+ int err;
file = fcheck_files(files, fd);
if (!file)
continue;
- path = d_path(&file->f_path, tmp, PAGE_SIZE);
- rv = sscanf(path, "socket:[%lu]", &s);
- if (rv <= 0)
- continue;
-
sock = sock_from_file(file, &err);
- if (!err)
+ if (sock)
sock_update_netprioidx(sock->sk, p);
}
- rcu_read_unlock();
+ spin_unlock(&files->file_lock);
task_unlock(p);
}
- kfree(tmp);
}
static struct cftype ss_files[] = {
diff --git a/net/core/scm.c b/net/core/scm.c
index 8f6ccfd..040cebe 100644
--- a/net/core/scm.c
+++ b/net/core/scm.c
@@ -265,6 +265,7 @@
for (i=0, cmfptr=(__force int __user *)CMSG_DATA(cm); i<fdmax;
i++, cmfptr++)
{
+ struct socket *sock;
int new_fd;
err = security_file_receive(fp[i]);
if (err)
@@ -281,6 +282,9 @@
}
/* Bump the usage count and install the file. */
get_file(fp[i]);
+ sock = sock_from_file(fp[i], &err);
+ if (sock)
+ sock_update_netprioidx(sock->sk, current);
fd_install(new_fd, fp[i]);
}
diff --git a/net/core/sock.c b/net/core/sock.c
index 6b654b3..8f67ced 100644
--- a/net/core/sock.c
+++ b/net/core/sock.c
@@ -1458,6 +1458,7 @@
} else {
sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM;
sk->sk_gso_max_size = dst->dev->gso_max_size;
+ sk->sk_gso_max_segs = dst->dev->gso_max_segs;
}
}
}
diff --git a/net/dccp/ccid.h b/net/dccp/ccid.h
index 75c3582..fb85d37 100644
--- a/net/dccp/ccid.h
+++ b/net/dccp/ccid.h
@@ -246,7 +246,7 @@
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_ops->ccid_hc_rx_getsockopt != NULL)
+ if (ccid != NULL && ccid->ccid_ops->ccid_hc_rx_getsockopt != NULL)
rc = ccid->ccid_ops->ccid_hc_rx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
@@ -257,7 +257,7 @@
u32 __user *optval, int __user *optlen)
{
int rc = -ENOPROTOOPT;
- if (ccid->ccid_ops->ccid_hc_tx_getsockopt != NULL)
+ if (ccid != NULL && ccid->ccid_ops->ccid_hc_tx_getsockopt != NULL)
rc = ccid->ccid_ops->ccid_hc_tx_getsockopt(sk, optname, len,
optval, optlen);
return rc;
diff --git a/net/dccp/ccids/ccid3.c b/net/dccp/ccids/ccid3.c
index d65e987..119c043 100644
--- a/net/dccp/ccids/ccid3.c
+++ b/net/dccp/ccids/ccid3.c
@@ -535,6 +535,7 @@
case DCCP_SOCKOPT_CCID_TX_INFO:
if (len < sizeof(tfrc))
return -EINVAL;
+ memset(&tfrc, 0, sizeof(tfrc));
tfrc.tfrctx_x = hc->tx_x;
tfrc.tfrctx_x_recv = hc->tx_x_recv;
tfrc.tfrctx_x_calc = hc->tx_x_calc;
diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c
index f0cdb30..57bd978 100644
--- a/net/ipv4/fib_trie.c
+++ b/net/ipv4/fib_trie.c
@@ -367,7 +367,7 @@
static inline void free_leaf(struct leaf *l)
{
- call_rcu_bh(&l->rcu, __leaf_free_rcu);
+ call_rcu(&l->rcu, __leaf_free_rcu);
}
static inline void free_leaf_info(struct leaf_info *leaf)
diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c
index db0cf17..7f75f21 100644
--- a/net/ipv4/inet_connection_sock.c
+++ b/net/ipv4/inet_connection_sock.c
@@ -404,12 +404,15 @@
{
const struct inet_request_sock *ireq = inet_rsk(req);
struct inet_sock *newinet = inet_sk(newsk);
- struct ip_options_rcu *opt = ireq->opt;
+ struct ip_options_rcu *opt;
struct net *net = sock_net(sk);
struct flowi4 *fl4;
struct rtable *rt;
fl4 = &newinet->cork.fl.u.ip4;
+
+ rcu_read_lock();
+ opt = rcu_dereference(newinet->inet_opt);
flowi4_init_output(fl4, sk->sk_bound_dev_if, sk->sk_mark,
RT_CONN_FLAGS(sk), RT_SCOPE_UNIVERSE,
sk->sk_protocol, inet_sk_flowi_flags(sk),
@@ -421,11 +424,13 @@
goto no_route;
if (opt && opt->opt.is_strictroute && rt->rt_gateway)
goto route_err;
+ rcu_read_unlock();
return &rt->dst;
route_err:
ip_rt_put(rt);
no_route:
+ rcu_read_unlock();
IP_INC_STATS_BH(net, IPSTATS_MIB_OUTNOROUTES);
return NULL;
}
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
index ba39a52..c196d74 100644
--- a/net/ipv4/ip_output.c
+++ b/net/ipv4/ip_output.c
@@ -197,7 +197,7 @@
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
- if (neigh) {
+ if (!IS_ERR(neigh)) {
int res = dst_neigh_output(dst, neigh, skb);
rcu_read_unlock_bh();
@@ -1338,10 +1338,10 @@
iph->ihl = 5;
iph->tos = inet->tos;
iph->frag_off = df;
- ip_select_ident(iph, &rt->dst, sk);
iph->ttl = ttl;
iph->protocol = sk->sk_protocol;
ip_copy_addrs(iph, fl4);
+ ip_select_ident(iph, &rt->dst, sk);
if (opt) {
iph->ihl += opt->optlen>>2;
@@ -1366,9 +1366,8 @@
return skb;
}
-int ip_send_skb(struct sk_buff *skb)
+int ip_send_skb(struct net *net, struct sk_buff *skb)
{
- struct net *net = sock_net(skb->sk);
int err;
err = ip_local_out(skb);
@@ -1391,7 +1390,7 @@
return 0;
/* Netfilter gets whole the not fragmented skb. */
- return ip_send_skb(skb);
+ return ip_send_skb(sock_net(sk), skb);
}
/*
@@ -1536,6 +1535,7 @@
arg->csumoffset) = csum_fold(csum_add(nskb->csum,
arg->csum));
nskb->ip_summed = CHECKSUM_NONE;
+ skb_orphan(nskb);
skb_set_queue_mapping(nskb, skb_get_queue_mapping(skb));
ip_push_pending_frames(sk, &fl4);
}
diff --git a/net/ipv4/netfilter/nf_nat_sip.c b/net/ipv4/netfilter/nf_nat_sip.c
index ea4a2381..4ad9cf1 100644
--- a/net/ipv4/netfilter/nf_nat_sip.c
+++ b/net/ipv4/netfilter/nf_nat_sip.c
@@ -148,7 +148,7 @@
if (ct_sip_parse_header_uri(ct, *dptr, NULL, *datalen,
hdr, NULL, &matchoff, &matchlen,
&addr, &port) > 0) {
- unsigned int matchend, poff, plen, buflen, n;
+ unsigned int olen, matchend, poff, plen, buflen, n;
char buffer[sizeof("nnn.nnn.nnn.nnn:nnnnn")];
/* We're only interested in headers related to this
@@ -163,17 +163,18 @@
goto next;
}
+ olen = *datalen;
if (!map_addr(skb, dataoff, dptr, datalen, matchoff, matchlen,
&addr, port))
return NF_DROP;
- matchend = matchoff + matchlen;
+ matchend = matchoff + matchlen + *datalen - olen;
/* The maddr= parameter (RFC 2361) specifies where to send
* the reply. */
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
"maddr=", &poff, &plen,
- &addr) > 0 &&
+ &addr, true) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.src.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.dst.u3.ip) {
buflen = sprintf(buffer, "%pI4",
@@ -187,7 +188,7 @@
* from which the server received the request. */
if (ct_sip_parse_address_param(ct, *dptr, matchend, *datalen,
"received=", &poff, &plen,
- &addr) > 0 &&
+ &addr, false) > 0 &&
addr.ip == ct->tuplehash[dir].tuple.dst.u3.ip &&
addr.ip != ct->tuplehash[!dir].tuple.src.u3.ip) {
buflen = sprintf(buffer, "%pI4",
diff --git a/net/ipv4/route.c b/net/ipv4/route.c
index c035251..fd9ecb5 100644
--- a/net/ipv4/route.c
+++ b/net/ipv4/route.c
@@ -70,7 +70,6 @@
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mm.h>
-#include <linux/bootmem.h>
#include <linux/string.h>
#include <linux/socket.h>
#include <linux/sockios.h>
@@ -80,7 +79,6 @@
#include <linux/netdevice.h>
#include <linux/proc_fs.h>
#include <linux/init.h>
-#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/inetdevice.h>
#include <linux/igmp.h>
@@ -88,11 +86,9 @@
#include <linux/mroute.h>
#include <linux/netfilter_ipv4.h>
#include <linux/random.h>
-#include <linux/jhash.h>
#include <linux/rcupdate.h>
#include <linux/times.h>
#include <linux/slab.h>
-#include <linux/prefetch.h>
#include <net/dst.h>
#include <net/net_namespace.h>
#include <net/protocol.h>
@@ -2032,7 +2028,6 @@
}
dev_out = net->loopback_dev;
fl4->flowi4_oif = dev_out->ifindex;
- res.fi = NULL;
flags |= RTCF_LOCAL;
goto make_route;
}
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
index e7e6eea..2109ff4 100644
--- a/net/ipv4/tcp.c
+++ b/net/ipv4/tcp.c
@@ -811,7 +811,9 @@
old_size_goal + mss_now > xmit_size_goal)) {
xmit_size_goal = old_size_goal;
} else {
- tp->xmit_size_goal_segs = xmit_size_goal / mss_now;
+ tp->xmit_size_goal_segs =
+ min_t(u16, xmit_size_goal / mss_now,
+ sk->sk_gso_max_segs);
xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
}
}
diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c
index 4d4db16..1432cdb 100644
--- a/net/ipv4/tcp_cong.c
+++ b/net/ipv4/tcp_cong.c
@@ -291,7 +291,8 @@
left = tp->snd_cwnd - in_flight;
if (sk_can_gso(sk) &&
left * sysctl_tcp_tso_win_divisor < tp->snd_cwnd &&
- left * tp->mss_cache < sk->sk_gso_max_size)
+ left * tp->mss_cache < sk->sk_gso_max_size &&
+ left < sk->sk_gso_max_segs)
return true;
return left <= tcp_max_tso_deferred_mss(tp);
}
diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c
index 2fd2bc9..85308b9 100644
--- a/net/ipv4/tcp_input.c
+++ b/net/ipv4/tcp_input.c
@@ -5392,6 +5392,8 @@
{
struct tcp_sock *tp = tcp_sk(sk);
+ if (unlikely(sk->sk_rx_dst == NULL))
+ inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb);
/*
* Header prediction.
* The code loosely follows the one in the famous
@@ -5605,7 +5607,7 @@
tcp_set_state(sk, TCP_ESTABLISHED);
if (skb != NULL) {
- inet_sk_rx_dst_set(sk, skb);
+ icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);
security_inet_conn_established(sk, skb);
}
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
index 42b2a6a..00a748d 100644
--- a/net/ipv4/tcp_ipv4.c
+++ b/net/ipv4/tcp_ipv4.c
@@ -417,10 +417,12 @@
if (code == ICMP_FRAG_NEEDED) { /* PMTU discovery (RFC1191) */
tp->mtu_info = info;
- if (!sock_owned_by_user(sk))
+ if (!sock_owned_by_user(sk)) {
tcp_v4_mtu_reduced(sk);
- else
- set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags);
+ } else {
+ if (!test_and_set_bit(TCP_MTU_REDUCED_DEFERRED, &tp->tsq_flags))
+ sock_hold(sk);
+ }
goto out;
}
@@ -1462,6 +1464,7 @@
goto exit_nonewsk;
newsk->sk_gso_type = SKB_GSO_TCPV4;
+ inet_sk_rx_dst_set(newsk, skb);
newtp = tcp_sk(newsk);
newinet = inet_sk(newsk);
@@ -1627,9 +1630,6 @@
sk->sk_rx_dst = NULL;
}
}
- if (unlikely(sk->sk_rx_dst == NULL))
- inet_sk_rx_dst_set(sk, skb);
-
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len)) {
rsk = sk;
goto reset;
@@ -1872,10 +1872,21 @@
.twsk_destructor= tcp_twsk_destructor,
};
+void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+
+ dst_hold(dst);
+ sk->sk_rx_dst = dst;
+ inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+}
+EXPORT_SYMBOL(inet_sk_rx_dst_set);
+
const struct inet_connection_sock_af_ops ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
+ .sk_rx_dst_set = inet_sk_rx_dst_set,
.conn_request = tcp_v4_conn_request,
.syn_recv_sock = tcp_v4_syn_recv_sock,
.net_header_len = sizeof(struct iphdr),
diff --git a/net/ipv4/tcp_metrics.c b/net/ipv4/tcp_metrics.c
index 2288a63..0abe67b 100644
--- a/net/ipv4/tcp_metrics.c
+++ b/net/ipv4/tcp_metrics.c
@@ -731,6 +731,18 @@
static void __net_exit tcp_net_metrics_exit(struct net *net)
{
+ unsigned int i;
+
+ for (i = 0; i < (1U << net->ipv4.tcp_metrics_hash_log) ; i++) {
+ struct tcp_metrics_block *tm, *next;
+
+ tm = rcu_dereference_protected(net->ipv4.tcp_metrics_hash[i].chain, 1);
+ while (tm) {
+ next = rcu_dereference_protected(tm->tcpm_next, 1);
+ kfree(tm);
+ tm = next;
+ }
+ }
kfree(net->ipv4.tcp_metrics_hash);
}
diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c
index 232a90c..6ff7f10 100644
--- a/net/ipv4/tcp_minisocks.c
+++ b/net/ipv4/tcp_minisocks.c
@@ -387,8 +387,6 @@
struct tcp_sock *oldtp = tcp_sk(sk);
struct tcp_cookie_values *oldcvp = oldtp->cookie_values;
- inet_sk_rx_dst_set(newsk, skb);
-
/* TCP Cookie Transactions require space for the cookie pair,
* as it differs for each connection. There is no need to
* copy any s_data_payload stored at the original socket.
diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c
index 3f1bcff..d046326 100644
--- a/net/ipv4/tcp_output.c
+++ b/net/ipv4/tcp_output.c
@@ -910,14 +910,18 @@
if (flags & (1UL << TCP_TSQ_DEFERRED))
tcp_tsq_handler(sk);
- if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED))
+ if (flags & (1UL << TCP_WRITE_TIMER_DEFERRED)) {
tcp_write_timer_handler(sk);
-
- if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED))
+ __sock_put(sk);
+ }
+ if (flags & (1UL << TCP_DELACK_TIMER_DEFERRED)) {
tcp_delack_timer_handler(sk);
-
- if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED))
+ __sock_put(sk);
+ }
+ if (flags & (1UL << TCP_MTU_REDUCED_DEFERRED)) {
sk->sk_prot->mtu_reduced(sk);
+ __sock_put(sk);
+ }
}
EXPORT_SYMBOL(tcp_release_cb);
@@ -940,7 +944,7 @@
* We cant xmit new skbs from this context, as we might already
* hold qdisc lock.
*/
-void tcp_wfree(struct sk_buff *skb)
+static void tcp_wfree(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct tcp_sock *tp = tcp_sk(sk);
@@ -1522,21 +1526,21 @@
* when we would be allowed to send the split-due-to-Nagle skb fully.
*/
static unsigned int tcp_mss_split_point(const struct sock *sk, const struct sk_buff *skb,
- unsigned int mss_now, unsigned int cwnd)
+ unsigned int mss_now, unsigned int max_segs)
{
const struct tcp_sock *tp = tcp_sk(sk);
- u32 needed, window, cwnd_len;
+ u32 needed, window, max_len;
window = tcp_wnd_end(tp) - TCP_SKB_CB(skb)->seq;
- cwnd_len = mss_now * cwnd;
+ max_len = mss_now * max_segs;
- if (likely(cwnd_len <= window && skb != tcp_write_queue_tail(sk)))
- return cwnd_len;
+ if (likely(max_len <= window && skb != tcp_write_queue_tail(sk)))
+ return max_len;
needed = min(skb->len, window);
- if (cwnd_len <= needed)
- return cwnd_len;
+ if (max_len <= needed)
+ return max_len;
return needed - needed % mss_now;
}
@@ -1765,7 +1769,8 @@
limit = min(send_win, cong_win);
/* If a full-sized TSO skb can be sent, do it. */
- if (limit >= sk->sk_gso_max_size)
+ if (limit >= min_t(unsigned int, sk->sk_gso_max_size,
+ sk->sk_gso_max_segs * tp->mss_cache))
goto send_now;
/* Middle in queue won't get any more data, full sendable already? */
@@ -1999,7 +2004,9 @@
limit = mss_now;
if (tso_segs > 1 && !tcp_urg_mode(tp))
limit = tcp_mss_split_point(sk, skb, mss_now,
- cwnd_quota);
+ min_t(unsigned int,
+ cwnd_quota,
+ sk->sk_gso_max_segs));
if (skb->len > limit &&
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c
index 6df36ad..b774a03 100644
--- a/net/ipv4/tcp_timer.c
+++ b/net/ipv4/tcp_timer.c
@@ -252,7 +252,8 @@
inet_csk(sk)->icsk_ack.blocked = 1;
NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_DELAYEDACKLOCKED);
/* deleguate our work to tcp_release_cb() */
- set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags);
+ if (!test_and_set_bit(TCP_DELACK_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
+ sock_hold(sk);
}
bh_unlock_sock(sk);
sock_put(sk);
@@ -481,7 +482,8 @@
tcp_write_timer_handler(sk);
} else {
/* deleguate our work to tcp_release_cb() */
- set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags);
+ if (!test_and_set_bit(TCP_WRITE_TIMER_DEFERRED, &tcp_sk(sk)->tsq_flags))
+ sock_hold(sk);
}
bh_unlock_sock(sk);
sock_put(sk);
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
index b4c3582..6f6d1ac 100644
--- a/net/ipv4/udp.c
+++ b/net/ipv4/udp.c
@@ -758,7 +758,7 @@
uh->check = CSUM_MANGLED_0;
send:
- err = ip_send_skb(skb);
+ err = ip_send_skb(sock_net(sk), skb);
if (err) {
if (err == -ENOBUFS && !inet->recverr) {
UDP_INC_STATS_USER(sock_net(sk),
diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 79181819..6bc85f7 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -494,8 +494,7 @@
struct net_device *dev;
struct inet6_dev *idev;
- rcu_read_lock();
- for_each_netdev_rcu(net, dev) {
+ for_each_netdev(net, dev) {
idev = __in6_dev_get(dev);
if (idev) {
int changed = (!idev->cnf.forwarding) ^ (!newf);
@@ -504,7 +503,6 @@
dev_forward_change(idev);
}
}
- rcu_read_unlock();
}
static int addrconf_fixup_forwarding(struct ctl_table *table, int *p, int newf)
diff --git a/net/ipv6/proc.c b/net/ipv6/proc.c
index da2e92d..745a320 100644
--- a/net/ipv6/proc.c
+++ b/net/ipv6/proc.c
@@ -307,10 +307,10 @@
goto proc_dev_snmp6_fail;
return 0;
+proc_dev_snmp6_fail:
+ proc_net_remove(net, "snmp6");
proc_snmp6_fail:
proc_net_remove(net, "sockstat6");
-proc_dev_snmp6_fail:
- proc_net_remove(net, "dev_snmp6");
return -ENOMEM;
}
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
index c66b90f..a3e60cc 100644
--- a/net/ipv6/tcp_ipv6.c
+++ b/net/ipv6/tcp_ipv6.c
@@ -94,6 +94,18 @@
}
#endif
+static void inet6_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
+{
+ struct dst_entry *dst = skb_dst(skb);
+ const struct rt6_info *rt = (const struct rt6_info *)dst;
+
+ dst_hold(dst);
+ sk->sk_rx_dst = dst;
+ inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
+ if (rt->rt6i_node)
+ inet6_sk(sk)->rx_dst_cookie = rt->rt6i_node->fn_sernum;
+}
+
static void tcp_v6_hash(struct sock *sk)
{
if (sk->sk_state != TCP_CLOSE) {
@@ -1270,6 +1282,7 @@
newsk->sk_gso_type = SKB_GSO_TCPV6;
__ip6_dst_store(newsk, dst, NULL, NULL);
+ inet6_sk_rx_dst_set(newsk, skb);
newtcp6sk = (struct tcp6_sock *)newsk;
inet_sk(newsk)->pinet6 = &newtcp6sk->inet6;
@@ -1447,7 +1460,17 @@
opt_skb = skb_clone(skb, sk_gfp_atomic(sk, GFP_ATOMIC));
if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+ struct dst_entry *dst = sk->sk_rx_dst;
+
sock_rps_save_rxhash(sk, skb);
+ if (dst) {
+ if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif ||
+ dst->ops->check(dst, np->rx_dst_cookie) == NULL) {
+ dst_release(dst);
+ sk->sk_rx_dst = NULL;
+ }
+ }
+
if (tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len))
goto reset;
if (opt_skb)
@@ -1705,9 +1728,9 @@
struct dst_entry *dst = sk->sk_rx_dst;
struct inet_sock *icsk = inet_sk(sk);
if (dst)
- dst = dst_check(dst, 0);
+ dst = dst_check(dst, inet6_sk(sk)->rx_dst_cookie);
if (dst &&
- icsk->rx_dst_ifindex == inet6_iif(skb))
+ icsk->rx_dst_ifindex == skb->skb_iif)
skb_dst_set_noref(skb, dst);
}
}
@@ -1723,6 +1746,7 @@
.queue_xmit = inet6_csk_xmit,
.send_check = tcp_v6_send_check,
.rebuild_header = inet6_sk_rebuild_header,
+ .sk_rx_dst_set = inet6_sk_rx_dst_set,
.conn_request = tcp_v6_conn_request,
.syn_recv_sock = tcp_v6_syn_recv_sock,
.net_header_len = sizeof(struct ipv6hdr),
@@ -1754,6 +1778,7 @@
.queue_xmit = ip_queue_xmit,
.send_check = tcp_v4_send_check,
.rebuild_header = inet_sk_rebuild_header,
+ .sk_rx_dst_set = inet_sk_rx_dst_set,
.conn_request = tcp_v6_conn_request,
.syn_recv_sock = tcp_v6_syn_recv_sock,
.net_header_len = sizeof(struct iphdr),
diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c
index ef39812..f8c4c08 100644
--- a/net/ipv6/xfrm6_policy.c
+++ b/net/ipv6/xfrm6_policy.c
@@ -73,6 +73,13 @@
return 0;
}
+static void xfrm6_init_dst(struct net *net, struct xfrm_dst *xdst)
+{
+ struct rt6_info *rt = (struct rt6_info *)xdst;
+
+ rt6_init_peer(rt, net->ipv6.peers);
+}
+
static int xfrm6_init_path(struct xfrm_dst *path, struct dst_entry *dst,
int nfheader_len)
{
@@ -286,6 +293,7 @@
.get_saddr = xfrm6_get_saddr,
.decode_session = _decode_session6,
.get_tos = xfrm6_get_tos,
+ .init_dst = xfrm6_init_dst,
.init_path = xfrm6_init_path,
.fill_dst = xfrm6_fill_dst,
.blackhole_route = ip6_blackhole_route,
diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c
index 35e1e4b..9275471 100644
--- a/net/l2tp/l2tp_ip6.c
+++ b/net/l2tp/l2tp_ip6.c
@@ -410,6 +410,7 @@
lsa->l2tp_family = AF_INET6;
lsa->l2tp_flowinfo = 0;
lsa->l2tp_scope_id = 0;
+ lsa->l2tp_unused = 0;
if (peer) {
if (!lsk->peer_conn_id)
return -ENOTCONN;
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index f6fe4d4..c219000 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -969,14 +969,13 @@
struct sockaddr_llc sllc;
struct sock *sk = sock->sk;
struct llc_sock *llc = llc_sk(sk);
- int rc = 0;
+ int rc = -EBADF;
memset(&sllc, 0, sizeof(sllc));
lock_sock(sk);
if (sock_flag(sk, SOCK_ZAPPED))
goto out;
*uaddrlen = sizeof(sllc);
- memset(uaddr, 0, *uaddrlen);
if (peer) {
rc = -ENOTCONN;
if (sk->sk_state != TCP_ESTABLISHED)
@@ -1206,7 +1205,7 @@
rc = llc_proc_init();
if (rc != 0) {
printk(llc_proc_err_msg);
- goto out_unregister_llc_proto;
+ goto out_station;
}
rc = llc_sysctl_init();
if (rc) {
@@ -1226,7 +1225,8 @@
llc_sysctl_exit();
out_proc:
llc_proc_exit();
-out_unregister_llc_proto:
+out_station:
+ llc_station_exit();
proto_unregister(&llc_proto);
goto out;
}
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index e32cab4..dd3e833 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -42,6 +42,7 @@
void llc_add_pack(int type, void (*handler)(struct llc_sap *sap,
struct sk_buff *skb))
{
+ smp_wmb(); /* ensure initialisation is complete before it's called */
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = handler;
}
@@ -50,11 +51,19 @@
{
if (type == LLC_DEST_SAP || type == LLC_DEST_CONN)
llc_type_handlers[type - 1] = NULL;
+ synchronize_net();
}
void llc_set_station_handler(void (*handler)(struct sk_buff *skb))
{
+ /* Ensure initialisation is complete before it's called */
+ if (handler)
+ smp_wmb();
+
llc_station_handler = handler;
+
+ if (!handler)
+ synchronize_net();
}
/**
@@ -150,6 +159,8 @@
int dest;
int (*rcv)(struct sk_buff *, struct net_device *,
struct packet_type *, struct net_device *);
+ void (*sta_handler)(struct sk_buff *skb);
+ void (*sap_handler)(struct llc_sap *sap, struct sk_buff *skb);
if (!net_eq(dev_net(dev), &init_net))
goto drop;
@@ -182,7 +193,8 @@
*/
rcv = rcu_dereference(sap->rcv_func);
dest = llc_pdu_type(skb);
- if (unlikely(!dest || !llc_type_handlers[dest - 1])) {
+ sap_handler = dest ? ACCESS_ONCE(llc_type_handlers[dest - 1]) : NULL;
+ if (unlikely(!sap_handler)) {
if (rcv)
rcv(skb, dev, pt, orig_dev);
else
@@ -193,7 +205,7 @@
if (cskb)
rcv(cskb, dev, pt, orig_dev);
}
- llc_type_handlers[dest - 1](sap, skb);
+ sap_handler(sap, skb);
}
llc_sap_put(sap);
out:
@@ -202,9 +214,10 @@
kfree_skb(skb);
goto out;
handle_station:
- if (!llc_station_handler)
+ sta_handler = ACCESS_ONCE(llc_station_handler);
+ if (!sta_handler)
goto drop;
- llc_station_handler(skb);
+ sta_handler(skb);
goto out;
}
diff --git a/net/llc/llc_station.c b/net/llc/llc_station.c
index 39a8d89..b2f2bac 100644
--- a/net/llc/llc_station.c
+++ b/net/llc/llc_station.c
@@ -268,7 +268,7 @@
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -293,7 +293,7 @@
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -322,7 +322,7 @@
out:
return rc;
free:
- kfree_skb(skb);
+ kfree_skb(nskb);
goto out;
}
@@ -687,12 +687,8 @@
llc_station_state_process(skb);
}
-int __init llc_station_init(void)
+void __init llc_station_init(void)
{
- int rc = -ENOBUFS;
- struct sk_buff *skb;
- struct llc_station_state_ev *ev;
-
skb_queue_head_init(&llc_main_station.mac_pdu_q);
skb_queue_head_init(&llc_main_station.ev_q.list);
spin_lock_init(&llc_main_station.ev_q.lock);
@@ -700,23 +696,12 @@
(unsigned long)&llc_main_station);
llc_main_station.ack_timer.expires = jiffies +
sysctl_llc_station_ack_timeout;
- skb = alloc_skb(0, GFP_ATOMIC);
- if (!skb)
- goto out;
- rc = 0;
- llc_set_station_handler(llc_station_rcv);
- ev = llc_station_ev(skb);
- memset(ev, 0, sizeof(*ev));
llc_main_station.maximum_retry = 1;
- llc_main_station.state = LLC_STATION_STATE_DOWN;
- ev->type = LLC_STATION_EV_TYPE_SIMPLE;
- ev->prim_type = LLC_STATION_EV_ENABLE_WITHOUT_DUP_ADDR_CHECK;
- rc = llc_station_next_state(skb);
-out:
- return rc;
+ llc_main_station.state = LLC_STATION_STATE_UP;
+ llc_set_station_handler(llc_station_rcv);
}
-void __exit llc_station_exit(void)
+void llc_station_exit(void)
{
llc_set_station_handler(NULL);
}
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 6fac18c..8557235 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -622,6 +622,7 @@
del_timer_sync(&sdata->u.mesh.housekeeping_timer);
del_timer_sync(&sdata->u.mesh.mesh_path_root_timer);
+ del_timer_sync(&sdata->u.mesh.mesh_path_timer);
/*
* If the timer fired while we waited for it, it will have
* requeued the work. Now the work will be running again
@@ -634,6 +635,8 @@
local->fif_other_bss--;
atomic_dec(&local->iff_allmultis);
ieee80211_configure_filter(local);
+
+ sdata->u.mesh.timers_running = 0;
}
static void ieee80211_mesh_rx_bcn_presp(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index cef0c9e..a4a5acd 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -1430,6 +1430,8 @@
del_timer_sync(&sdata->u.mgd.bcn_mon_timer);
del_timer_sync(&sdata->u.mgd.timer);
del_timer_sync(&sdata->u.mgd.chswitch_timer);
+
+ sdata->u.mgd.timers_running = 0;
}
void ieee80211_sta_rx_notify(struct ieee80211_sub_if_data *sdata,
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index bcaee5d..839dd97 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -299,7 +299,7 @@
if (local->scan_req != local->int_scan_req)
cfg80211_scan_done(local->scan_req, aborted);
local->scan_req = NULL;
- local->scan_sdata = NULL;
+ rcu_assign_pointer(local->scan_sdata, NULL);
local->scanning = 0;
local->scan_channel = NULL;
@@ -984,7 +984,6 @@
kfree(local->sched_scan_ies.ie[i]);
drv_sched_scan_stop(local, sdata);
- rcu_assign_pointer(local->sched_scan_sdata, NULL);
}
out:
mutex_unlock(&local->mtx);
diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c
index 84444dd..72bf32a 100644
--- a/net/netfilter/ipvs/ip_vs_ctl.c
+++ b/net/netfilter/ipvs/ip_vs_ctl.c
@@ -2759,6 +2759,7 @@
{
struct ip_vs_timeout_user t;
+ memset(&t, 0, sizeof(t));
__ip_vs_get_timeouts(net, &t);
if (copy_to_user(user, &t, sizeof(t)) != 0)
ret = -EFAULT;
diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c
index 45cf602..527651a 100644
--- a/net/netfilter/nf_conntrack_expect.c
+++ b/net/netfilter/nf_conntrack_expect.c
@@ -361,23 +361,6 @@
}
}
-static inline int refresh_timer(struct nf_conntrack_expect *i)
-{
- struct nf_conn_help *master_help = nfct_help(i->master);
- const struct nf_conntrack_expect_policy *p;
-
- if (!del_timer(&i->timeout))
- return 0;
-
- p = &rcu_dereference_protected(
- master_help->helper,
- lockdep_is_held(&nf_conntrack_lock)
- )->expect_policy[i->class];
- i->timeout.expires = jiffies + p->timeout * HZ;
- add_timer(&i->timeout);
- return 1;
-}
-
static inline int __nf_ct_expect_check(struct nf_conntrack_expect *expect)
{
const struct nf_conntrack_expect_policy *p;
@@ -386,7 +369,7 @@
struct nf_conn_help *master_help = nfct_help(master);
struct nf_conntrack_helper *helper;
struct net *net = nf_ct_exp_net(expect);
- struct hlist_node *n;
+ struct hlist_node *n, *next;
unsigned int h;
int ret = 1;
@@ -395,12 +378,12 @@
goto out;
}
h = nf_ct_expect_dst_hash(&expect->tuple);
- hlist_for_each_entry(i, n, &net->ct.expect_hash[h], hnode) {
+ hlist_for_each_entry_safe(i, n, next, &net->ct.expect_hash[h], hnode) {
if (expect_matches(i, expect)) {
- /* Refresh timer: if it's dying, ignore.. */
- if (refresh_timer(i)) {
- ret = 0;
- goto out;
+ if (del_timer(&i->timeout)) {
+ nf_ct_unlink_expect(i);
+ nf_ct_expect_put(i);
+ break;
}
} else if (expect_clash(i, expect)) {
ret = -EBUSY;
diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c
index 14f67a2..da4fc37 100644
--- a/net/netfilter/nf_conntrack_netlink.c
+++ b/net/netfilter/nf_conntrack_netlink.c
@@ -1896,10 +1896,15 @@
ctnetlink_nfqueue_parse(const struct nlattr *attr, struct nf_conn *ct)
{
struct nlattr *cda[CTA_MAX+1];
+ int ret;
nla_parse_nested(cda, CTA_MAX, attr, ct_nla_policy);
- return ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+ spin_lock_bh(&nf_conntrack_lock);
+ ret = ctnetlink_nfqueue_parse_ct((const struct nlattr **)cda, ct);
+ spin_unlock_bh(&nf_conntrack_lock);
+
+ return ret;
}
static struct nfq_ct_hook ctnetlink_nfqueue_hook = {
diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c
index 758a1ba..5c0a112 100644
--- a/net/netfilter/nf_conntrack_sip.c
+++ b/net/netfilter/nf_conntrack_sip.c
@@ -183,12 +183,12 @@
return len + digits_len(ct, dptr, limit, shift);
}
-static int parse_addr(const struct nf_conn *ct, const char *cp,
- const char **endp, union nf_inet_addr *addr,
- const char *limit)
+static int sip_parse_addr(const struct nf_conn *ct, const char *cp,
+ const char **endp, union nf_inet_addr *addr,
+ const char *limit, bool delim)
{
const char *end;
- int ret = 0;
+ int ret;
if (!ct)
return 0;
@@ -197,16 +197,28 @@
switch (nf_ct_l3num(ct)) {
case AF_INET:
ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
+ if (ret == 0)
+ return 0;
break;
case AF_INET6:
+ if (cp < limit && *cp == '[')
+ cp++;
+ else if (delim)
+ return 0;
+
ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
+ if (ret == 0)
+ return 0;
+
+ if (end < limit && *end == ']')
+ end++;
+ else if (delim)
+ return 0;
break;
default:
BUG();
}
- if (ret == 0 || end == cp)
- return 0;
if (endp)
*endp = end;
return 1;
@@ -219,7 +231,7 @@
union nf_inet_addr addr;
const char *aux = dptr;
- if (!parse_addr(ct, dptr, &dptr, &addr, limit)) {
+ if (!sip_parse_addr(ct, dptr, &dptr, &addr, limit, true)) {
pr_debug("ip: %s parse failed.!\n", dptr);
return 0;
}
@@ -296,7 +308,7 @@
return 0;
dptr += shift;
- if (!parse_addr(ct, dptr, &end, addr, limit))
+ if (!sip_parse_addr(ct, dptr, &end, addr, limit, true))
return -1;
if (end < limit && *end == ':') {
end++;
@@ -550,7 +562,7 @@
if (ret == 0)
return ret;
- if (!parse_addr(ct, dptr + *matchoff, &c, addr, limit))
+ if (!sip_parse_addr(ct, dptr + *matchoff, &c, addr, limit, true))
return -1;
if (*c == ':') {
c++;
@@ -599,7 +611,7 @@
unsigned int dataoff, unsigned int datalen,
const char *name,
unsigned int *matchoff, unsigned int *matchlen,
- union nf_inet_addr *addr)
+ union nf_inet_addr *addr, bool delim)
{
const char *limit = dptr + datalen;
const char *start, *end;
@@ -613,7 +625,7 @@
return 0;
start += strlen(name);
- if (!parse_addr(ct, start, &end, addr, limit))
+ if (!sip_parse_addr(ct, start, &end, addr, limit, delim))
return 0;
*matchoff = start - dptr;
*matchlen = end - start;
@@ -675,6 +687,47 @@
return 1;
}
+static int sdp_parse_addr(const struct nf_conn *ct, const char *cp,
+ const char **endp, union nf_inet_addr *addr,
+ const char *limit)
+{
+ const char *end;
+ int ret;
+
+ memset(addr, 0, sizeof(*addr));
+ switch (nf_ct_l3num(ct)) {
+ case AF_INET:
+ ret = in4_pton(cp, limit - cp, (u8 *)&addr->ip, -1, &end);
+ break;
+ case AF_INET6:
+ ret = in6_pton(cp, limit - cp, (u8 *)&addr->ip6, -1, &end);
+ break;
+ default:
+ BUG();
+ }
+
+ if (ret == 0)
+ return 0;
+ if (endp)
+ *endp = end;
+ return 1;
+}
+
+/* skip ip address. returns its length. */
+static int sdp_addr_len(const struct nf_conn *ct, const char *dptr,
+ const char *limit, int *shift)
+{
+ union nf_inet_addr addr;
+ const char *aux = dptr;
+
+ if (!sdp_parse_addr(ct, dptr, &dptr, &addr, limit)) {
+ pr_debug("ip: %s parse failed.!\n", dptr);
+ return 0;
+ }
+
+ return dptr - aux;
+}
+
/* SDP header parsing: a SDP session description contains an ordered set of
* headers, starting with a section containing general session parameters,
* optionally followed by multiple media descriptions.
@@ -686,10 +739,10 @@
*/
static const struct sip_header ct_sdp_hdrs[] = {
[SDP_HDR_VERSION] = SDP_HDR("v=", NULL, digits_len),
- [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", epaddr_len),
- [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", epaddr_len),
- [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", epaddr_len),
- [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", epaddr_len),
+ [SDP_HDR_OWNER_IP4] = SDP_HDR("o=", "IN IP4 ", sdp_addr_len),
+ [SDP_HDR_CONNECTION_IP4] = SDP_HDR("c=", "IN IP4 ", sdp_addr_len),
+ [SDP_HDR_OWNER_IP6] = SDP_HDR("o=", "IN IP6 ", sdp_addr_len),
+ [SDP_HDR_CONNECTION_IP6] = SDP_HDR("c=", "IN IP6 ", sdp_addr_len),
[SDP_HDR_MEDIA] = SDP_HDR("m=", NULL, media_len),
};
@@ -775,8 +828,8 @@
if (ret <= 0)
return ret;
- if (!parse_addr(ct, dptr + *matchoff, NULL, addr,
- dptr + *matchoff + *matchlen))
+ if (!sdp_parse_addr(ct, dptr + *matchoff, NULL, addr,
+ dptr + *matchoff + *matchlen))
return -1;
return 1;
}
@@ -1515,7 +1568,6 @@
}
static struct nf_conntrack_helper sip[MAX_PORTS][4] __read_mostly;
-static char sip_names[MAX_PORTS][4][sizeof("sip-65535")] __read_mostly;
static const struct nf_conntrack_expect_policy sip_exp_policy[SIP_EXPECT_MAX + 1] = {
[SIP_EXPECT_SIGNALLING] = {
@@ -1585,9 +1637,9 @@
sip[i][j].me = THIS_MODULE;
if (ports[i] == SIP_PORT)
- sprintf(sip_names[i][j], "sip");
+ sprintf(sip[i][j].name, "sip");
else
- sprintf(sip_names[i][j], "sip-%u", i);
+ sprintf(sip[i][j].name, "sip-%u", i);
pr_debug("port #%u: %u\n", i, ports[i]);
diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c
index 5463969..1445d73 100644
--- a/net/netlink/af_netlink.c
+++ b/net/netlink/af_netlink.c
@@ -1362,7 +1362,7 @@
if (NULL == siocb->scm)
siocb->scm = &scm;
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, true);
if (err < 0)
return err;
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index ceaca7c..aee7196 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -1079,7 +1079,7 @@
default:
WARN(1, "TPACKET version not supported\n");
BUG();
- return 0;
+ return NULL;
}
}
@@ -1273,6 +1273,14 @@
spin_unlock(&f->lock);
}
+bool match_fanout_group(struct packet_type *ptype, struct sock * sk)
+{
+ if (ptype->af_packet_priv == (void*)((struct packet_sock *)sk)->fanout)
+ return true;
+
+ return false;
+}
+
static int fanout_add(struct sock *sk, u16 id, u16 type_flags)
{
struct packet_sock *po = pkt_sk(sk);
@@ -1325,6 +1333,7 @@
match->prot_hook.dev = po->prot_hook.dev;
match->prot_hook.func = packet_rcv_fanout;
match->prot_hook.af_packet_priv = match;
+ match->prot_hook.id_match = match_fanout_group;
dev_add_pack(&match->prot_hook);
list_add(&match->list, &fanout_list);
}
@@ -1936,7 +1945,6 @@
if (likely(po->tx_ring.pg_vec)) {
ph = skb_shinfo(skb)->destructor_arg;
- BUG_ON(__packet_get_status(po, ph) != TP_STATUS_SENDING);
BUG_ON(atomic_read(&po->tx_ring.pending) == 0);
atomic_dec(&po->tx_ring.pending);
__packet_set_status(po, ph, TP_STATUS_AVAILABLE);
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index f10fb82..05d60859 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -67,6 +67,9 @@
struct tcf_common *pc;
int ret = 0;
int err;
+#ifdef CONFIG_GACT_PROB
+ struct tc_gact_p *p_parm = NULL;
+#endif
if (nla == NULL)
return -EINVAL;
@@ -82,6 +85,12 @@
#ifndef CONFIG_GACT_PROB
if (tb[TCA_GACT_PROB] != NULL)
return -EOPNOTSUPP;
+#else
+ if (tb[TCA_GACT_PROB]) {
+ p_parm = nla_data(tb[TCA_GACT_PROB]);
+ if (p_parm->ptype >= MAX_RAND)
+ return -EINVAL;
+ }
#endif
pc = tcf_hash_check(parm->index, a, bind, &gact_hash_info);
@@ -103,8 +112,7 @@
spin_lock_bh(&gact->tcf_lock);
gact->tcf_action = parm->action;
#ifdef CONFIG_GACT_PROB
- if (tb[TCA_GACT_PROB] != NULL) {
- struct tc_gact_p *p_parm = nla_data(tb[TCA_GACT_PROB]);
+ if (p_parm) {
gact->tcfg_paction = p_parm->paction;
gact->tcfg_pval = p_parm->pval;
gact->tcfg_ptype = p_parm->ptype;
@@ -133,7 +141,7 @@
spin_lock(&gact->tcf_lock);
#ifdef CONFIG_GACT_PROB
- if (gact->tcfg_ptype && gact_rand[gact->tcfg_ptype] != NULL)
+ if (gact->tcfg_ptype)
action = gact_rand[gact->tcfg_ptype](gact);
else
action = gact->tcf_action;
diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c
index 60e281a..58fb3c7 100644
--- a/net/sched/act_ipt.c
+++ b/net/sched/act_ipt.c
@@ -185,7 +185,12 @@
err2:
kfree(tname);
err1:
- kfree(pc);
+ if (ret == ACT_P_CREATED) {
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
+ }
return err;
}
diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c
index fe81cc1..9c0fd0c 100644
--- a/net/sched/act_mirred.c
+++ b/net/sched/act_mirred.c
@@ -200,13 +200,12 @@
out:
if (err) {
m->tcf_qstats.overlimits++;
- /* should we be asking for packet to be dropped?
- * may make sense for redirect case only
- */
- retval = TC_ACT_SHOT;
- } else {
+ if (m->tcfm_eaction != TCA_EGRESS_MIRROR)
+ retval = TC_ACT_SHOT;
+ else
+ retval = m->tcf_action;
+ } else
retval = m->tcf_action;
- }
spin_unlock(&m->tcf_lock);
return retval;
diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c
index 26aa2f6..45c53ab 100644
--- a/net/sched/act_pedit.c
+++ b/net/sched/act_pedit.c
@@ -74,7 +74,10 @@
p = to_pedit(pc);
keys = kmalloc(ksize, GFP_KERNEL);
if (keys == NULL) {
- kfree(pc);
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
return -ENOMEM;
}
ret = ACT_P_CREATED;
diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c
index 3922f2a..3714f60 100644
--- a/net/sched/act_simple.c
+++ b/net/sched/act_simple.c
@@ -131,7 +131,10 @@
d = to_defact(pc);
ret = alloc_defdata(d, defdata);
if (ret < 0) {
- kfree(pc);
+ if (est)
+ gen_kill_estimator(&pc->tcfc_bstats,
+ &pc->tcfc_rate_est);
+ kfree_rcu(pc, tcfc_rcu);
return ret;
}
d->tcf_action = parm->action;
diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c
index 9af01f3..e4723d3 100644
--- a/net/sched/sch_qfq.c
+++ b/net/sched/sch_qfq.c
@@ -203,6 +203,34 @@
return index;
}
+/* Length of the next packet (0 if the queue is empty). */
+static unsigned int qdisc_peek_len(struct Qdisc *sch)
+{
+ struct sk_buff *skb;
+
+ skb = sch->ops->peek(sch);
+ return skb ? qdisc_pkt_len(skb) : 0;
+}
+
+static void qfq_deactivate_class(struct qfq_sched *, struct qfq_class *);
+static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
+ unsigned int len);
+
+static void qfq_update_class_params(struct qfq_sched *q, struct qfq_class *cl,
+ u32 lmax, u32 inv_w, int delta_w)
+{
+ int i;
+
+ /* update qfq-specific data */
+ cl->lmax = lmax;
+ cl->inv_w = inv_w;
+ i = qfq_calc_index(cl->inv_w, cl->lmax);
+
+ cl->grp = &q->groups[i];
+
+ q->wsum += delta_w;
+}
+
static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid,
struct nlattr **tca, unsigned long *arg)
{
@@ -250,6 +278,8 @@
lmax = 1UL << QFQ_MTU_SHIFT;
if (cl != NULL) {
+ bool need_reactivation = false;
+
if (tca[TCA_RATE]) {
err = gen_replace_estimator(&cl->bstats, &cl->rate_est,
qdisc_root_sleeping_lock(sch),
@@ -258,12 +288,29 @@
return err;
}
- if (inv_w != cl->inv_w) {
- sch_tree_lock(sch);
- q->wsum += delta_w;
- cl->inv_w = inv_w;
- sch_tree_unlock(sch);
+ if (lmax == cl->lmax && inv_w == cl->inv_w)
+ return 0; /* nothing to update */
+
+ i = qfq_calc_index(inv_w, lmax);
+ sch_tree_lock(sch);
+ if (&q->groups[i] != cl->grp && cl->qdisc->q.qlen > 0) {
+ /*
+ * shift cl->F back, to not charge the
+ * class for the not-yet-served head
+ * packet
+ */
+ cl->F = cl->S;
+ /* remove class from its slot in the old group */
+ qfq_deactivate_class(q, cl);
+ need_reactivation = true;
}
+
+ qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
+
+ if (need_reactivation) /* activate in new group */
+ qfq_activate_class(q, cl, qdisc_peek_len(cl->qdisc));
+ sch_tree_unlock(sch);
+
return 0;
}
@@ -273,11 +320,8 @@
cl->refcnt = 1;
cl->common.classid = classid;
- cl->lmax = lmax;
- cl->inv_w = inv_w;
- i = qfq_calc_index(cl->inv_w, cl->lmax);
- cl->grp = &q->groups[i];
+ qfq_update_class_params(q, cl, lmax, inv_w, delta_w);
cl->qdisc = qdisc_create_dflt(sch->dev_queue,
&pfifo_qdisc_ops, classid);
@@ -294,7 +338,6 @@
return err;
}
}
- q->wsum += weight;
sch_tree_lock(sch);
qdisc_class_hash_insert(&q->clhash, &cl->common);
@@ -711,15 +754,6 @@
}
}
-/* What is length of next packet in queue (0 if queue is empty) */
-static unsigned int qdisc_peek_len(struct Qdisc *sch)
-{
- struct sk_buff *skb;
-
- skb = sch->ops->peek(sch);
- return skb ? qdisc_pkt_len(skb) : 0;
-}
-
/*
* Updates the class, returns true if also the group needs to be updated.
*/
@@ -843,11 +877,8 @@
static int qfq_enqueue(struct sk_buff *skb, struct Qdisc *sch)
{
struct qfq_sched *q = qdisc_priv(sch);
- struct qfq_group *grp;
struct qfq_class *cl;
int err;
- u64 roundedS;
- int s;
cl = qfq_classify(skb, sch, &err);
if (cl == NULL) {
@@ -876,11 +907,25 @@
return err;
/* If reach this point, queue q was idle */
- grp = cl->grp;
+ qfq_activate_class(q, cl, qdisc_pkt_len(skb));
+
+ return err;
+}
+
+/*
+ * Handle class switch from idle to backlogged.
+ */
+static void qfq_activate_class(struct qfq_sched *q, struct qfq_class *cl,
+ unsigned int pkt_len)
+{
+ struct qfq_group *grp = cl->grp;
+ u64 roundedS;
+ int s;
+
qfq_update_start(q, cl);
/* compute new finish time and rounded start. */
- cl->F = cl->S + (u64)qdisc_pkt_len(skb) * cl->inv_w;
+ cl->F = cl->S + (u64)pkt_len * cl->inv_w;
roundedS = qfq_round_down(cl->S, grp->slot_shift);
/*
@@ -917,8 +962,6 @@
skip_update:
qfq_slot_insert(grp, cl, roundedS);
-
- return err;
}
diff --git a/net/socket.c b/net/socket.c
index dfe5b66..a5471f8 100644
--- a/net/socket.c
+++ b/net/socket.c
@@ -2657,6 +2657,7 @@
if (copy_from_user(&ifc32, uifc32, sizeof(struct compat_ifconf)))
return -EFAULT;
+ memset(&ifc, 0, sizeof(ifc));
if (ifc32.ifcbuf == 0) {
ifc32.ifc_len = 0;
ifc.ifc_len = 0;
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index e4768c1..c5ee4ff 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1450,7 +1450,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
@@ -1619,7 +1619,7 @@
if (NULL == siocb->scm)
siocb->scm = &tmp_scm;
wait_for_unix_gc();
- err = scm_send(sock, msg, siocb->scm);
+ err = scm_send(sock, msg, siocb->scm, false);
if (err < 0)
return err;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 31b40cc..dcd64d5 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -952,6 +952,11 @@
*/
synchronize_rcu();
INIT_LIST_HEAD(&wdev->list);
+ /*
+ * Ensure that all events have been processed and
+ * freed.
+ */
+ cfg80211_process_wdev_events(wdev);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 5206c68..bc7430b 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -426,6 +426,7 @@
struct net_device *dev, enum nl80211_iftype ntype,
u32 *flags, struct vif_params *params);
void cfg80211_process_rdev_events(struct cfg80211_registered_device *rdev);
+void cfg80211_process_wdev_events(struct wireless_dev *wdev);
int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
struct wireless_dev *wdev,
diff --git a/net/wireless/reg.c b/net/wireless/reg.c
index 2303ee7..2ded3c7 100644
--- a/net/wireless/reg.c
+++ b/net/wireless/reg.c
@@ -680,6 +680,8 @@
channel_flags |= IEEE80211_CHAN_NO_IBSS;
if (rd_flags & NL80211_RRF_DFS)
channel_flags |= IEEE80211_CHAN_RADAR;
+ if (rd_flags & NL80211_RRF_NO_OFDM)
+ channel_flags |= IEEE80211_CHAN_NO_OFDM;
return channel_flags;
}
@@ -901,7 +903,21 @@
chan->max_antenna_gain = min(chan->orig_mag,
(int) MBI_TO_DBI(power_rule->max_antenna_gain));
chan->max_reg_power = (int) MBM_TO_DBM(power_rule->max_eirp);
- chan->max_power = min(chan->max_power, chan->max_reg_power);
+ if (chan->orig_mpwr) {
+ /*
+ * Devices that have their own custom regulatory domain
+ * but also use WIPHY_FLAG_STRICT_REGULATORY will follow the
+ * passed country IE power settings.
+ */
+ if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE &&
+ wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY &&
+ wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY)
+ chan->max_power = chan->max_reg_power;
+ else
+ chan->max_power = min(chan->orig_mpwr,
+ chan->max_reg_power);
+ } else
+ chan->max_power = chan->max_reg_power;
}
static void handle_band(struct wiphy *wiphy,
@@ -1885,6 +1901,7 @@
chan->flags = chan->orig_flags;
chan->max_antenna_gain = chan->orig_mag;
chan->max_power = chan->orig_mpwr;
+ chan->beacon_found = false;
}
}
}
diff --git a/net/wireless/util.c b/net/wireless/util.c
index 26f8cd3..994e2f0 100644
--- a/net/wireless/util.c
+++ b/net/wireless/util.c
@@ -735,7 +735,7 @@
wdev->connect_keys = NULL;
}
-static void cfg80211_process_wdev_events(struct wireless_dev *wdev)
+void cfg80211_process_wdev_events(struct wireless_dev *wdev)
{
struct cfg80211_event *ev;
unsigned long flags;
diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c
index c5a5165..5a2aa17 100644
--- a/net/xfrm/xfrm_policy.c
+++ b/net/xfrm/xfrm_policy.c
@@ -1357,6 +1357,8 @@
memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst));
xdst->flo.ops = &xfrm_bundle_fc_ops;
+ if (afinfo->init_dst)
+ afinfo->init_dst(net, xdst);
} else
xdst = ERR_PTR(-ENOBUFS);
diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c
index 5b228f9..87cd0e4 100644
--- a/net/xfrm/xfrm_state.c
+++ b/net/xfrm/xfrm_state.c
@@ -415,8 +415,17 @@
if (x->lft.hard_add_expires_seconds) {
long tmo = x->lft.hard_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
- goto expired;
+ if (tmo <= 0) {
+ if (x->xflags & XFRM_SOFT_EXPIRE) {
+ /* enter hard expire without soft expire first?!
+ * setting a new date could trigger this.
+ * workarbound: fix x->curflt.add_time by below:
+ */
+ x->curlft.add_time = now - x->saved_tmo - 1;
+ tmo = x->lft.hard_add_expires_seconds - x->saved_tmo;
+ } else
+ goto expired;
+ }
if (tmo < next)
next = tmo;
}
@@ -433,10 +442,14 @@
if (x->lft.soft_add_expires_seconds) {
long tmo = x->lft.soft_add_expires_seconds +
x->curlft.add_time - now;
- if (tmo <= 0)
+ if (tmo <= 0) {
warn = 1;
- else if (tmo < next)
+ x->xflags &= ~XFRM_SOFT_EXPIRE;
+ } else if (tmo < next) {
next = tmo;
+ x->xflags |= XFRM_SOFT_EXPIRE;
+ x->saved_tmo = tmo;
+ }
}
if (x->lft.soft_use_expires_seconds) {
long tmo = x->lft.soft_use_expires_seconds +
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 913d6bd..ca05ba2 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -3016,7 +3016,8 @@
$herectx .= raw_line($linenr, $n) . "\n";
}
- if (($stmts =~ tr/;/;/) == 1) {
+ if (($stmts =~ tr/;/;/) == 1 &&
+ $stmts !~ /^\s*(if|while|for|switch)\b/) {
WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
"Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
}
diff --git a/scripts/decodecode b/scripts/decodecode
index 18ba881..4f8248d 100755
--- a/scripts/decodecode
+++ b/scripts/decodecode
@@ -89,7 +89,7 @@
disas $T
cat $T.dis >> $T.aa
-faultline=`cat $T.dis | head -1 | cut -d":" -f2`
+faultline=`cat $T.dis | head -1 | cut -d":" -f2-`
faultline=`echo "$faultline" | sed -e 's/\[/\\\[/g; s/\]/\\\]/g'`
cat $T.oo | sed -e "s/\($faultline\)/\*\1 <-- trapping instruction/g"
diff --git a/scripts/kernel-doc b/scripts/kernel-doc
index 9b0c0b8..8fd107a 100755
--- a/scripts/kernel-doc
+++ b/scripts/kernel-doc
@@ -1786,6 +1786,7 @@
$prototype =~ s/__init +//;
$prototype =~ s/__init_or_module +//;
$prototype =~ s/__must_check +//;
+ $prototype =~ s/__weak +//;
$prototype =~ s/^#\s*define\s+//; #ak added
$prototype =~ s/__attribute__\s*\(\([a-z,]*\)\)//;
diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c
index 83554ee..0cc99a3 100644
--- a/security/yama/yama_lsm.c
+++ b/security/yama/yama_lsm.c
@@ -279,12 +279,46 @@
}
if (rc) {
- char name[sizeof(current->comm)];
printk_ratelimited(KERN_NOTICE
"ptrace of pid %d was attempted by: %s (pid %d)\n",
- child->pid,
- get_task_comm(name, current),
- current->pid);
+ child->pid, current->comm, current->pid);
+ }
+
+ return rc;
+}
+
+/**
+ * yama_ptrace_traceme - validate PTRACE_TRACEME calls
+ * @parent: task that will become the ptracer of the current task
+ *
+ * Returns 0 if following the ptrace is allowed, -ve on error.
+ */
+static int yama_ptrace_traceme(struct task_struct *parent)
+{
+ int rc;
+
+ /* If standard caps disallows it, so does Yama. We should
+ * only tighten restrictions further.
+ */
+ rc = cap_ptrace_traceme(parent);
+ if (rc)
+ return rc;
+
+ /* Only disallow PTRACE_TRACEME on more aggressive settings. */
+ switch (ptrace_scope) {
+ case YAMA_SCOPE_CAPABILITY:
+ if (!ns_capable(task_user_ns(parent), CAP_SYS_PTRACE))
+ rc = -EPERM;
+ break;
+ case YAMA_SCOPE_NO_ATTACH:
+ rc = -EPERM;
+ break;
+ }
+
+ if (rc) {
+ printk_ratelimited(KERN_NOTICE
+ "ptraceme of pid %d was attempted by: %s (pid %d)\n",
+ current->pid, parent->comm, parent->pid);
}
return rc;
@@ -294,6 +328,7 @@
.name = "yama",
.ptrace_access_check = yama_ptrace_access_check,
+ .ptrace_traceme = yama_ptrace_traceme,
.task_prctl = yama_task_prctl,
.task_free = yama_task_free,
};
diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c
index 0d7b25e..4e1fda7 100644
--- a/sound/arm/pxa2xx-ac97.c
+++ b/sound/arm/pxa2xx-ac97.c
@@ -106,7 +106,7 @@
.prepare = pxa2xx_ac97_pcm_prepare,
};
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int pxa2xx_ac97_do_suspend(struct snd_card *card)
{
@@ -243,7 +243,7 @@
.driver = {
.name = "pxa2xx-ac97",
.owner = THIS_MODULE,
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
.pm = &pxa2xx_ac97_pm_ops,
#endif
},
diff --git a/sound/atmel/abdac.c b/sound/atmel/abdac.c
index eb4ceb7..277ebce 100644
--- a/sound/atmel/abdac.c
+++ b/sound/atmel/abdac.c
@@ -452,6 +452,7 @@
dac->regs = ioremap(regs->start, resource_size(regs));
if (!dac->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto out_free_card;
}
@@ -534,7 +535,7 @@
return retval;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atmel_abdac_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c
index bf47025..9052aff 100644
--- a/sound/atmel/ac97c.c
+++ b/sound/atmel/ac97c.c
@@ -278,14 +278,9 @@
if (retval < 0)
return retval;
/* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (cpu_is_at32ap7000()) {
- if (retval < 0)
- return retval;
- /* snd_pcm_lib_malloc_pages returns 1 if buffer is changed. */
- if (retval == 1)
- if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
- dw_dma_cyclic_free(chip->dma.rx_chan);
- }
+ if (cpu_is_at32ap7000() && retval == 1)
+ if (test_and_clear_bit(DMA_RX_READY, &chip->flags))
+ dw_dma_cyclic_free(chip->dma.rx_chan);
/* Set restrictions to params. */
mutex_lock(&opened_mutex);
@@ -980,6 +975,7 @@
if (!chip->regs) {
dev_dbg(&pdev->dev, "could not remap register memory\n");
+ retval = -ENOMEM;
goto err_ioremap;
}
@@ -1134,7 +1130,7 @@
return retval;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int atmel_ac97c_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c
index 4e7ec2b..d0f0035 100644
--- a/sound/core/sgbuf.c
+++ b/sound/core/sgbuf.c
@@ -101,7 +101,7 @@
if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device,
chunk, &tmpb) < 0) {
if (!sgbuf->pages)
- return NULL;
+ goto _failed;
if (!res_size)
goto _failed;
size = sgbuf->pages * PAGE_SIZE;
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c
index 1128b35..5a34355 100644
--- a/sound/drivers/aloop.c
+++ b/sound/drivers/aloop.c
@@ -1176,7 +1176,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int loopback_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index f7d3bfc..54bb664 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -1064,7 +1064,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_dummy_suspend(struct device *pdev)
{
struct snd_card *card = dev_get_drvdata(pdev);
diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c
index 6ca59fc..ef17129 100644
--- a/sound/drivers/pcsp/pcsp.c
+++ b/sound/drivers/pcsp/pcsp.c
@@ -199,7 +199,7 @@
pcspkr_stop_sound();
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int pcsp_suspend(struct device *dev)
{
struct snd_pcsp *chip = dev_get_drvdata(dev);
@@ -212,7 +212,7 @@
#define PCSP_PM_OPS &pcsp_pm
#else
#define PCSP_PM_OPS NULL
-#endif /* CONFIG_PM */
+#endif /* CONFIG_PM_SLEEP */
static void pcsp_shutdown(struct platform_device *dev)
{
diff --git a/sound/isa/als100.c b/sound/isa/als100.c
index 2d67c78..f7cdaf5 100644
--- a/sound/isa/als100.c
+++ b/sound/isa/als100.c
@@ -233,7 +233,7 @@
irq[dev], dma8[dev], dma16[dev]);
}
- if ((error = snd_sb16dsp_pcm(chip, 0, NULL)) < 0) {
+ if ((error = snd_sb16dsp_pcm(chip, 0, &chip->pcm)) < 0) {
snd_card_free(card);
return error;
}
diff --git a/sound/oss/sb_audio.c b/sound/oss/sb_audio.c
index 733b014..b2b3c01 100644
--- a/sound/oss/sb_audio.c
+++ b/sound/oss/sb_audio.c
@@ -575,13 +575,15 @@
if (speed > 0)
{
int tmp;
- int s = speed * devc->channels;
+ int s;
if (speed < 5000)
speed = 5000;
if (speed > 44100)
speed = 44100;
+ s = speed * devc->channels;
+
devc->tconst = (256 - ((1000000 + s / 2) / s)) & 0xff;
tmp = 256 - devc->tconst;
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index f75f5ff..a71d1c1 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -94,7 +94,7 @@
if (snd_BUG_ON(codec_index != CS46XX_PRIMARY_CODEC_INDEX &&
codec_index != CS46XX_SECONDARY_CODEC_INDEX))
- return -EINVAL;
+ return 0xffff;
chip->active_ctrl(chip, 1);
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 8e40262..2f6e9c7 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1725,8 +1725,10 @@
atc_connect_resources(atc);
atc->timer = ct_timer_new(atc);
- if (!atc->timer)
+ if (!atc->timer) {
+ err = -ENOMEM;
goto error1;
+ }
err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, atc, &ops);
if (err < 0)
diff --git a/sound/pci/emu10k1/memory.c b/sound/pci/emu10k1/memory.c
index 4f502a2..0a43662 100644
--- a/sound/pci/emu10k1/memory.c
+++ b/sound/pci/emu10k1/memory.c
@@ -326,7 +326,10 @@
for (page = blk->first_page; page <= blk->last_page; page++, idx++) {
unsigned long ofs = idx << PAGE_SHIFT;
dma_addr_t addr;
- addr = snd_pcm_sgbuf_get_addr(substream, ofs);
+ if (ofs >= runtime->dma_bytes)
+ addr = emu->silent_page.addr;
+ else
+ addr = snd_pcm_sgbuf_get_addr(substream, ofs);
if (! is_valid_page(emu, addr)) {
printk(KERN_ERR "emu: failure page = %d\n", idx);
mutex_unlock(&hdr->block_mutex);
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index 647218d..4f7d2df 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -332,13 +332,12 @@
if (cfg->dig_outs)
snd_printd(" dig-out=0x%x/0x%x\n",
cfg->dig_out_pins[0], cfg->dig_out_pins[1]);
- snd_printd(" inputs:");
+ snd_printd(" inputs:\n");
for (i = 0; i < cfg->num_inputs; i++) {
- snd_printd(" %s=0x%x",
+ snd_printd(" %s=0x%x\n",
hda_get_autocfg_input_label(codec, cfg, i),
cfg->inputs[i].pin);
}
- snd_printd("\n");
if (cfg->dig_in_pin)
snd_printd(" dig-in=0x%x\n", cfg->dig_in_pin);
diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
index 0bc2315..0849aac 100644
--- a/sound/pci/hda/hda_beep.c
+++ b/sound/pci/hda/hda_beep.c
@@ -231,16 +231,22 @@
}
EXPORT_SYMBOL_HDA(snd_hda_detach_beep_device);
+static bool ctl_has_mute(struct snd_kcontrol *kcontrol)
+{
+ struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
+ return query_amp_caps(codec, get_amp_nid(kcontrol),
+ get_amp_direction(kcontrol)) & AC_AMPCAP_MUTE;
+}
+
/* get/put callbacks for beep mute mixer switches */
int snd_hda_mixer_amp_switch_get_beep(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
- if (beep) {
+ if (beep && (!beep->enabled || !ctl_has_mute(kcontrol))) {
ucontrol->value.integer.value[0] =
- ucontrol->value.integer.value[1] =
- beep->enabled;
+ ucontrol->value.integer.value[1] = beep->enabled;
return 0;
}
return snd_hda_mixer_amp_switch_get(kcontrol, ucontrol);
@@ -252,9 +258,20 @@
{
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_beep *beep = codec->beep;
- if (beep)
- snd_hda_enable_beep_device(codec,
- *ucontrol->value.integer.value);
+ if (beep) {
+ u8 chs = get_amp_channels(kcontrol);
+ int enable = 0;
+ long *valp = ucontrol->value.integer.value;
+ if (chs & 1) {
+ enable |= *valp;
+ valp++;
+ }
+ if (chs & 2)
+ enable |= *valp;
+ snd_hda_enable_beep_device(codec, enable);
+ }
+ if (!ctl_has_mute(kcontrol))
+ return 0;
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
}
EXPORT_SYMBOL_HDA(snd_hda_mixer_amp_switch_put_beep);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 88a9c20..f560051 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1386,6 +1386,44 @@
}
EXPORT_SYMBOL_HDA(snd_hda_codec_configure);
+/* update the stream-id if changed */
+static void update_pcm_stream_id(struct hda_codec *codec,
+ struct hda_cvt_setup *p, hda_nid_t nid,
+ u32 stream_tag, int channel_id)
+{
+ unsigned int oldval, newval;
+
+ if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
+ oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
+ newval = (stream_tag << 4) | channel_id;
+ if (oldval != newval)
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_CHANNEL_STREAMID,
+ newval);
+ p->stream_tag = stream_tag;
+ p->channel_id = channel_id;
+ }
+}
+
+/* update the format-id if changed */
+static void update_pcm_format(struct hda_codec *codec, struct hda_cvt_setup *p,
+ hda_nid_t nid, int format)
+{
+ unsigned int oldval;
+
+ if (p->format_id != format) {
+ oldval = snd_hda_codec_read(codec, nid, 0,
+ AC_VERB_GET_STREAM_FORMAT, 0);
+ if (oldval != format) {
+ msleep(1);
+ snd_hda_codec_write(codec, nid, 0,
+ AC_VERB_SET_STREAM_FORMAT,
+ format);
+ }
+ p->format_id = format;
+ }
+}
+
/**
* snd_hda_codec_setup_stream - set up the codec for streaming
* @codec: the CODEC to set up
@@ -1400,7 +1438,6 @@
{
struct hda_codec *c;
struct hda_cvt_setup *p;
- unsigned int oldval, newval;
int type;
int i;
@@ -1413,29 +1450,13 @@
p = get_hda_cvt_setup(codec, nid);
if (!p)
return;
- /* update the stream-id if changed */
- if (p->stream_tag != stream_tag || p->channel_id != channel_id) {
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval)
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- p->stream_tag = stream_tag;
- p->channel_id = channel_id;
- }
- /* update the format-id if changed */
- if (p->format_id != format) {
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT, 0);
- if (oldval != format) {
- msleep(1);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
- p->format_id = format;
- }
+
+ if (codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+ update_pcm_stream_id(codec, p, nid, stream_tag, channel_id);
+ if (!codec->pcm_format_first)
+ update_pcm_format(codec, p, nid, format);
+
p->active = 1;
p->dirty = 0;
@@ -3497,7 +3518,7 @@
{
int sup = snd_hda_param_read(codec, fg, AC_PAR_POWER_STATE);
- if (sup < 0)
+ if (sup == -1)
return false;
if (sup & power_state)
return true;
@@ -4433,6 +4454,8 @@
* then there is no need to go through power up here.
*/
if (codec->power_on) {
+ if (codec->power_transition < 0)
+ codec->power_transition = 0;
spin_unlock(&codec->power_lock);
return;
}
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index c422d33..7fbc1bc 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -861,6 +861,7 @@
unsigned int no_trigger_sense:1; /* don't trigger at pin-sensing */
unsigned int ignore_misc_bit:1; /* ignore MISC_NO_PRESENCE bit */
unsigned int no_jack_detect:1; /* Machine has no jack-detection */
+ unsigned int pcm_format_first:1; /* PCM format must be set first */
#ifdef CONFIG_SND_HDA_POWER_SAVE
unsigned int power_on :1; /* current (global) power-state */
int power_transition; /* power-state in transition */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c8aced1..60882c6 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -151,6 +151,7 @@
"{Intel, CPT},"
"{Intel, PPT},"
"{Intel, LPT},"
+ "{Intel, LPT_LP},"
"{Intel, HPT},"
"{Intel, PBG},"
"{Intel, SCH},"
@@ -3270,6 +3271,14 @@
{ PCI_DEVICE(0x8086, 0x8c20),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c20),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
+ /* Lynx Point-LP */
+ { PCI_DEVICE(0x8086, 0x9c21),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_SCH_SNOOP |
+ AZX_DCAPS_BUFSIZE | AZX_DCAPS_POSFIX_COMBO },
/* Haswell */
{ PCI_DEVICE(0x8086, 0x0c0c),
.driver_data = AZX_DRIVER_SCH | AZX_DCAPS_SCH_SNOOP |
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 7e46258..6894ec6 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -412,7 +412,7 @@
if (digi1 & AC_DIG1_EMPHASIS)
snd_iprintf(buffer, " Preemphasis");
if (digi1 & AC_DIG1_COPYRIGHT)
- snd_iprintf(buffer, " Copyright");
+ snd_iprintf(buffer, " Non-Copyright");
if (digi1 & AC_DIG1_NONAUDIO)
snd_iprintf(buffer, " Non-Audio");
if (digi1 & AC_DIG1_PROFESSIONAL)
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index d0d3540..49750a9 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -246,7 +246,7 @@
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_OUT_UNMUTE);
}
- if (dac)
+ if (dac && (get_wcaps(codec, dac) & AC_WCAP_OUT_AMP))
snd_hda_codec_write(codec, dac, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO);
}
@@ -261,7 +261,7 @@
AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
- if (adc)
+ if (adc && (get_wcaps(codec, adc) & AC_WCAP_IN_AMP))
snd_hda_codec_write(codec, adc, 0, AC_VERB_SET_AMP_GAIN_MUTE,
AMP_IN_UNMUTE(0));
}
@@ -275,6 +275,10 @@
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_MUTE_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_MUTE) == 0) {
+ snd_printdd("Skipping '%s %s Switch' (no mute on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
sprintf(namestr, "%s %s Switch", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
@@ -286,6 +290,10 @@
int type = dir ? HDA_INPUT : HDA_OUTPUT;
struct snd_kcontrol_new knew =
HDA_CODEC_VOLUME_MONO(namestr, nid, chan, 0, type);
+ if ((query_amp_caps(codec, nid, type) & AC_AMPCAP_NUM_STEPS) == 0) {
+ snd_printdd("Skipping '%s %s Volume' (no amp on node 0x%x)\n", pfx, dirstr[dir], nid);
+ return 0;
+ }
sprintf(namestr, "%s %s Volume", pfx, dirstr[dir]);
return snd_hda_ctl_add(codec, nid, snd_ctl_new1(&knew, codec));
}
@@ -464,50 +472,17 @@
}
/*
- * PCM stuffs
- */
-static void ca0132_setup_stream(struct hda_codec *codec, hda_nid_t nid,
- u32 stream_tag,
- int channel_id, int format)
-{
- unsigned int oldval, newval;
-
- if (!nid)
- return;
-
- snd_printdd("ca0132_setup_stream: "
- "NID=0x%x, stream=0x%x, channel=%d, format=0x%x\n",
- nid, stream_tag, channel_id, format);
-
- /* update the format-id if changed */
- oldval = snd_hda_codec_read(codec, nid, 0,
- AC_VERB_GET_STREAM_FORMAT,
- 0);
- if (oldval != format) {
- msleep(20);
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_STREAM_FORMAT,
- format);
- }
-
- oldval = snd_hda_codec_read(codec, nid, 0, AC_VERB_GET_CONV, 0);
- newval = (stream_tag << 4) | channel_id;
- if (oldval != newval) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_CHANNEL_STREAMID,
- newval);
- }
-}
-
-static void ca0132_cleanup_stream(struct hda_codec *codec, hda_nid_t nid)
-{
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_STREAM_FORMAT, 0);
- snd_hda_codec_write(codec, nid, 0, AC_VERB_SET_CHANNEL_STREAMID, 0);
-}
-
-/*
* PCM callbacks
*/
+static int ca0132_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
+ hinfo);
+}
+
static int ca0132_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -515,10 +490,8 @@
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dacs[0], stream_tag, 0, format);
-
- return 0;
+ return snd_hda_multi_out_analog_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
static int ca0132_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -526,15 +499,20 @@
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dacs[0]);
-
- return 0;
+ return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
}
/*
* Digital out
*/
+static int ca0132_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ struct ca0132_spec *spec = codec->spec;
+ return snd_hda_multi_out_dig_open(codec, &spec->multiout);
+}
+
static int ca0132_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
unsigned int stream_tag,
@@ -542,10 +520,8 @@
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dig_out, stream_tag, 0, format);
-
- return 0;
+ return snd_hda_multi_out_dig_prepare(codec, &spec->multiout,
+ stream_tag, format, substream);
}
static int ca0132_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -553,65 +529,15 @@
struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dig_out);
-
- return 0;
+ return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
}
-/*
- * Analog capture
- */
-static int ca0132_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
+static int ca0132_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
{
struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->adcs[substream->number],
- stream_tag, 0, format);
-
- return 0;
-}
-
-static int ca0132_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->adcs[substream->number]);
-
- return 0;
-}
-
-/*
- * Digital capture
- */
-static int ca0132_dig_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- unsigned int stream_tag,
- unsigned int format,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_setup_stream(codec, spec->dig_in, stream_tag, 0, format);
-
- return 0;
-}
-
-static int ca0132_dig_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
- struct hda_codec *codec,
- struct snd_pcm_substream *substream)
-{
- struct ca0132_spec *spec = codec->spec;
-
- ca0132_cleanup_stream(codec, spec->dig_in);
-
- return 0;
+ return snd_hda_multi_out_dig_close(codec, &spec->multiout);
}
/*
@@ -621,6 +547,7 @@
.channels_min = 2,
.channels_max = 2,
.ops = {
+ .open = ca0132_playback_pcm_open,
.prepare = ca0132_playback_pcm_prepare,
.cleanup = ca0132_playback_pcm_cleanup
},
@@ -630,10 +557,6 @@
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .ops = {
- .prepare = ca0132_capture_pcm_prepare,
- .cleanup = ca0132_capture_pcm_cleanup
- },
};
static struct hda_pcm_stream ca0132_pcm_digital_playback = {
@@ -641,6 +564,8 @@
.channels_min = 2,
.channels_max = 2,
.ops = {
+ .open = ca0132_dig_playback_pcm_open,
+ .close = ca0132_dig_playback_pcm_close,
.prepare = ca0132_dig_playback_pcm_prepare,
.cleanup = ca0132_dig_playback_pcm_cleanup
},
@@ -650,10 +575,6 @@
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
- .ops = {
- .prepare = ca0132_dig_capture_pcm_prepare,
- .cleanup = ca0132_dig_capture_pcm_cleanup
- },
};
static int ca0132_build_pcms(struct hda_codec *codec)
@@ -928,18 +849,16 @@
spec->dig_out);
if (err < 0)
return err;
- err = add_out_volume(codec, spec->dig_out, "IEC958");
+ err = snd_hda_create_spdif_share_sw(codec, &spec->multiout);
if (err < 0)
return err;
+ /* spec->multiout.share_spdif = 1; */
}
if (spec->dig_in) {
err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in);
if (err < 0)
return err;
- err = add_in_volume(codec, spec->dig_in, "IEC958");
- if (err < 0)
- return err;
}
return 0;
}
@@ -961,6 +880,9 @@
struct ca0132_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->autocfg;
+ codec->pcm_format_first = 1;
+ codec->no_sticky_stream = 1;
+
/* line-outs */
cfg->line_outs = 1;
cfg->line_out_pins[0] = 0x0b; /* front */
@@ -988,14 +910,24 @@
/* Mic-in */
spec->input_pins[0] = 0x12;
- spec->input_labels[0] = "Mic-In";
+ spec->input_labels[0] = "Mic";
spec->adcs[0] = 0x07;
/* Line-In */
spec->input_pins[1] = 0x11;
- spec->input_labels[1] = "Line-In";
+ spec->input_labels[1] = "Line";
spec->adcs[1] = 0x08;
spec->num_inputs = 2;
+
+ /* SPDIF I/O */
+ spec->dig_out = 0x05;
+ spec->multiout.dig_out_nid = spec->dig_out;
+ cfg->dig_out_pins[0] = 0x0c;
+ cfg->dig_outs = 1;
+ cfg->dig_out_type[0] = HDA_PCM_TYPE_SPDIF;
+ spec->dig_in = 0x09;
+ cfg->dig_in_pin = 0x0e;
+ cfg->dig_in_type = HDA_PCM_TYPE_SPDIF;
}
static void ca0132_init_chip(struct hda_codec *codec)
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 1436118..5e22a8f 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -2967,12 +2967,10 @@
};
static const struct snd_pci_quirk cxt5066_cfg_tbl[] = {
- SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT5066_AUTO),
SND_PCI_QUIRK_MASK(0x1025, 0xff00, 0x0400, "Acer", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x02d8, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x02f5, "Dell Vostro 320", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0401, "Dell Vostro 1014", CXT5066_DELL_VOSTRO),
- SND_PCI_QUIRK(0x1028, 0x0402, "Dell Vostro", CXT5066_DELL_VOSTRO),
SND_PCI_QUIRK(0x1028, 0x0408, "Dell Inspiron One 19T", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x050f, "Dell Inspiron", CXT5066_IDEAPAD),
SND_PCI_QUIRK(0x1028, 0x0510, "Dell Vostro", CXT5066_IDEAPAD),
@@ -2988,14 +2986,10 @@
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400s", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c5, "Thinkpad Edge 13", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21c6, "Thinkpad Edge 13", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T510", CXT5066_AUTO),
- SND_PCI_QUIRK(0x17aa, 0x21cf, "Lenovo T520 & W520", CXT5066_AUTO),
SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT5066_THINKPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo U350", CXT5066_ASUS),
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo G560", CXT5066_ASUS),
- SND_PCI_QUIRK(0x17aa, 0x3938, "Lenovo G565", CXT5066_AUTO),
- SND_PCI_QUIRK(0x1b0a, 0x2092, "CyberpowerPC Gamer Xplorer N57001", CXT5066_AUTO),
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 69b9284..8f23374 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -877,8 +877,6 @@
struct hdmi_eld *eld;
struct hdmi_spec_per_cvt *per_cvt = NULL;
- hinfo->nid = 0; /* clear the leftover value */
-
/* Validate hinfo */
pin_idx = hinfo_to_pin_index(spec, hinfo);
if (snd_BUG_ON(pin_idx < 0))
@@ -1163,6 +1161,14 @@
return hdmi_setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
}
+static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
+ struct hda_codec *codec,
+ struct snd_pcm_substream *substream)
+{
+ snd_hda_codec_cleanup_stream(codec, hinfo->nid);
+ return 0;
+}
+
static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
struct hda_codec *codec,
struct snd_pcm_substream *substream)
@@ -1202,6 +1208,7 @@
.open = hdmi_pcm_open,
.close = hdmi_pcm_close,
.prepare = generic_hdmi_playback_pcm_prepare,
+ .cleanup = generic_hdmi_playback_pcm_cleanup,
};
static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -1220,7 +1227,6 @@
pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
pstr->substreams = 1;
pstr->ops = generic_ops;
- pstr->nid = 1; /* FIXME: just for avoiding a debug WARNING */
/* other pstr fields are set in open */
}
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 344b221..4f81dd4 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -6099,6 +6099,8 @@
[ALC269_FIXUP_PCM_44K] = {
.type = ALC_FIXUP_FUNC,
.v.func = alc269_fixup_pcm_44k,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_QUANTA_MUTE
},
[ALC269_FIXUP_STEREO_DMIC] = {
.type = ALC_FIXUP_FUNC,
@@ -6206,9 +6208,11 @@
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21ca, "Thinkpad L412", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21e9, "Thinkpad Edge 15", ALC269_FIXUP_SKU_IGNORE),
+ SND_PCI_QUIRK(0x17aa, 0x21f6, "Thinkpad T530", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_QUANTA_MUTE),
- SND_PCI_QUIRK(0x17aa, 0x3bf8, "Lenovo Ideapd", ALC269_FIXUP_PCM_44K),
+ SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
#if 0
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 94040cc..ea5775a 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4272,7 +4272,8 @@
unsigned int gpio;
int i;
- snd_hda_sequence_write(codec, spec->init);
+ if (spec->init)
+ snd_hda_sequence_write(codec, spec->init);
/* power down adcs initially */
if (spec->powerdown_adcs)
@@ -5748,7 +5749,6 @@
/* fallthru */
case 0x111d76b4: /* 6 Port without Analog Mixer */
case 0x111d76b5:
- spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
@@ -5773,7 +5773,6 @@
spec->stream_delay = 40; /* 40 milliseconds */
/* disable VSW */
- spec->init = stac92hd71bxx_core_init;
unmute_init++;
snd_hda_codec_set_pincfg(codec, 0x0f, 0x40f000f0);
snd_hda_codec_set_pincfg(codec, 0x19, 0x40f000f3);
@@ -5788,7 +5787,6 @@
/* fallthru */
default:
- spec->init = stac92hd71bxx_core_init;
codec->slave_dig_outs = stac92hd71bxx_slave_dig_outs;
spec->num_dmics = stac92xx_connected_ports(codec,
stac92hd71bxx_dmic_nids,
@@ -5796,6 +5794,9 @@
break;
}
+ if (get_wcaps_type(get_wcaps(codec, 0x28)) == AC_WID_VOL_KNB)
+ spec->init = stac92hd71bxx_core_init;
+
if (get_wcaps(codec, 0xa) & AC_WCAP_IN_AMP)
snd_hda_sequence_write_cache(codec, unmute_init);
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index 80d90cb..4307717 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -1752,6 +1752,14 @@
{
struct via_spec *spec = codec->spec;
vt1708_stop_hp_work(spec);
+
+ if (spec->codec_type == VT1802) {
+ /* Fix pop noise on headphones */
+ int i;
+ for (i = 0; i < spec->autocfg.hp_outs; i++)
+ snd_hda_set_pin_ctl(codec, spec->autocfg.hp_pins[i], 0);
+ }
+
return 0;
}
#endif
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index d1ab4370..5579b08 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -851,6 +851,8 @@
/* hardcoded device name & channel count */
err = snd_pcm_new(chip->card, (char *)card_name, 0,
1, 1, &pcm);
+ if (err < 0)
+ return err;
pcm->private_data = chip;
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index b8ac871..b12308b 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -6585,7 +6585,7 @@
snd_printk(KERN_ERR "HDSPM: "
"unable to kmalloc Mixer memory of %d Bytes\n",
(int)sizeof(struct hdspm_mixer));
- return err;
+ return -ENOMEM;
}
hdspm->port_names_in = NULL;
diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c
index 512434e..805ab6e 100644
--- a/sound/pci/sis7019.c
+++ b/sound/pci/sis7019.c
@@ -1377,8 +1377,9 @@
if (rc)
goto error_out_cleanup;
- if (request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
- sis)) {
+ rc = request_irq(pci->irq, sis_interrupt, IRQF_SHARED, KBUILD_MODNAME,
+ sis);
+ if (rc) {
dev_err(&pci->dev, "unable to allocate irq %d\n", sis->irq);
goto error_out_cleanup;
}
diff --git a/sound/ppc/powermac.c b/sound/ppc/powermac.c
index f5ceb6f..210cafe 100644
--- a/sound/ppc/powermac.c
+++ b/sound/ppc/powermac.c
@@ -143,7 +143,7 @@
return 0;
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM_SLEEP
static int snd_pmac_driver_suspend(struct device *dev)
{
struct snd_card *card = dev_get_drvdata(dev);
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 1aa52ef..9b18b52 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -1040,6 +1040,7 @@
GFP_KERNEL);
if (!the_card.null_buffer_start_vaddr) {
pr_info("%s: nullbuffer alloc failed\n", __func__);
+ ret = -ENOMEM;
goto clean_preallocate;
}
pr_debug("%s: null vaddr=%p dma=%#llx\n", __func__,
diff --git a/sound/soc/blackfin/bf6xx-sport.c b/sound/soc/blackfin/bf6xx-sport.c
index 318c5ba5..dfb7443 100644
--- a/sound/soc/blackfin/bf6xx-sport.c
+++ b/sound/soc/blackfin/bf6xx-sport.c
@@ -413,7 +413,14 @@
void sport_delete(struct sport_device *sport)
{
+ if (sport->tx_desc)
+ dma_free_coherent(NULL, sport->tx_desc_size,
+ sport->tx_desc, 0);
+ if (sport->rx_desc)
+ dma_free_coherent(NULL, sport->rx_desc_size,
+ sport->rx_desc, 0);
sport_free_resource(sport);
+ kfree(sport);
}
EXPORT_SYMBOL(sport_delete);
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 3c79592..23b4018 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2406,6 +2406,10 @@
/* Setup AB8500 according to board-settings */
pdata = (struct ab8500_platform_data *)dev_get_platdata(dev->parent);
+
+ /* Inform SoC Core that we have our own I/O arrangements. */
+ codec->control_data = (void *)true;
+
status = ab8500_audio_setup_mics(codec, &pdata->codec->amics);
if (status < 0) {
pr_err("%s: Failed to setup mics (%d)!\n", __func__, status);
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 8c39ddd..11b1b71 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -186,6 +186,7 @@
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 6276e35..8f726c0 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -581,6 +581,8 @@
{
struct mc13783_priv *priv = snd_soc_codec_get_drvdata(codec);
+ codec->control_data = priv->mc13xxx;
+
mc13xxx_lock(priv->mc13xxx);
/* these are the reset values */
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 8af6a52..df2f99d 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -239,6 +239,7 @@
{"Headphone Mux", "DAC", "DAC"}, /* dac --> hp_mux */
{"LO", NULL, "DAC"}, /* dac --> line_out */
+ {"LINE_IN", NULL, "VAG_POWER"},
{"Headphone Mux", "LINE_IN", "LINE_IN"},/* line_in --> hp_mux */
{"HP", NULL, "Headphone Mux"}, /* hp_mux --> hp */
@@ -1357,8 +1358,6 @@
if (ret)
goto err;
- snd_soc_dapm_new_widgets(&codec->dapm);
-
return 0;
err:
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 982e437..33c0f3d 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -340,6 +340,7 @@
printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 6537f16..e33d327 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -128,13 +128,9 @@
ARIZONA_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
-ARIZONA_MIXER_CONTROLS("DRC2L", ARIZONA_DRC2LMIX_INPUT_1_SOURCE),
-ARIZONA_MIXER_CONTROLS("DRC2R", ARIZONA_DRC2RMIX_INPUT_1_SOURCE),
SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
-SND_SOC_BYTES_MASK("DRC2", ARIZONA_DRC2_CTRL1, 5,
- ARIZONA_DRC2R_ENA | ARIZONA_DRC2L_ENA),
ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
@@ -236,8 +232,6 @@
ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(DRC2L, ARIZONA_DRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(DRC2R, ARIZONA_DRC2RMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
@@ -349,10 +343,6 @@
NULL, 0),
SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
NULL, 0),
-SND_SOC_DAPM_PGA("DRC2L", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2L_ENA_SHIFT, 0,
- NULL, 0),
-SND_SOC_DAPM_PGA("DRC2R", ARIZONA_DRC2_CTRL1, ARIZONA_DRC2R_ENA_SHIFT, 0,
- NULL, 0),
SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
NULL, 0),
@@ -466,8 +456,6 @@
ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
ARIZONA_MIXER_WIDGETS(DRC1R, "DRC1R"),
-ARIZONA_MIXER_WIDGETS(DRC2L, "DRC2L"),
-ARIZONA_MIXER_WIDGETS(DRC2R, "DRC2R"),
ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
@@ -553,8 +541,6 @@
{ name, "EQ4", "EQ4" }, \
{ name, "DRC1L", "DRC1L" }, \
{ name, "DRC1R", "DRC1R" }, \
- { name, "DRC2L", "DRC2L" }, \
- { name, "DRC2R", "DRC2R" }, \
{ name, "LHPF1", "LHPF1" }, \
{ name, "LHPF2", "LHPF2" }, \
{ name, "LHPF3", "LHPF3" }, \
@@ -639,6 +625,15 @@
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -675,8 +670,6 @@
ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
- ARIZONA_MIXER_ROUTES("DRC2L", "DRC2L"),
- ARIZONA_MIXER_ROUTES("DRC2R", "DRC2R"),
ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 8033f70..01ebbcc 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -681,6 +681,18 @@
{ "AIF2 Capture", NULL, "SYSCLK" },
{ "AIF3 Capture", NULL, "SYSCLK" },
+ { "IN1L PGA", NULL, "IN1L" },
+ { "IN1R PGA", NULL, "IN1R" },
+
+ { "IN2L PGA", NULL, "IN2L" },
+ { "IN2R PGA", NULL, "IN2R" },
+
+ { "IN3L PGA", NULL, "IN3L" },
+ { "IN3R PGA", NULL, "IN3R" },
+
+ { "IN4L PGA", NULL, "IN4L" },
+ { "IN4R PGA", NULL, "IN4R" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index eaf6586..ce67200 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -2501,6 +2501,9 @@
/* VMID 2*250k */
snd_soc_update_bits(codec, WM8962_PWR_MGMT_1,
WM8962_VMID_SEL_MASK, 0x100);
+
+ if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+ msleep(100);
break;
case SND_SOC_BIAS_OFF:
@@ -3730,21 +3733,6 @@
regcache_sync(wm8962->regmap);
- regmap_update_bits(wm8962->regmap, WM8962_ANTI_POP,
- WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA,
- WM8962_STARTUP_BIAS_ENA | WM8962_VMID_BUF_ENA);
-
- /* Bias enable at 2*50k for ramp */
- regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
- WM8962_VMID_SEL_MASK | WM8962_BIAS_ENA,
- WM8962_BIAS_ENA | 0x180);
-
- msleep(5);
-
- /* VMID back to 2x250k for standby */
- regmap_update_bits(wm8962->regmap, WM8962_PWR_MGMT_1,
- WM8962_VMID_SEL_MASK, 0x100);
-
return 0;
}
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index bb62f4b..6c9eeca 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -2649,7 +2649,7 @@
return -EINVAL;
}
- bclk_rate = params_rate(params) * 2;
+ bclk_rate = params_rate(params) * 4;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
bclk_rate *= 16;
@@ -3253,10 +3253,13 @@
int ret;
int report;
+ pm_runtime_get_sync(dev);
+
ret = regmap_read(regmap, WM8994_INTERRUPT_RAW_STATUS_2, ®);
if (ret < 0) {
dev_err(dev, "Failed to read microphone status: %d\n",
ret);
+ pm_runtime_put(dev);
return;
}
@@ -3299,6 +3302,8 @@
snd_soc_jack_report(priv->micdet[1].jack, report,
SND_JACK_HEADSET | SND_JACK_BTN_0);
+
+ pm_runtime_put(dev);
}
static irqreturn_t wm8994_mic_irq(int irq, void *data)
@@ -3421,12 +3426,15 @@
int reg;
bool present;
+ pm_runtime_get_sync(codec->dev);
+
mutex_lock(&wm8994->accdet_lock);
reg = snd_soc_read(codec, WM1811_JACKDET_CTRL);
if (reg < 0) {
dev_err(codec->dev, "Failed to read jack status: %d\n", reg);
mutex_unlock(&wm8994->accdet_lock);
+ pm_runtime_put(codec->dev);
return IRQ_NONE;
}
@@ -3491,6 +3499,7 @@
SND_JACK_MECHANICAL | SND_JACK_HEADSET |
wm8994->btn_mask);
+ pm_runtime_put(codec->dev);
return IRQ_HANDLED;
}
@@ -3602,6 +3611,8 @@
if (!(snd_soc_read(codec, WM8958_MIC_DETECT_1) & WM8958_MICD_ENA))
return IRQ_HANDLED;
+ pm_runtime_get_sync(codec->dev);
+
/* We may occasionally read a detection without an impedence
* range being provided - if that happens loop again.
*/
@@ -3612,6 +3623,7 @@
dev_err(codec->dev,
"Failed to read mic detect status: %d\n",
reg);
+ pm_runtime_put(codec->dev);
return IRQ_NONE;
}
@@ -3639,6 +3651,7 @@
dev_warn(codec->dev, "Accessory detection with no callback\n");
out:
+ pm_runtime_put(codec->dev);
return IRQ_HANDLED;
}
@@ -4025,6 +4038,8 @@
break;
case WM8958:
if (wm8994->revision < 1) {
+ snd_soc_dapm_add_routes(dapm, wm8994_intercon,
+ ARRAY_SIZE(wm8994_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_revd_intercon,
ARRAY_SIZE(wm8994_revd_intercon));
snd_soc_dapm_add_routes(dapm, wm8994_lateclk_revd_intercon,
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 099e6ec..c6d2076 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -148,7 +148,7 @@
SOC_SINGLE("Capture ADC Switch", AC97_REC_GAIN, 15, 1, 1),
SOC_ENUM("Capture Volume Steps", wm9712_enum[6]),
-SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 1),
+SOC_DOUBLE("Capture Volume", AC97_REC_GAIN, 8, 0, 63, 0),
SOC_SINGLE("Capture ZC Switch", AC97_REC_GAIN, 7, 1, 0),
SOC_SINGLE_TLV("Mic 1 Volume", AC97_MIC, 8, 31, 1, main_tlv),
@@ -272,7 +272,7 @@
/* Mic select */
static const struct snd_kcontrol_new wm9712_mic_src_controls =
-SOC_DAPM_ENUM("Route", wm9712_enum[7]);
+SOC_DAPM_ENUM("Mic Source Select", wm9712_enum[7]);
/* diff select */
static const struct snd_kcontrol_new wm9712_diff_sel_controls =
@@ -291,7 +291,9 @@
&wm9712_capture_selectl_controls),
SND_SOC_DAPM_MUX("Right Capture Select", SND_SOC_NOPM, 0, 0,
&wm9712_capture_selectr_controls),
-SND_SOC_DAPM_MUX("Mic Select Source", SND_SOC_NOPM, 0, 0,
+SND_SOC_DAPM_MUX("Left Mic Select Source", SND_SOC_NOPM, 0, 0,
+ &wm9712_mic_src_controls),
+SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
&wm9712_mic_src_controls),
SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
&wm9712_diff_sel_controls),
@@ -319,6 +321,7 @@
SND_SOC_DAPM_PGA("Line PGA", AC97_INT_PAGING, 2, 1, NULL, 0),
SND_SOC_DAPM_PGA("Phone PGA", AC97_INT_PAGING, 1, 1, NULL, 0),
SND_SOC_DAPM_PGA("Mic PGA", AC97_INT_PAGING, 0, 1, NULL, 0),
+SND_SOC_DAPM_PGA("Differential Mic", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_MICBIAS("Mic Bias", AC97_INT_PAGING, 10, 1),
SND_SOC_DAPM_OUTPUT("MONOOUT"),
SND_SOC_DAPM_OUTPUT("HPOUTL"),
@@ -379,6 +382,18 @@
{"Mic PGA", NULL, "MIC1"},
{"Mic PGA", NULL, "MIC2"},
+ /* microphones */
+ {"Differential Mic", NULL, "MIC1"},
+ {"Differential Mic", NULL, "MIC2"},
+ {"Left Mic Select Source", "Mic 1", "MIC1"},
+ {"Left Mic Select Source", "Mic 2", "MIC2"},
+ {"Left Mic Select Source", "Stereo", "MIC1"},
+ {"Left Mic Select Source", "Differential", "Differential Mic"},
+ {"Right Mic Select Source", "Mic 1", "MIC1"},
+ {"Right Mic Select Source", "Mic 2", "MIC2"},
+ {"Right Mic Select Source", "Stereo", "MIC2"},
+ {"Right Mic Select Source", "Differential", "Differential Mic"},
+
/* left capture selector */
{"Left Capture Select", "Mic", "MIC1"},
{"Left Capture Select", "Speaker Mixer", "Speaker Mixer"},
@@ -619,6 +634,7 @@
{
int ret = 0;
+ codec->control_data = codec; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 3eb19fb..d0b8a32 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1196,6 +1196,7 @@
if (wm9713 == NULL)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
+ codec->control_data = wm9713; /* we don't use regmap! */
ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
if (ret < 0)
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 95441bf..ce5e5cd 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -380,14 +380,20 @@
static void davinci_mcasp_start(struct davinci_audio_dev *dev, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
- if (dev->txnumevt) /* enable FIFO */
+ if (dev->txnumevt) { /* enable FIFO */
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
+ FIFO_ENABLE);
mcasp_set_bits(dev->base + DAVINCI_MCASP_WFIFOCTL,
FIFO_ENABLE);
+ }
mcasp_start_tx(dev);
} else {
- if (dev->rxnumevt) /* enable FIFO */
+ if (dev->rxnumevt) { /* enable FIFO */
+ mcasp_clr_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
+ FIFO_ENABLE);
mcasp_set_bits(dev->base + DAVINCI_MCASP_RFIFOCTL,
FIFO_ENABLE);
+ }
mcasp_start_rx(dev);
}
}
diff --git a/sound/soc/fsl/imx-ssi.c b/sound/soc/fsl/imx-ssi.c
index 28dd76c..81d7728 100644
--- a/sound/soc/fsl/imx-ssi.c
+++ b/sound/soc/fsl/imx-ssi.c
@@ -380,13 +380,14 @@
static struct snd_soc_dai_driver imx_ssi_dai = {
.probe = imx_ssi_dai_probe,
.playback = {
- .channels_min = 1,
+ /* The SSI does not support monaural audio. */
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
.capture = {
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = SNDRV_PCM_RATE_8000_96000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index 99a997f..b6fa776 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -10,7 +10,7 @@
if SND_MXS_SOC
config SND_SOC_MXS_SGTL5000
- tristate "SoC Audio support for i.MX boards with sgtl5000"
+ tristate "SoC Audio support for MXS boards with sgtl5000"
depends on I2C
select SND_SOC_SGTL5000
help
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index aba71bf..b303071 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -394,9 +394,14 @@
struct snd_soc_dai *cpu_dai)
{
struct mxs_saif *saif = snd_soc_dai_get_drvdata(cpu_dai);
+ struct mxs_saif *master_saif;
u32 scr, stat;
int ret;
+ master_saif = mxs_saif_get_master(saif);
+ if (!master_saif)
+ return -EINVAL;
+
/* mclk should already be set */
if (!saif->mclk && saif->mclk_in_use) {
dev_err(cpu_dai->dev, "set mclk first\n");
@@ -420,6 +425,25 @@
return ret;
}
+ /* prepare clk in hw_param, enable in trigger */
+ clk_prepare(saif->clk);
+ if (saif != master_saif) {
+ /*
+ * Set an initial clock rate for the saif internal logic to work
+ * properly. This is important when working in EXTMASTER mode
+ * that uses the other saif's BITCLK&LRCLK but it still needs a
+ * basic clock which should be fast enough for the internal
+ * logic.
+ */
+ clk_enable(saif->clk);
+ ret = clk_set_rate(saif->clk, 24000000);
+ clk_disable(saif->clk);
+ if (ret)
+ return ret;
+
+ clk_prepare(master_saif->clk);
+ }
+
scr = __raw_readl(saif->base + SAIF_CTRL);
scr &= ~BM_SAIF_CTRL_WORD_LENGTH;
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 34835e8..d33c48b 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -745,7 +745,7 @@
{
const char *signal, *src;
- if (mcbsp->pdata->mux_signal)
+ if (!mcbsp->pdata->mux_signal)
return -EINVAL;
switch (mux) {
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index 1046083..acdd3ef 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -820,3 +820,4 @@
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP I2S SoC Interface");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-mcbsp");
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index 5a649da..f0feb06 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -441,3 +441,4 @@
MODULE_AUTHOR("Jarkko Nikula <jarkko.nikula@bitmer.com>");
MODULE_DESCRIPTION("OMAP PCM DMA module");
MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-pcm-audio");
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index b7b2a1f..89b0646 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -20,7 +20,7 @@
#include <sound/pcm_params.h>
#include <plat/audio.h>
-#include <plat/dma.h>
+#include <mach/dma.h>
#include "dma.h"
#include "pcm.h"
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index f219b2f..c501af6 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -826,7 +826,7 @@
}
if (!rtd->cpu_dai) {
- dev_dbg(card->dev, "CPU DAI %s not registered\n",
+ dev_err(card->dev, "CPU DAI %s not registered\n",
dai_link->cpu_dai_name);
return -EPROBE_DEFER;
}
@@ -857,14 +857,14 @@
}
if (!rtd->codec_dai) {
- dev_dbg(card->dev, "CODEC DAI %s not registered\n",
+ dev_err(card->dev, "CODEC DAI %s not registered\n",
dai_link->codec_dai_name);
return -EPROBE_DEFER;
}
}
if (!rtd->codec) {
- dev_dbg(card->dev, "CODEC %s not registered\n",
+ dev_err(card->dev, "CODEC %s not registered\n",
dai_link->codec_name);
return -EPROBE_DEFER;
}
@@ -888,7 +888,7 @@
rtd->platform = platform;
}
if (!rtd->platform) {
- dev_dbg(card->dev, "platform %s not registered\n",
+ dev_err(card->dev, "platform %s not registered\n",
dai_link->platform_name);
return -EPROBE_DEFER;
}
@@ -1096,7 +1096,7 @@
}
/* If the driver didn't set I/O up try regmap */
- if (!codec->control_data)
+ if (!codec->write && dev_get_regmap(codec->dev, NULL))
snd_soc_codec_set_cache_io(codec, 0, 0, SND_SOC_REGMAP);
if (driver->controls)
@@ -1481,6 +1481,8 @@
return 0;
}
+ dev_err(card->dev, "%s not registered\n", aux_dev->codec_name);
+
return -EPROBE_DEFER;
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index 7f8b3b7..0c17293 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -103,7 +103,7 @@
}
/* Report before the DAPM sync to help users updating micbias status */
- blocking_notifier_call_chain(&jack->notifier, status, jack);
+ blocking_notifier_call_chain(&jack->notifier, jack->status, jack);
snd_soc_dapm_sync(dapm);
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index d684df2..e463529 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -177,7 +177,7 @@
}
alc5632->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
- if (alc5632->gpio_hp_det == -ENODEV)
+ if (alc5632->gpio_hp_det == -EPROBE_DEFER)
return -EPROBE_DEFER;
ret = snd_soc_of_parse_card_name(card, "nvidia,model");
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 0c5bb33..d4f14e4 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -284,27 +284,27 @@
} else if (np) {
pdata->gpio_spkr_en = of_get_named_gpio(np,
"nvidia,spkr-en-gpios", 0);
- if (pdata->gpio_spkr_en == -ENODEV)
+ if (pdata->gpio_spkr_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_hp_mute = of_get_named_gpio(np,
"nvidia,hp-mute-gpios", 0);
- if (pdata->gpio_hp_mute == -ENODEV)
+ if (pdata->gpio_hp_mute == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_hp_det = of_get_named_gpio(np,
"nvidia,hp-det-gpios", 0);
- if (pdata->gpio_hp_det == -ENODEV)
+ if (pdata->gpio_hp_det == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_int_mic_en = of_get_named_gpio(np,
"nvidia,int-mic-en-gpios", 0);
- if (pdata->gpio_int_mic_en == -ENODEV)
+ if (pdata->gpio_int_mic_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
pdata->gpio_ext_mic_en = of_get_named_gpio(np,
"nvidia,ext-mic-en-gpios", 0);
- if (pdata->gpio_ext_mic_en == -ENODEV)
+ if (pdata->gpio_ext_mic_en == -EPROBE_DEFER)
return -EPROBE_DEFER;
}
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 62ac028..057e28e 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -21,7 +21,7 @@
#include <linux/mfd/dbx500-prcmu.h>
#include <mach/hardware.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index ee14d2d..5c472f3 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -19,7 +19,7 @@
#include <linux/slab.h>
#include <mach/hardware.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#include <sound/soc.h>
diff --git a/sound/soc/ux500/ux500_msp_i2s.h b/sound/soc/ux500/ux500_msp_i2s.h
index 7f71b4a..2d9136d 100644
--- a/sound/soc/ux500/ux500_msp_i2s.h
+++ b/sound/soc/ux500/ux500_msp_i2s.h
@@ -17,7 +17,7 @@
#include <linux/platform_device.h>
-#include <mach/board-mop500-msp.h>
+#include <mach/msp.h>
#define MSP_INPUT_FREQ_APB 48000000
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index 0f647d2..c411812 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -821,10 +821,6 @@
if (++ep->use_count != 1)
return 0;
- /* just to be sure */
- deactivate_urbs(ep, 0, 1);
- wait_clear_urbs(ep);
-
ep->active_mask = 0;
ep->unlink_mask = 0;
ep->phase = 0;
diff --git a/sound/usb/pcm.c b/sound/usb/pcm.c
index a1298f3..62ec808 100644
--- a/sound/usb/pcm.c
+++ b/sound/usb/pcm.c
@@ -544,6 +544,9 @@
subs->last_frame_number = 0;
runtime->delay = 0;
+ /* clear the pending deactivation on the target EPs */
+ deactivate_endpoints(subs);
+
/* for playback, submit the URBs now; otherwise, the first hwptr_done
* updates for all URBs would happen at the same time when starting */
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK)
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 77f124f..35655c3 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -319,6 +319,8 @@
LIB_H += util/cgroup.h
LIB_H += $(TRACE_EVENT_DIR)event-parse.h
LIB_H += util/target.h
+LIB_H += util/rblist.h
+LIB_H += util/intlist.h
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
@@ -383,6 +385,8 @@
LIB_OBJS += $(OUTPUT)util/cpumap.o
LIB_OBJS += $(OUTPUT)util/cgroup.o
LIB_OBJS += $(OUTPUT)util/target.o
+LIB_OBJS += $(OUTPUT)util/rblist.o
+LIB_OBJS += $(OUTPUT)util/intlist.o
BUILTIN_OBJS += $(OUTPUT)builtin-annotate.o
@@ -983,7 +987,8 @@
$(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope*
$(MAKE) -C Documentation/ clean
$(RM) $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)PERF-CFLAGS
- $(RM) $(OUTPUT)util/*-{bison,flex}*
+ $(RM) $(OUTPUT)util/*-bison*
+ $(RM) $(OUTPUT)util/*-flex*
$(python-clean)
.PHONY: all install clean strip $(LIBTRACEEVENT)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index f5a6452..4db6e1b 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -313,7 +313,7 @@
}
}
- perf_session__update_sample_type(session);
+ perf_session__set_id_hdr_size(session);
}
static int process_buildids(struct perf_record *rec)
@@ -844,8 +844,6 @@
struct perf_record *rec = &record;
char errbuf[BUFSIZ];
- perf_header__set_cmdline(argc, argv);
-
evsel_list = perf_evlist__new(NULL, NULL);
if (evsel_list == NULL)
return -ENOMEM;
diff --git a/tools/perf/builtin-report.c b/tools/perf/builtin-report.c
index 69b1c11..7c88a24 100644
--- a/tools/perf/builtin-report.c
+++ b/tools/perf/builtin-report.c
@@ -249,8 +249,9 @@
static int perf_report__setup_sample_type(struct perf_report *rep)
{
struct perf_session *self = rep->session;
+ u64 sample_type = perf_evlist__sample_type(self->evlist);
- if (!self->fd_pipe && !(self->sample_type & PERF_SAMPLE_CALLCHAIN)) {
+ if (!self->fd_pipe && !(sample_type & PERF_SAMPLE_CALLCHAIN)) {
if (sort__has_parent) {
ui__error("Selected --sort parent, but no "
"callchain data. Did you call "
@@ -274,7 +275,7 @@
if (sort__branch_mode == 1) {
if (!self->fd_pipe &&
- !(self->sample_type & PERF_SAMPLE_BRANCH_STACK)) {
+ !(sample_type & PERF_SAMPLE_BRANCH_STACK)) {
ui__error("Selected -b but no branch data. "
"Did you call perf record without -b?\n");
return -1;
diff --git a/tools/perf/builtin-test.c b/tools/perf/builtin-test.c
index d909eb7..1d592f5 100644
--- a/tools/perf/builtin-test.c
+++ b/tools/perf/builtin-test.c
@@ -478,7 +478,6 @@
unsigned int nr_events[nsyscalls],
expected_nr_events[nsyscalls], i, j;
struct perf_evsel *evsels[nsyscalls], *evsel;
- int sample_size = __perf_evsel__sample_size(attr.sample_type);
for (i = 0; i < nsyscalls; ++i) {
char name[64];
@@ -563,8 +562,7 @@
goto out_munmap;
}
- err = perf_event__parse_sample(event, attr.sample_type, sample_size,
- false, &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample, false);
if (err) {
pr_err("Can't parse sample, err = %d\n", err);
goto out_munmap;
@@ -661,12 +659,12 @@
const char *cmd = "sleep";
const char *argv[] = { cmd, "1", NULL, };
char *bname;
- u64 sample_type, prev_time = 0;
+ u64 prev_time = 0;
bool found_cmd_mmap = false,
found_libc_mmap = false,
found_vdso_mmap = false,
found_ld_mmap = false;
- int err = -1, errs = 0, i, wakeups = 0, sample_size;
+ int err = -1, errs = 0, i, wakeups = 0;
u32 cpu;
int total_events = 0, nr_events[PERF_RECORD_MAX] = { 0, };
@@ -757,13 +755,6 @@
}
/*
- * We'll need these two to parse the PERF_SAMPLE_* fields in each
- * event.
- */
- sample_type = perf_evlist__sample_type(evlist);
- sample_size = __perf_evsel__sample_size(sample_type);
-
- /*
* Now that all is properly set up, enable the events, they will
* count just on workload.pid, which will start...
*/
@@ -788,9 +779,7 @@
if (type < PERF_RECORD_MAX)
nr_events[type]++;
- err = perf_event__parse_sample(event, sample_type,
- sample_size, true,
- &sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &sample, false);
if (err < 0) {
if (verbose)
perf_event__fprintf(event, stderr);
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 35e86c6..68cd61e 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -38,6 +38,7 @@
#include "util/cpumap.h"
#include "util/xyarray.h"
#include "util/sort.h"
+#include "util/intlist.h"
#include "util/debug.h"
@@ -706,8 +707,16 @@
int err;
if (!machine && perf_guest) {
- pr_err("Can't find guest [%d]'s kernel information\n",
- event->ip.pid);
+ static struct intlist *seen;
+
+ if (!seen)
+ seen = intlist__new();
+
+ if (!intlist__has_entry(seen, event->ip.pid)) {
+ pr_err("Can't find guest [%d]'s kernel information\n",
+ event->ip.pid);
+ intlist__add(seen, event->ip.pid);
+ }
return;
}
@@ -811,7 +820,7 @@
int ret;
while ((event = perf_evlist__mmap_read(top->evlist, idx)) != NULL) {
- ret = perf_session__parse_sample(session, event, &sample);
+ ret = perf_evlist__parse_sample(top->evlist, event, &sample, false);
if (ret) {
pr_err("Can't parse sample, err = %d\n", ret);
continue;
@@ -943,8 +952,10 @@
* based cpu-clock-tick sw counter, which
* is always available even if no PMU support:
*/
- if (attr->type == PERF_TYPE_HARDWARE &&
- attr->config == PERF_COUNT_HW_CPU_CYCLES) {
+ if ((err == ENOENT || err == ENXIO) &&
+ (attr->type == PERF_TYPE_HARDWARE) &&
+ (attr->config == PERF_COUNT_HW_CPU_CYCLES)) {
+
if (verbose)
ui__warning("Cycles event not supported,\n"
"trying to fall back to cpu-clock-ticks\n");
@@ -1032,7 +1043,7 @@
&top->session->host_machine);
perf_top__start_counters(top);
top->session->evlist = top->evlist;
- perf_session__update_sample_type(top->session);
+ perf_session__set_id_hdr_size(top->session);
/* Wait for a minimal set of events before starting the snapshot */
poll(top->evlist->pollfd, top->evlist->nr_fds, 100);
diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h
index 1b19728..d84870b 100644
--- a/tools/perf/util/event.h
+++ b/tools/perf/util/event.h
@@ -197,9 +197,6 @@
const char *perf_event__name(unsigned int id);
-int perf_event__parse_sample(const union perf_event *event, u64 type,
- int sample_size, bool sample_id_all,
- struct perf_sample *sample, bool swapped);
int perf_event__synthesize_sample(union perf_event *event, u64 type,
const struct perf_sample *sample,
bool swapped);
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 3edfd34..9b38681 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -881,3 +881,10 @@
return 0;
}
+
+int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+ struct perf_sample *sample, bool swapped)
+{
+ struct perf_evsel *e = list_entry(evlist->entries.next, struct perf_evsel, node);
+ return perf_evsel__parse_sample(e, event, sample, swapped);
+}
diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h
index 40d4d3c..528c1ac 100644
--- a/tools/perf/util/evlist.h
+++ b/tools/perf/util/evlist.h
@@ -122,6 +122,9 @@
bool perf_evlist__sample_id_all(const const struct perf_evlist *evlist);
u16 perf_evlist__id_hdr_size(const struct perf_evlist *evlist);
+int perf_evlist__parse_sample(struct perf_evlist *evlist, union perf_event *event,
+ struct perf_sample *sample, bool swapped);
+
bool perf_evlist__valid_sample_type(const struct perf_evlist *evlist);
bool perf_evlist__valid_sample_id_all(const struct perf_evlist *evlist);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index e817713..2eaae14 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -20,7 +20,7 @@
#define FD(e, x, y) (*(int *)xyarray__entry(e->fd, x, y))
#define GROUP_FD(group_fd, cpu) (*(int *)xyarray__entry(group_fd, cpu, 0))
-int __perf_evsel__sample_size(u64 sample_type)
+static int __perf_evsel__sample_size(u64 sample_type)
{
u64 mask = sample_type & PERF_SAMPLE_MASK;
int size = 0;
@@ -53,6 +53,7 @@
evsel->attr = *attr;
INIT_LIST_HEAD(&evsel->node);
hists__init(&evsel->hists);
+ evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
}
struct perf_evsel *perf_evsel__new(struct perf_event_attr *attr, int idx)
@@ -728,10 +729,10 @@
return false;
}
-int perf_event__parse_sample(const union perf_event *event, u64 type,
- int sample_size, bool sample_id_all,
+int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
struct perf_sample *data, bool swapped)
{
+ u64 type = evsel->attr.sample_type;
const u64 *array;
/*
@@ -746,14 +747,14 @@
data->period = 1;
if (event->header.type != PERF_RECORD_SAMPLE) {
- if (!sample_id_all)
+ if (!evsel->attr.sample_id_all)
return 0;
return perf_event__parse_id_sample(event, type, data, swapped);
}
array = event->sample.array;
- if (sample_size + sizeof(event->header) > event->header.size)
+ if (evsel->sample_size + sizeof(event->header) > event->header.size)
return -EFAULT;
if (type & PERF_SAMPLE_IP) {
@@ -895,7 +896,7 @@
u.val32[1] = sample->tid;
if (swapped) {
/*
- * Inverse of what is done in perf_event__parse_sample
+ * Inverse of what is done in perf_evsel__parse_sample
*/
u.val32[0] = bswap_32(u.val32[0]);
u.val32[1] = bswap_32(u.val32[1]);
@@ -930,7 +931,7 @@
u.val32[0] = sample->cpu;
if (swapped) {
/*
- * Inverse of what is done in perf_event__parse_sample
+ * Inverse of what is done in perf_evsel__parse_sample
*/
u.val32[0] = bswap_32(u.val32[0]);
u.val64 = bswap_64(u.val64);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 67cc503..b559929 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -65,6 +65,7 @@
void *func;
void *data;
} handler;
+ unsigned int sample_size;
bool supported;
};
@@ -177,13 +178,8 @@
return __perf_evsel__read(evsel, ncpus, nthreads, true);
}
-int __perf_evsel__sample_size(u64 sample_type);
-
-static inline int perf_evsel__sample_size(struct perf_evsel *evsel)
-{
- return __perf_evsel__sample_size(evsel->attr.sample_type);
-}
-
void hists__init(struct hists *hists);
+int perf_evsel__parse_sample(struct perf_evsel *evsel, union perf_event *event,
+ struct perf_sample *sample, bool swapped);
#endif /* __PERF_EVSEL_H */
diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c
index 3a6d204..74ea3c2 100644
--- a/tools/perf/util/header.c
+++ b/tools/perf/util/header.c
@@ -174,6 +174,15 @@
{
int i;
+ /*
+ * If header_argv has already been set, do not override it.
+ * This allows a command to set the cmdline, parse args and
+ * then call another builtin function that implements a
+ * command -- e.g, cmd_kvm calling cmd_record.
+ */
+ if (header_argv)
+ return 0;
+
header_argc = (u32)argc;
/* do not include NULL termination */
diff --git a/tools/perf/util/intlist.c b/tools/perf/util/intlist.c
new file mode 100644
index 0000000..fd530dc
--- /dev/null
+++ b/tools/perf/util/intlist.c
@@ -0,0 +1,101 @@
+/*
+ * Based on intlist.c by:
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <linux/compiler.h>
+
+#include "intlist.h"
+
+static struct rb_node *intlist__node_new(struct rblist *rblist __used,
+ const void *entry)
+{
+ int i = (int)((long)entry);
+ struct rb_node *rc = NULL;
+ struct int_node *node = malloc(sizeof(*node));
+
+ if (node != NULL) {
+ node->i = i;
+ rc = &node->rb_node;
+ }
+
+ return rc;
+}
+
+static void int_node__delete(struct int_node *ilist)
+{
+ free(ilist);
+}
+
+static void intlist__node_delete(struct rblist *rblist __used,
+ struct rb_node *rb_node)
+{
+ struct int_node *node = container_of(rb_node, struct int_node, rb_node);
+
+ int_node__delete(node);
+}
+
+static int intlist__node_cmp(struct rb_node *rb_node, const void *entry)
+{
+ int i = (int)((long)entry);
+ struct int_node *node = container_of(rb_node, struct int_node, rb_node);
+
+ return node->i - i;
+}
+
+int intlist__add(struct intlist *ilist, int i)
+{
+ return rblist__add_node(&ilist->rblist, (void *)((long)i));
+}
+
+void intlist__remove(struct intlist *ilist __used, struct int_node *node)
+{
+ int_node__delete(node);
+}
+
+struct int_node *intlist__find(struct intlist *ilist, int i)
+{
+ struct int_node *node = NULL;
+ struct rb_node *rb_node = rblist__find(&ilist->rblist, (void *)((long)i));
+
+ if (rb_node)
+ node = container_of(rb_node, struct int_node, rb_node);
+
+ return node;
+}
+
+struct intlist *intlist__new(void)
+{
+ struct intlist *ilist = malloc(sizeof(*ilist));
+
+ if (ilist != NULL) {
+ rblist__init(&ilist->rblist);
+ ilist->rblist.node_cmp = intlist__node_cmp;
+ ilist->rblist.node_new = intlist__node_new;
+ ilist->rblist.node_delete = intlist__node_delete;
+ }
+
+ return ilist;
+}
+
+void intlist__delete(struct intlist *ilist)
+{
+ if (ilist != NULL)
+ rblist__delete(&ilist->rblist);
+}
+
+struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx)
+{
+ struct int_node *node = NULL;
+ struct rb_node *rb_node;
+
+ rb_node = rblist__entry(&ilist->rblist, idx);
+ if (rb_node)
+ node = container_of(rb_node, struct int_node, rb_node);
+
+ return node;
+}
diff --git a/tools/perf/util/intlist.h b/tools/perf/util/intlist.h
new file mode 100644
index 0000000..6d63ab9
--- /dev/null
+++ b/tools/perf/util/intlist.h
@@ -0,0 +1,75 @@
+#ifndef __PERF_INTLIST_H
+#define __PERF_INTLIST_H
+
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+#include "rblist.h"
+
+struct int_node {
+ struct rb_node rb_node;
+ int i;
+};
+
+struct intlist {
+ struct rblist rblist;
+};
+
+struct intlist *intlist__new(void);
+void intlist__delete(struct intlist *ilist);
+
+void intlist__remove(struct intlist *ilist, struct int_node *in);
+int intlist__add(struct intlist *ilist, int i);
+
+struct int_node *intlist__entry(const struct intlist *ilist, unsigned int idx);
+struct int_node *intlist__find(struct intlist *ilist, int i);
+
+static inline bool intlist__has_entry(struct intlist *ilist, int i)
+{
+ return intlist__find(ilist, i) != NULL;
+}
+
+static inline bool intlist__empty(const struct intlist *ilist)
+{
+ return rblist__empty(&ilist->rblist);
+}
+
+static inline unsigned int intlist__nr_entries(const struct intlist *ilist)
+{
+ return rblist__nr_entries(&ilist->rblist);
+}
+
+/* For intlist iteration */
+static inline struct int_node *intlist__first(struct intlist *ilist)
+{
+ struct rb_node *rn = rb_first(&ilist->rblist.entries);
+ return rn ? rb_entry(rn, struct int_node, rb_node) : NULL;
+}
+static inline struct int_node *intlist__next(struct int_node *in)
+{
+ struct rb_node *rn;
+ if (!in)
+ return NULL;
+ rn = rb_next(&in->rb_node);
+ return rn ? rb_entry(rn, struct int_node, rb_node) : NULL;
+}
+
+/**
+ * intlist_for_each - iterate over a intlist
+ * @pos: the &struct int_node to use as a loop cursor.
+ * @ilist: the &struct intlist for loop.
+ */
+#define intlist__for_each(pos, ilist) \
+ for (pos = intlist__first(ilist); pos; pos = intlist__next(pos))
+
+/**
+ * intlist_for_each_safe - iterate over a intlist safe against removal of
+ * int_node
+ * @pos: the &struct int_node to use as a loop cursor.
+ * @n: another &struct int_node to use as temporary storage.
+ * @ilist: the &struct intlist for loop.
+ */
+#define intlist__for_each_safe(pos, n, ilist) \
+ for (pos = intlist__first(ilist), n = intlist__next(pos); pos;\
+ pos = n, n = intlist__next(n))
+#endif /* __PERF_INTLIST_H */
diff --git a/tools/perf/util/parse-events-test.c b/tools/perf/util/parse-events-test.c
index 1b997d2..127d648 100644
--- a/tools/perf/util/parse-events-test.c
+++ b/tools/perf/util/parse-events-test.c
@@ -13,6 +13,9 @@
} \
} while (0)
+#define PERF_TP_SAMPLE_TYPE (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | \
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD)
+
static int test__checkevent_tracepoint(struct perf_evlist *evlist)
{
struct perf_evsel *evsel = list_entry(evlist->entries.next,
@@ -21,8 +24,7 @@
TEST_ASSERT_VAL("wrong number of entries", 1 == evlist->nr_entries);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
- evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
return 0;
}
@@ -37,8 +39,7 @@
TEST_ASSERT_VAL("wrong type",
PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU)
- == evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period",
1 == evsel->attr.sample_period);
}
@@ -428,8 +429,7 @@
evsel = list_entry(evsel->node.next, struct perf_evsel, node);
TEST_ASSERT_VAL("wrong type", PERF_TYPE_TRACEPOINT == evsel->attr.type);
TEST_ASSERT_VAL("wrong sample_type",
- (PERF_SAMPLE_RAW | PERF_SAMPLE_TIME | PERF_SAMPLE_CPU) ==
- evsel->attr.sample_type);
+ PERF_TP_SAMPLE_TYPE == evsel->attr.sample_type);
TEST_ASSERT_VAL("wrong sample_period", 1 == evsel->attr.sample_period);
TEST_ASSERT_VAL("wrong exclude_user", evsel->attr.exclude_user);
TEST_ASSERT_VAL("wrong exclude_kernel", !evsel->attr.exclude_kernel);
diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c
index 99d02aa..594f8fa 100644
--- a/tools/perf/util/parse-options.c
+++ b/tools/perf/util/parse-options.c
@@ -1,6 +1,7 @@
#include "util.h"
#include "parse-options.h"
#include "cache.h"
+#include "header.h"
#define OPT_SHORT 1
#define OPT_UNSET 2
@@ -413,6 +414,8 @@
{
struct parse_opt_ctx_t ctx;
+ perf_header__set_cmdline(argc, argv);
+
parse_options_start(&ctx, argc, argv, flags);
switch (parse_options_step(&ctx, options, usagestr)) {
case PARSE_OPT_HELP:
diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c
index e03b58a..0688bfb 100644
--- a/tools/perf/util/python.c
+++ b/tools/perf/util/python.c
@@ -797,17 +797,13 @@
event = perf_evlist__mmap_read(evlist, cpu);
if (event != NULL) {
- struct perf_evsel *first;
PyObject *pyevent = pyrf_event__new(event);
struct pyrf_event *pevent = (struct pyrf_event *)pyevent;
if (pyevent == NULL)
return PyErr_NoMemory();
- first = list_entry(evlist->entries.next, struct perf_evsel, node);
- err = perf_event__parse_sample(event, first->attr.sample_type,
- perf_evsel__sample_size(first),
- sample_id_all, &pevent->sample, false);
+ err = perf_evlist__parse_sample(evlist, event, &pevent->sample, false);
if (err)
return PyErr_Format(PyExc_OSError,
"perf: can't parse sample, err=%d", err);
diff --git a/tools/perf/util/rblist.c b/tools/perf/util/rblist.c
new file mode 100644
index 0000000..0171fb6
--- /dev/null
+++ b/tools/perf/util/rblist.c
@@ -0,0 +1,107 @@
+/*
+ * Based on strlist.c by:
+ * (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
+ *
+ * Licensed under the GPLv2.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "rblist.h"
+
+int rblist__add_node(struct rblist *rblist, const void *new_entry)
+{
+ struct rb_node **p = &rblist->entries.rb_node;
+ struct rb_node *parent = NULL, *new_node;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+
+ rc = rblist->node_cmp(parent, new_entry);
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return -EEXIST;
+ }
+
+ new_node = rblist->node_new(rblist, new_entry);
+ if (new_node == NULL)
+ return -ENOMEM;
+
+ rb_link_node(new_node, parent, p);
+ rb_insert_color(new_node, &rblist->entries);
+ ++rblist->nr_entries;
+
+ return 0;
+}
+
+void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node)
+{
+ rb_erase(rb_node, &rblist->entries);
+ rblist->node_delete(rblist, rb_node);
+}
+
+struct rb_node *rblist__find(struct rblist *rblist, const void *entry)
+{
+ struct rb_node **p = &rblist->entries.rb_node;
+ struct rb_node *parent = NULL;
+
+ while (*p != NULL) {
+ int rc;
+
+ parent = *p;
+
+ rc = rblist->node_cmp(parent, entry);
+ if (rc > 0)
+ p = &(*p)->rb_left;
+ else if (rc < 0)
+ p = &(*p)->rb_right;
+ else
+ return parent;
+ }
+
+ return NULL;
+}
+
+void rblist__init(struct rblist *rblist)
+{
+ if (rblist != NULL) {
+ rblist->entries = RB_ROOT;
+ rblist->nr_entries = 0;
+ }
+
+ return;
+}
+
+void rblist__delete(struct rblist *rblist)
+{
+ if (rblist != NULL) {
+ struct rb_node *pos, *next = rb_first(&rblist->entries);
+
+ while (next) {
+ pos = next;
+ next = rb_next(pos);
+ rb_erase(pos, &rblist->entries);
+ rblist->node_delete(rblist, pos);
+ }
+ free(rblist);
+ }
+}
+
+struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx)
+{
+ struct rb_node *node;
+
+ for (node = rb_first(&rblist->entries); node; node = rb_next(node)) {
+ if (!idx--)
+ return node;
+ }
+
+ return NULL;
+}
diff --git a/tools/perf/util/rblist.h b/tools/perf/util/rblist.h
new file mode 100644
index 0000000..6d0cae5
--- /dev/null
+++ b/tools/perf/util/rblist.h
@@ -0,0 +1,47 @@
+#ifndef __PERF_RBLIST_H
+#define __PERF_RBLIST_H
+
+#include <linux/rbtree.h>
+#include <stdbool.h>
+
+/*
+ * create node structs of the form:
+ * struct my_node {
+ * struct rb_node rb_node;
+ * ... my data ...
+ * };
+ *
+ * create list structs of the form:
+ * struct mylist {
+ * struct rblist rblist;
+ * ... my data ...
+ * };
+ */
+
+struct rblist {
+ struct rb_root entries;
+ unsigned int nr_entries;
+
+ int (*node_cmp)(struct rb_node *rbn, const void *entry);
+ struct rb_node *(*node_new)(struct rblist *rlist, const void *new_entry);
+ void (*node_delete)(struct rblist *rblist, struct rb_node *rb_node);
+};
+
+void rblist__init(struct rblist *rblist);
+void rblist__delete(struct rblist *rblist);
+int rblist__add_node(struct rblist *rblist, const void *new_entry);
+void rblist__remove_node(struct rblist *rblist, struct rb_node *rb_node);
+struct rb_node *rblist__find(struct rblist *rblist, const void *entry);
+struct rb_node *rblist__entry(const struct rblist *rblist, unsigned int idx);
+
+static inline bool rblist__empty(const struct rblist *rblist)
+{
+ return rblist->nr_entries == 0;
+}
+
+static inline unsigned int rblist__nr_entries(const struct rblist *rblist)
+{
+ return rblist->nr_entries;
+}
+
+#endif /* __PERF_RBLIST_H */
diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c
index 8e4f075..2437fb0 100644
--- a/tools/perf/util/session.c
+++ b/tools/perf/util/session.c
@@ -80,14 +80,12 @@
return -1;
}
-void perf_session__update_sample_type(struct perf_session *self)
+void perf_session__set_id_hdr_size(struct perf_session *session)
{
- self->sample_type = perf_evlist__sample_type(self->evlist);
- self->sample_size = __perf_evsel__sample_size(self->sample_type);
- self->sample_id_all = perf_evlist__sample_id_all(self->evlist);
- self->id_hdr_size = perf_evlist__id_hdr_size(self->evlist);
- self->host_machine.id_hdr_size = self->id_hdr_size;
- machines__set_id_hdr_size(&self->machines, self->id_hdr_size);
+ u16 id_hdr_size = perf_evlist__id_hdr_size(session->evlist);
+
+ session->host_machine.id_hdr_size = id_hdr_size;
+ machines__set_id_hdr_size(&session->machines, id_hdr_size);
}
int perf_session__create_kernel_maps(struct perf_session *self)
@@ -147,7 +145,7 @@
if (mode == O_RDONLY) {
if (perf_session__open(self, force) < 0)
goto out_delete;
- perf_session__update_sample_type(self);
+ perf_session__set_id_hdr_size(self);
} else if (mode == O_WRONLY) {
/*
* In O_RDONLY mode this will be performed when reading the
@@ -158,7 +156,7 @@
}
if (tool && tool->ordering_requires_timestamps &&
- tool->ordered_samples && !self->sample_id_all) {
+ tool->ordered_samples && !perf_evlist__sample_id_all(self->evlist)) {
dump_printf("WARNING: No sample_id_all support, falling back to unordered processing\n");
tool->ordered_samples = false;
}
@@ -673,7 +671,8 @@
if (iter->timestamp > limit)
break;
- ret = perf_session__parse_sample(s, iter->event, &sample);
+ ret = perf_evlist__parse_sample(s->evlist, iter->event, &sample,
+ s->header.needs_swap);
if (ret)
pr_err("Can't parse sample, err = %d\n", ret);
else
@@ -865,16 +864,18 @@
union perf_event *event,
struct perf_sample *sample)
{
+ u64 sample_type = perf_evlist__sample_type(session->evlist);
+
if (event->header.type != PERF_RECORD_SAMPLE &&
- !session->sample_id_all) {
+ !perf_evlist__sample_id_all(session->evlist)) {
fputs("-1 -1 ", stdout);
return;
}
- if ((session->sample_type & PERF_SAMPLE_CPU))
+ if ((sample_type & PERF_SAMPLE_CPU))
printf("%u ", sample->cpu);
- if (session->sample_type & PERF_SAMPLE_TIME)
+ if (sample_type & PERF_SAMPLE_TIME)
printf("%" PRIu64 " ", sample->time);
}
@@ -899,6 +900,8 @@
static void dump_sample(struct perf_session *session, union perf_event *event,
struct perf_sample *sample)
{
+ u64 sample_type;
+
if (!dump_trace)
return;
@@ -906,10 +909,12 @@
event->header.misc, sample->pid, sample->tid, sample->ip,
sample->period, sample->addr);
- if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
+ sample_type = perf_evlist__sample_type(session->evlist);
+
+ if (sample_type & PERF_SAMPLE_CALLCHAIN)
callchain__printf(sample);
- if (session->sample_type & PERF_SAMPLE_BRANCH_STACK)
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK)
branch_stack__printf(sample);
}
@@ -1006,7 +1011,7 @@
union perf_event *event, struct perf_sample *sample)
{
if (event->header.type != PERF_RECORD_SAMPLE ||
- !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
+ !(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_CALLCHAIN))
return 0;
if (!ip_callchain__valid(sample->callchain, event)) {
@@ -1030,7 +1035,7 @@
case PERF_RECORD_HEADER_ATTR:
err = tool->attr(event, &session->evlist);
if (err == 0)
- perf_session__update_sample_type(session);
+ perf_session__set_id_hdr_size(session);
return err;
case PERF_RECORD_HEADER_EVENT_TYPE:
return tool->event_type(tool, event);
@@ -1065,7 +1070,7 @@
int ret;
if (session->header.needs_swap)
- event_swap(event, session->sample_id_all);
+ event_swap(event, perf_evlist__sample_id_all(session->evlist));
if (event->header.type >= PERF_RECORD_HEADER_MAX)
return -EINVAL;
@@ -1078,7 +1083,8 @@
/*
* For all kernel events we get the sample data
*/
- ret = perf_session__parse_sample(session, event, &sample);
+ ret = perf_evlist__parse_sample(session->evlist, event, &sample,
+ session->header.needs_swap);
if (ret)
return ret;
@@ -1389,9 +1395,9 @@
return err;
}
-bool perf_session__has_traces(struct perf_session *self, const char *msg)
+bool perf_session__has_traces(struct perf_session *session, const char *msg)
{
- if (!(self->sample_type & PERF_SAMPLE_RAW)) {
+ if (!(perf_evlist__sample_type(session->evlist) & PERF_SAMPLE_RAW)) {
pr_err("No trace sample to read. Did you call 'perf %s'?\n", msg);
return false;
}
diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h
index 7c435bd..1f7ec87 100644
--- a/tools/perf/util/session.h
+++ b/tools/perf/util/session.h
@@ -41,13 +41,9 @@
* perf.data file.
*/
struct hists hists;
- u64 sample_type;
- int sample_size;
int fd;
bool fd_pipe;
bool repipe;
- bool sample_id_all;
- u16 id_hdr_size;
int cwdlen;
char *cwd;
struct ordered_samples ordered_samples;
@@ -86,7 +82,7 @@
int perf_session__create_kernel_maps(struct perf_session *self);
-void perf_session__update_sample_type(struct perf_session *self);
+void perf_session__set_id_hdr_size(struct perf_session *session);
void perf_session__remove_thread(struct perf_session *self, struct thread *th);
static inline
@@ -130,24 +126,6 @@
size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp);
-static inline int perf_session__parse_sample(struct perf_session *session,
- const union perf_event *event,
- struct perf_sample *sample)
-{
- return perf_event__parse_sample(event, session->sample_type,
- session->sample_size,
- session->sample_id_all, sample,
- session->header.needs_swap);
-}
-
-static inline int perf_session__synthesize_sample(struct perf_session *session,
- union perf_event *event,
- const struct perf_sample *sample)
-{
- return perf_event__synthesize_sample(event, session->sample_type,
- sample, session->header.needs_swap);
-}
-
struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session,
unsigned int type);
diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c
index 6783a20..95856ff 100644
--- a/tools/perf/util/strlist.c
+++ b/tools/perf/util/strlist.c
@@ -10,23 +10,28 @@
#include <stdlib.h>
#include <string.h>
-static struct str_node *str_node__new(const char *s, bool dupstr)
+static
+struct rb_node *strlist__node_new(struct rblist *rblist, const void *entry)
{
- struct str_node *self = malloc(sizeof(*self));
+ const char *s = entry;
+ struct rb_node *rc = NULL;
+ struct strlist *strlist = container_of(rblist, struct strlist, rblist);
+ struct str_node *snode = malloc(sizeof(*snode));
- if (self != NULL) {
- if (dupstr) {
+ if (snode != NULL) {
+ if (strlist->dupstr) {
s = strdup(s);
if (s == NULL)
goto out_delete;
}
- self->s = s;
+ snode->s = s;
+ rc = &snode->rb_node;
}
- return self;
+ return rc;
out_delete:
- free(self);
+ free(snode);
return NULL;
}
@@ -37,36 +42,26 @@
free(self);
}
+static
+void strlist__node_delete(struct rblist *rblist, struct rb_node *rb_node)
+{
+ struct strlist *slist = container_of(rblist, struct strlist, rblist);
+ struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
+
+ str_node__delete(snode, slist->dupstr);
+}
+
+static int strlist__node_cmp(struct rb_node *rb_node, const void *entry)
+{
+ const char *str = entry;
+ struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
+
+ return strcmp(snode->s, str);
+}
+
int strlist__add(struct strlist *self, const char *new_entry)
{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
- struct str_node *sn;
-
- while (*p != NULL) {
- int rc;
-
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, new_entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return -EEXIST;
- }
-
- sn = str_node__new(new_entry, self->dupstr);
- if (sn == NULL)
- return -ENOMEM;
-
- rb_link_node(&sn->rb_node, parent, p);
- rb_insert_color(&sn->rb_node, &self->entries);
- ++self->nr_entries;
-
- return 0;
+ return rblist__add_node(&self->rblist, new_entry);
}
int strlist__load(struct strlist *self, const char *filename)
@@ -96,34 +91,20 @@
return err;
}
-void strlist__remove(struct strlist *self, struct str_node *sn)
+void strlist__remove(struct strlist *slist, struct str_node *snode)
{
- rb_erase(&sn->rb_node, &self->entries);
- str_node__delete(sn, self->dupstr);
+ str_node__delete(snode, slist->dupstr);
}
-struct str_node *strlist__find(struct strlist *self, const char *entry)
+struct str_node *strlist__find(struct strlist *slist, const char *entry)
{
- struct rb_node **p = &self->entries.rb_node;
- struct rb_node *parent = NULL;
+ struct str_node *snode = NULL;
+ struct rb_node *rb_node = rblist__find(&slist->rblist, entry);
- while (*p != NULL) {
- struct str_node *sn;
- int rc;
+ if (rb_node)
+ snode = container_of(rb_node, struct str_node, rb_node);
- parent = *p;
- sn = rb_entry(parent, struct str_node, rb_node);
- rc = strcmp(sn->s, entry);
-
- if (rc > 0)
- p = &(*p)->rb_left;
- else if (rc < 0)
- p = &(*p)->rb_right;
- else
- return sn;
- }
-
- return NULL;
+ return snode;
}
static int strlist__parse_list_entry(struct strlist *self, const char *s)
@@ -156,9 +137,12 @@
struct strlist *self = malloc(sizeof(*self));
if (self != NULL) {
- self->entries = RB_ROOT;
+ rblist__init(&self->rblist);
+ self->rblist.node_cmp = strlist__node_cmp;
+ self->rblist.node_new = strlist__node_new;
+ self->rblist.node_delete = strlist__node_delete;
+
self->dupstr = dupstr;
- self->nr_entries = 0;
if (slist && strlist__parse_list(self, slist) != 0)
goto out_error;
}
@@ -171,30 +155,18 @@
void strlist__delete(struct strlist *self)
{
- if (self != NULL) {
- struct str_node *pos;
- struct rb_node *next = rb_first(&self->entries);
-
- while (next) {
- pos = rb_entry(next, struct str_node, rb_node);
- next = rb_next(&pos->rb_node);
- strlist__remove(self, pos);
- }
- self->entries = RB_ROOT;
- free(self);
- }
+ if (self != NULL)
+ rblist__delete(&self->rblist);
}
-struct str_node *strlist__entry(const struct strlist *self, unsigned int idx)
+struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx)
{
- struct rb_node *nd;
+ struct str_node *snode = NULL;
+ struct rb_node *rb_node;
- for (nd = rb_first(&self->entries); nd; nd = rb_next(nd)) {
- struct str_node *pos = rb_entry(nd, struct str_node, rb_node);
+ rb_node = rblist__entry(&slist->rblist, idx);
+ if (rb_node)
+ snode = container_of(rb_node, struct str_node, rb_node);
- if (!idx--)
- return pos;
- }
-
- return NULL;
+ return snode;
}
diff --git a/tools/perf/util/strlist.h b/tools/perf/util/strlist.h
index 3ba8390..dd9f922 100644
--- a/tools/perf/util/strlist.h
+++ b/tools/perf/util/strlist.h
@@ -4,14 +4,15 @@
#include <linux/rbtree.h>
#include <stdbool.h>
+#include "rblist.h"
+
struct str_node {
struct rb_node rb_node;
const char *s;
};
struct strlist {
- struct rb_root entries;
- unsigned int nr_entries;
+ struct rblist rblist;
bool dupstr;
};
@@ -32,18 +33,18 @@
static inline bool strlist__empty(const struct strlist *self)
{
- return self->nr_entries == 0;
+ return rblist__empty(&self->rblist);
}
static inline unsigned int strlist__nr_entries(const struct strlist *self)
{
- return self->nr_entries;
+ return rblist__nr_entries(&self->rblist);
}
/* For strlist iteration */
static inline struct str_node *strlist__first(struct strlist *self)
{
- struct rb_node *rn = rb_first(&self->entries);
+ struct rb_node *rn = rb_first(&self->rblist.entries);
return rn ? rb_entry(rn, struct str_node, rb_node) : NULL;
}
static inline struct str_node *strlist__next(struct str_node *sn)
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index fdad4eee..8b63b67 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -64,7 +64,7 @@
DSO_BINARY_TYPE__NOT_FOUND,
};
-#define DSO_BINARY_TYPE__SYMTAB_CNT sizeof(binary_type_symtab)
+#define DSO_BINARY_TYPE__SYMTAB_CNT ARRAY_SIZE(binary_type_symtab)
static enum dso_binary_type binary_type_data[] = {
DSO_BINARY_TYPE__BUILD_ID_CACHE,
@@ -72,7 +72,7 @@
DSO_BINARY_TYPE__NOT_FOUND,
};
-#define DSO_BINARY_TYPE__DATA_CNT sizeof(binary_type_data)
+#define DSO_BINARY_TYPE__DATA_CNT ARRAY_SIZE(binary_type_data)
int dso__name_len(const struct dso *dso)
{
@@ -2875,6 +2875,7 @@
int i, items = 0;
char path[PATH_MAX];
pid_t pid;
+ char *endp;
if (symbol_conf.default_guest_vmlinux_name ||
symbol_conf.default_guest_modules ||
@@ -2891,7 +2892,14 @@
/* Filter out . and .. */
continue;
}
- pid = atoi(namelist[i]->d_name);
+ pid = (pid_t)strtol(namelist[i]->d_name, &endp, 10);
+ if ((*endp != '\0') ||
+ (endp == namelist[i]->d_name) ||
+ (errno == ERANGE)) {
+ pr_debug("invalid directory (%s). Skipping.\n",
+ namelist[i]->d_name);
+ continue;
+ }
sprintf(path, "%s/%s/proc/kallsyms",
symbol_conf.guestmount,
namelist[i]->d_name);
diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c
index 3f59c49..051eaa6 100644
--- a/tools/perf/util/target.c
+++ b/tools/perf/util/target.c
@@ -110,7 +110,7 @@
int idx;
const char *msg;
- BUG_ON(buflen > 0);
+ BUG_ON(buflen == 0);
if (errnum >= 0) {
const char *err = strerror_r(errnum, buf, buflen);
diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c
index b0adb27..68d0734 100644
--- a/tools/usb/testusb.c
+++ b/tools/usb/testusb.c
@@ -253,9 +253,6 @@
if (flag != FTW_F)
return 0;
- /* ignore /proc/bus/usb/{devices,drivers} */
- if (strrchr(name, '/')[1] == 'd')
- return 0;
fd = fopen(name, "rb");
if (!fd) {
@@ -356,28 +353,8 @@
static const char *usbfs_dir_find(void)
{
- static char usbfs_path_0[] = "/dev/usb/devices";
- static char usbfs_path_1[] = "/proc/bus/usb/devices";
static char udev_usb_path[] = "/dev/bus/usb";
- static char *const usbfs_paths[] = {
- usbfs_path_0, usbfs_path_1
- };
-
- static char *const *
- end = usbfs_paths + sizeof usbfs_paths / sizeof *usbfs_paths;
-
- char *const *it = usbfs_paths;
- do {
- int fd = open(*it, O_RDONLY);
- close(fd);
- if (fd >= 0) {
- strrchr(*it, '/')[0] = '\0';
- return *it;
- }
- } while (++it != end);
-
- /* real device-nodes managed by udev */
if (access(udev_usb_path, F_OK) == 0)
return udev_usb_path;
@@ -489,7 +466,7 @@
goto usage;
if (!all && !device) {
fprintf (stderr, "must specify '-a' or '-D dev', "
- "or DEVICE=/proc/bus/usb/BBB/DDD in env\n");
+ "or DEVICE=/dev/bus/usb/BBB/DDD in env\n");
goto usage;
}