Merge branch 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Oct 2012 17:16:42 +0000 (10:16 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 1 Oct 2012 17:16:42 +0000 (10:16 -0700)
Pull RCU changes from Ingo Molnar:

 0. 'idle RCU':

     Adds RCU APIs that allow non-idle tasks to enter RCU idle mode and
     provides x86 code to make use of them, allowing RCU to treat
     user-mode execution as an extended quiescent state when the new
     RCU_USER_QS kernel configuration parameter is specified.  (Work is
     in progress to port this to a few other architectures, but is not
     part of this series.)

 1.  A fix for a latent bug that has been in RCU ever since the addition
     of CPU stall warnings.  This bug results in false-positive stall
     warnings, but thus far only on embedded systems with severely
     cut-down userspace configurations.

 2.  Further reductions in latency spikes for huge systems, along with
     additional boot-time adaptation to the actual hardware.

     This is a large change, as it moves RCU grace-period initialization
     and cleanup, along with quiescent-state forcing, from softirq to a
     kthread.  However, it appears to be in quite good shape (famous
     last words).

 3.  Updates to documentation and rcutorture, the latter category
     including keeping statistics on CPU-hotplug latencies and fixing
     some initialization-time races.

 4.  CPU-hotplug fixes and improvements.

 5.  Idle-loop fixes that were omitted on an earlier submission.

 6.  Miscellaneous fixes and improvements

In certain RCU configurations new kernel threads will show up (rcu_bh,
rcu_sched), showing RCU processing overhead.

* 'core-rcu-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (90 commits)
  rcu: Apply micro-optimization and int/bool fixes to RCU's idle handling
  rcu: Userspace RCU extended QS selftest
  x86: Exit RCU extended QS on notify resume
  x86: Use the new schedule_user API on userspace preemption
  rcu: Exit RCU extended QS on user preemption
  rcu: Exit RCU extended QS on kernel preemption after irq/exception
  x86: Exception hooks for userspace RCU extended QS
  x86: Unspaghettize do_general_protection()
  x86: Syscall hooks for userspace RCU extended QS
  rcu: Switch task's syscall hooks on context switch
  rcu: Ignore userspace extended quiescent state by default
  rcu: Allow rcu_user_enter()/exit() to nest
  rcu: Settle config for userspace extended quiescent state
  rcu: Make RCU_FAST_NO_HZ handle adaptive ticks
  rcu: New rcu_user_enter_after_irq() and rcu_user_exit_after_irq() APIs
  rcu: New rcu_user_enter() and rcu_user_exit() APIs
  ia64: Add missing RCU idle APIs on idle loop
  xtensa: Add missing RCU idle APIs on idle loop
  score: Add missing RCU idle APIs on idle loop
  parisc: Add missing RCU idle APIs on idle loop
  ...

311 files changed:
Documentation/ABI/testing/sysfs-class-regulator
Documentation/ABI/testing/sysfs-driver-wacom
Documentation/accounting/getdelays.c
Documentation/devicetree/bindings/regulator/regulator.txt
Documentation/devicetree/bindings/regulator/tps65217.txt
Documentation/devicetree/bindings/regulator/tps6586x.txt
Documentation/dontdiff
Documentation/ia64/aliasing-test.c
Documentation/power/swsusp.txt
Documentation/trace/kprobetrace.txt
Documentation/vfio.txt
MAINTAINERS
Makefile
arch/arm/mach-mxs/mach-mxs.c
arch/arm/mach-orion5x/common.c
arch/arm/mach-tegra/board-harmony-power.c
arch/arm/mm/dma-mapping.c
arch/c6x/include/asm/Kbuild
arch/c6x/include/asm/barrier.h [deleted file]
arch/powerpc/boot/.gitignore
arch/tile/include/gxio/iorpc_trio.h
arch/um/include/asm/processor-generic.h
arch/um/include/shared/common-offsets.h
arch/um/include/shared/user.h
arch/um/kernel/exec.c
arch/um/kernel/process.c
arch/um/kernel/signal.c
arch/um/kernel/syscall.c
arch/um/scripts/Makefile.rules
arch/x86/include/asm/hpet.h
arch/x86/um/Kconfig
arch/x86/um/shared/sysdep/kernel-offsets.h
arch/x86/um/shared/sysdep/syscalls.h
arch/x86/um/signal.c
arch/x86/um/sys_call_table_32.c
arch/x86/um/syscalls_32.c
arch/x86/um/syscalls_64.c
arch/x86/xen/setup.c
drivers/base/regmap/regmap-irq.c
drivers/base/regmap/regmap.c
drivers/block/nvme.c
drivers/block/rbd.c
drivers/dma/at_hdmac.c
drivers/dma/ep93xx_dma.c
drivers/dma/fsldma.c
drivers/dma/imx-dma.c
drivers/dma/intel_mid_dma.c
drivers/dma/intel_mid_dma_regs.h
drivers/dma/ioat/hw.h
drivers/dma/pl330.c
drivers/dma/ppc4xx/adma.c
drivers/dma/ste_dma40_ll.h
drivers/edac/i3200_edac.c
drivers/edac/i5000_edac.c
drivers/edac/sb_edac.c
drivers/extcon/extcon-arizona.c
drivers/gpio/gpio-lpc32xx.c
drivers/gpu/drm/nouveau/nouveau_abi16.c
drivers/gpu/drm/nouveau/nvc0_fb.c
drivers/gpu/drm/nouveau/nvc0_fifo.c
drivers/gpu/drm/nouveau/nve0_fifo.c
drivers/gpu/drm/udl/udl_connector.c
drivers/gpu/drm/vmwgfx/vmwgfx_fence.c
drivers/hid/Kconfig
drivers/hid/Makefile
drivers/hid/hid-a4tech.c
drivers/hid/hid-apple.c
drivers/hid/hid-aureal.c
drivers/hid/hid-belkin.c
drivers/hid/hid-cherry.c
drivers/hid/hid-core.c
drivers/hid/hid-cypress.c
drivers/hid/hid-debug.c
drivers/hid/hid-ezkey.c
drivers/hid/hid-gyration.c
drivers/hid/hid-holtekff.c
drivers/hid/hid-ids.h
drivers/hid/hid-input.c
drivers/hid/hid-lcpower.c
drivers/hid/hid-lenovo-tpkbd.c
drivers/hid/hid-lg.c
drivers/hid/hid-lg.h
drivers/hid/hid-lg4ff.c
drivers/hid/hid-magicmouse.c
drivers/hid/hid-microsoft.c
drivers/hid/hid-monterey.c
drivers/hid/hid-multitouch.c
drivers/hid/hid-ntrig.c
drivers/hid/hid-petalynx.c
drivers/hid/hid-picolcd.c [deleted file]
drivers/hid/hid-picolcd.h [new file with mode: 0644]
drivers/hid/hid-picolcd_backlight.c [new file with mode: 0644]
drivers/hid/hid-picolcd_cir.c [new file with mode: 0644]
drivers/hid/hid-picolcd_core.c [new file with mode: 0644]
drivers/hid/hid-picolcd_debugfs.c [new file with mode: 0644]
drivers/hid/hid-picolcd_fb.c [new file with mode: 0644]
drivers/hid/hid-picolcd_lcd.c [new file with mode: 0644]
drivers/hid/hid-picolcd_leds.c [new file with mode: 0644]
drivers/hid/hid-primax.c
drivers/hid/hid-prodikeys.c
drivers/hid/hid-ps3remote.c [new file with mode: 0644]
drivers/hid/hid-samsung.c
drivers/hid/hid-sony.c
drivers/hid/hid-sunplus.c
drivers/hid/hid-uclogic.c
drivers/hid/hid-wacom.c
drivers/hid/hid-waltop.c
drivers/hid/hid-wiimote-ext.c
drivers/hid/hidraw.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/hid-quirks.c
drivers/input/evdev.c
drivers/input/input-mt.c
drivers/input/input.c
drivers/input/misc/uinput.c
drivers/input/mouse/alps.c
drivers/input/mouse/bcm5974.c
drivers/input/mouse/elantech.c
drivers/input/mouse/sentelic.c
drivers/input/mouse/synaptics.c
drivers/input/tablet/wacom_wac.c
drivers/input/touchscreen/atmel_mxt_ts.c
drivers/input/touchscreen/cyttsp_core.c
drivers/input/touchscreen/edt-ft5x06.c
drivers/input/touchscreen/egalax_ts.c
drivers/input/touchscreen/ili210x.c
drivers/input/touchscreen/mms114.c
drivers/input/touchscreen/penmount.c
drivers/input/touchscreen/wacom_w8001.c
drivers/iommu/amd_iommu.c
drivers/md/dm-mpath.c
drivers/md/dm-table.c
drivers/md/dm-thin.c
drivers/md/dm-verity.c
drivers/md/dm.c
drivers/md/dm.h
drivers/md/raid10.c
drivers/md/raid5.c
drivers/mfd/ab8500-gpadc.c
drivers/mfd/rc5t583.c
drivers/mfd/rdc321x-southbridge.c
drivers/mfd/tps6586x.c
drivers/mfd/tps65911-comparator.c
drivers/mfd/wm8994-irq.c
drivers/mmc/core/sdio.c
drivers/mmc/host/at91_mci.c
drivers/mmc/host/atmel-mci.c
drivers/mmc/host/omap_hsmmc.c
drivers/mmc/host/sdhci-esdhc-imx.c
drivers/mmc/host/vub300.c
drivers/mtd/mtdchar.c
drivers/net/ethernet/3com/typhoon.c
drivers/net/ethernet/broadcom/bnx2.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sp.h
drivers/net/ethernet/octeon/octeon_mgmt.c
drivers/net/ethernet/pasemi/pasemi_mac.c
drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c
drivers/net/phy/bcm87xx.c
drivers/net/phy/micrel.c
drivers/net/phy/smsc.c
drivers/net/ppp/pppoe.c
drivers/net/team/team.c
drivers/net/usb/smsc75xx.c
drivers/net/wireless/b43/Kconfig
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/rtlwifi/rtl8192c/fw_common.c
drivers/net/wireless/rtlwifi/rtl8192de/fw.c
drivers/pci/.gitignore [deleted file]
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/aat2870-regulator.c
drivers/regulator/ab3100.c
drivers/regulator/ab8500.c
drivers/regulator/arizona-ldo1.c
drivers/regulator/arizona-micsupp.c
drivers/regulator/core.c
drivers/regulator/da9052-regulator.c
drivers/regulator/dummy.c
drivers/regulator/fan53555.c [new file with mode: 0644]
drivers/regulator/isl6271a-regulator.c
drivers/regulator/lp872x.c
drivers/regulator/lp8788-buck.c
drivers/regulator/lp8788-ldo.c
drivers/regulator/max77686.c
drivers/regulator/max8907-regulator.c [new file with mode: 0644]
drivers/regulator/mc13783-regulator.c
drivers/regulator/mc13892-regulator.c
drivers/regulator/mc13xxx-regulator-core.c
drivers/regulator/mc13xxx.h
drivers/regulator/of_regulator.c
drivers/regulator/palmas-regulator.c
drivers/regulator/s2mps11.c
drivers/regulator/tps6524x-regulator.c
drivers/regulator/tps6586x-regulator.c
drivers/regulator/twl-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8400-regulator.c
drivers/scsi/aic7xxx/aic79xx_core.c
drivers/scsi/bfa/bfa_ioc.c
drivers/scsi/bfa/bfa_ioc.h
drivers/scsi/bnx2fc/bnx2fc_fcoe.c
drivers/scsi/gdth.h
drivers/scsi/ipr.c
drivers/scsi/isci/host.c
drivers/scsi/isci/init.c
drivers/scsi/isci/port.c
drivers/scsi/isci/request.c
drivers/scsi/isci/task.c
drivers/scsi/lpfc/lpfc_init.c
drivers/scsi/lpfc/lpfc_sli.c
drivers/scsi/megaraid.c
drivers/scsi/megaraid.h
drivers/scsi/mvumi.c
drivers/scsi/qla4xxx/ql4_os.c
drivers/scsi/vmw_pvscsi.c
drivers/sh/pfc/pinctrl.c
drivers/spi/spi-au1550.c
drivers/spi/spi-bfin-sport.c
drivers/spi/spi-oc-tiny.c
drivers/spi/spi-ppc4xx.c
drivers/spi/spi-topcliff-pch.c
drivers/target/iscsi/iscsi_target_parameters.c
drivers/usb/core/devices.c
drivers/usb/core/hcd.c
drivers/usb/host/ohci-at91.c
drivers/vfio/pci/vfio_pci_intrs.c
drivers/video/backlight/88pm860x_bl.c
drivers/video/exynos/exynos_mipi_dsi.c
drivers/video/tmiofb.c
drivers/w1/masters/ds1wm.c
fs/btrfs/ctree.h
fs/btrfs/delayed-ref.h
fs/btrfs/inode.c
fs/dcache.c
fs/ext2/balloc.c
fs/ext3/balloc.c
fs/ext3/inode.c
fs/ext4/inode.c
fs/ext4/mballoc.c
fs/fs-writeback.c
fs/gfs2/aops.c
fs/gfs2/bmap.c
fs/gfs2/file.c
fs/gfs2/glock.c
fs/gfs2/glops.c
fs/gfs2/incore.h
fs/gfs2/inode.c
fs/gfs2/ops_fstype.c
fs/gfs2/quota.c
fs/gfs2/rgrp.c
fs/gfs2/rgrp.h
fs/gfs2/super.c
fs/gfs2/trace_gfs2.h
fs/gfs2/trans.h
fs/gfs2/xattr.c
fs/libfs.c
fs/lockd/svclock.c
fs/namespace.c
fs/nfs/super.c
include/asm-generic/unistd.h
include/linux/hid.h
include/linux/input.h
include/linux/input/mt.h
include/linux/iommu.h
include/linux/irqdesc.h
include/linux/mfd/max77686.h
include/linux/mfd/max8998.h
include/linux/mfd/tps6586x.h
include/linux/micrel_phy.h
include/linux/nvme.h
include/linux/regmap.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/regulator/fan53555.h [new file with mode: 0644]
include/linux/regulator/machine.h
include/linux/security.h
include/trace/define_trace.h
lib/flex_proportions.c
mm/bootmem.c
mm/huge_memory.c
net/8021q/vlanproc.c
net/batman-adv/bat_iv_ogm.c
net/batman-adv/soft-interface.c
net/bluetooth/hci_core.c
net/bluetooth/l2cap_core.c
net/bluetooth/mgmt.c
net/ceph/messenger.c
net/core/sock.c
net/ipv4/inetpeer.c
net/ipv4/raw.c
net/ipv6/mip6.c
net/ipv6/raw.c
net/l2tp/l2tp_netlink.c
net/netfilter/xt_limit.c
net/wireless/reg.c
scripts/checksyscalls.sh
scripts/coccinelle/api/memdup_user.cocci
scripts/kconfig/streamline_config.pl
security/apparmor/.gitignore
sound/oss/.gitignore
sound/soc/codecs/wm2000.c
sound/usb/endpoint.c
tools/perf/util/callchain.h
tools/perf/util/parse-events-test.c
tools/perf/util/python-ext-sources
tools/testing/ktest/examples/include/defaults.conf
tools/testing/ktest/examples/include/tests.conf
tools/testing/ktest/ktest.pl
tools/testing/selftests/vm/run_vmtests

index e091fa8..bc578bc 100644 (file)
@@ -349,3 +349,24 @@ Description:
 
                This will be one of the same strings reported by
                the "state" attribute.
+
+What:          /sys/class/regulator/.../bypass
+Date:          September 2012
+KernelVersion: 3.7
+Contact:       Mark Brown <broonie@opensource.wolfsonmicro.com>
+Description:
+               Some regulator directories will contain a field called
+               bypass.  This indicates if the device is in bypass mode.
+
+               This will be one of the following strings:
+
+               'enabled'
+               'disabled'
+               'unknown'
+
+               'enabled' means the regulator is in bypass mode.
+
+               'disabled' means that the regulator is regulating.
+
+               'unknown' means software cannot determine the state, or
+               the reported state is invalid.
index 8d55a83..7fc7810 100644 (file)
@@ -1,3 +1,16 @@
+WWhat:         /sys/class/hidraw/hidraw*/device/oled*_img
+Date:          June 2012
+Contact:       linux-bluetooth@vger.kernel.org
+Description:
+               The /sys/class/hidraw/hidraw*/device/oled*_img files control
+               OLED mocro displays on Intuos4 Wireless tablet. Accepted image
+               has to contain 256 bytes (64x32 px 1 bit colour). The format
+               is the same as PBM image 62x32px without header (64 bits per
+               horizontal line, 32 lines). An example of setting OLED No. 0:
+               dd bs=256 count=1 if=img_file of=[path to oled0_img]/oled0_img
+               The attribute is read only and no local copy of the image is
+               stored.
+
 What:          /sys/class/hidraw/hidraw*/device/speed
 Date:          April 2010
 Kernel Version:        2.6.35
index f6318f6..6f706ac 100644 (file)
@@ -98,10 +98,9 @@ static int create_nl_socket(int protocol)
        if (rcvbufsz)
                if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF,
                                &rcvbufsz, sizeof(rcvbufsz)) < 0) {
-                       fprintf(stderr, "Unable to set socket rcv buf size "
-                                       "to %d\n",
+                       fprintf(stderr, "Unable to set socket rcv buf size to %d\n",
                                rcvbufsz);
-                       return -1;
+                       goto error;
                }
 
        memset(&local, 0, sizeof(local));
index 66ece3f..ecfc6cc 100644 (file)
@@ -11,10 +11,13 @@ Optional properties:
 - regulator-boot-on: bootloader/firmware enabled regulator
 - <name>-supply: phandle to the parent supply/regulator node
 - regulator-ramp-delay: ramp delay for regulator(in uV/uS)
+
+Deprecated properties:
 - regulator-compatible: If a regulator chip contains multiple
   regulators, and if the chip's binding contains a child node that
   describes each regulator, then this property indicates which regulator
-  this child node is intended to configure.
+  this child node is intended to configure. If this property is missing,
+  the node's name will be used instead.
 
 Example:
 
index 0487e96..d316fb8 100644 (file)
@@ -22,66 +22,49 @@ Example:
                compatible = "ti,tps65217";
 
                regulators {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
-
-                       dcdc1_reg: regulator@0 {
-                               reg = <0>;
-                               regulator-compatible = "dcdc1";
+                       dcdc1_reg: dcdc1 {
                                regulator-min-microvolt = <900000>;
                                regulator-max-microvolt = <1800000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       dcdc2_reg: regulator@1 {
-                               reg = <1>;
-                               regulator-compatible = "dcdc2";
+                       dcdc2_reg: dcdc2 {
                                regulator-min-microvolt = <900000>;
                                regulator-max-microvolt = <3300000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       dcdc3_reg: regulator@2 {
-                               reg = <2>;
-                               regulator-compatible = "dcdc3";
+                       dcdc3_reg: dcc3 {
                                regulator-min-microvolt = <900000>;
                                regulator-max-microvolt = <1500000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       ldo1_reg: regulator@3 {
-                               reg = <3>;
-                               regulator-compatible = "ldo1";
+                       ldo1_reg: ldo1 {
                                regulator-min-microvolt = <1000000>;
                                regulator-max-microvolt = <3300000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       ldo2_reg: regulator@4 {
-                               reg = <4>;
-                               regulator-compatible = "ldo2";
+                       ldo2_reg: ldo2 {
                                regulator-min-microvolt = <900000>;
                                regulator-max-microvolt = <3300000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       ldo3_reg: regulator@5 {
-                               reg = <5>;
-                               regulator-compatible = "ldo3";
+                       ldo3_reg: ldo3 {
                                regulator-min-microvolt = <1800000>;
                                regulator-max-microvolt = <3300000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       ldo4_reg: regulator@6 {
-                               reg = <6>;
-                               regulator-compatible = "ldo4";
+                       ldo4_reg: ldo4 {
                                regulator-min-microvolt = <1800000>;
                                regulator-max-microvolt = <3300000>;
                                regulator-boot-on;
index da80c2a..07b9ef6 100644 (file)
@@ -6,9 +6,13 @@ Required properties:
 - interrupts: the interrupt outputs of the controller
 - #gpio-cells: number of cells to describe a GPIO
 - gpio-controller: mark the device as a GPIO controller
-- 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
+- regulators: A node that houses a sub-node for each regulator within the
+  device. Each sub-node is identified using the node's name (or the deprecated
+  regulator-compatible property if present), with valid values listed below.
+  The content of each sub-node is defined by the standard binding for
+  regulators; see regulator.txt.
+  sys, sm[0-2], ldo[0-9] and ldo_rtc
+- sys-supply: The input supply for SYS.
 - 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.
@@ -20,6 +24,9 @@ Required properties:
 
 Each regulator is defined using the standard binding for regulators.
 
+Note: LDO5 and LDO_RTC is supplied by SYS regulator internally and driver
+      take care of making proper parent child relationship.
+
 Example:
 
        pmu: tps6586x@34 {
@@ -30,6 +37,7 @@ Example:
                #gpio-cells = <2>;
                gpio-controller;
 
+               sys-supply = <&some_reg>;
                vin-sm0-supply = <&some_reg>;
                vin-sm1-supply = <&some_reg>;
                vin-sm2-supply = <&some_reg>;
@@ -40,103 +48,80 @@ Example:
                vinldo9-supply = <...>;
 
                regulators {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
+                       sys_reg: sys {
+                               regulator-name = "vdd_sys";
+                               regulator-boot-on;
+                               regulator-always-on;
+                       };
 
-                       sm0_reg: regulator@0 {
-                               reg = <0>;
-                               regulator-compatible = "sm0";
+                       sm0_reg: sm0 {
                                regulator-min-microvolt = < 725000>;
                                regulator-max-microvolt = <1500000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       sm1_reg: regulator@1 {
-                               reg = <1>;
-                               regulator-compatible = "sm1";
+                       sm1_reg: sm1 {
                                regulator-min-microvolt = < 725000>;
                                regulator-max-microvolt = <1500000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       sm2_reg: regulator@2 {
-                               reg = <2>;
-                               regulator-compatible = "sm2";
+                       sm2_reg: sm2 {
                                regulator-min-microvolt = <3000000>;
                                regulator-max-microvolt = <4550000>;
                                regulator-boot-on;
                                regulator-always-on;
                        };
 
-                       ldo0_reg: regulator@3 {
-                               reg = <3>;
-                               regulator-compatible = "ldo0";
+                       ldo0_reg: ldo0 {
                                regulator-name = "PCIE CLK";
                                regulator-min-microvolt = <3300000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo1_reg: regulator@4 {
-                               reg = <4>;
-                               regulator-compatible = "ldo1";
+                       ldo1_reg: ldo1 {
                                regulator-min-microvolt = < 725000>;
                                regulator-max-microvolt = <1500000>;
                        };
 
-                       ldo2_reg: regulator@5 {
-                               reg = <5>;
-                               regulator-compatible = "ldo2";
+                       ldo2_reg: ldo2 {
                                regulator-min-microvolt = < 725000>;
                                regulator-max-microvolt = <1500000>;
                        };
 
-                       ldo3_reg: regulator@6 {
-                               reg = <6>;
-                               regulator-compatible = "ldo3";
+                       ldo3_reg: ldo3 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo4_reg: regulator@7 {
-                               reg = <7>;
-                               regulator-compatible = "ldo4";
+                       ldo4_reg: ldo4 {
                                regulator-min-microvolt = <1700000>;
                                regulator-max-microvolt = <2475000>;
                        };
 
-                       ldo5_reg: regulator@8 {
-                               reg = <8>;
-                               regulator-compatible = "ldo5";
+                       ldo5_reg: ldo5 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo6_reg: regulator@9 {
-                               reg = <9>;
-                               regulator-compatible = "ldo6";
+                       ldo6_reg: ldo6 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo7_reg: regulator@10 {
-                               reg = <10>;
-                               regulator-compatible = "ldo7";
+                       ldo7_reg: ldo7 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo8_reg: regulator@11 {
-                               reg = <11>;
-                               regulator-compatible = "ldo8";
+                       ldo8_reg: ldo8 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
 
-                       ldo9_reg: regulator@12 {
-                               reg = <12>;
-                               regulator-compatible = "ldo9";
+                       ldo9_reg: ldo9 {
                                regulator-min-microvolt = <1250000>;
                                regulator-max-microvolt = <3300000>;
                        };
index 39462cf..74c25c8 100644 (file)
@@ -162,7 +162,6 @@ mach-types.h
 machtypes.h
 map
 map_hugetlb
-maui_boot.h
 media
 mconf
 miboot*
index 5caa2af..62a190d 100644 (file)
@@ -132,6 +132,7 @@ static int read_rom(char *path)
 
        rc = write(fd, "1", 2);
        if (rc <= 0) {
+               close(fd);
                perror("write");
                return -1;
        }
index 92341b8..0b4b63e 100644 (file)
@@ -53,7 +53,7 @@ before suspend (it is limited to 500 MB by default).
 
 Article about goals and implementation of Software Suspend for Linux
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Author: G\82ábor Kuti
+Author: Gábor Kuti
 Last revised: 2003-10-20 by Pavel Machek
 
 Idea and goals to achieve
index d0d0bb9..d68ea5f 100644 (file)
@@ -12,7 +12,7 @@ kprobes can probe (this means, all functions body except for __kprobes
 functions). Unlike the Tracepoint based event, this can be added and removed
 dynamically, on the fly.
 
-To enable this feature, build your kernel with CONFIG_KPROBE_TRACING=y.
+To enable this feature, build your kernel with CONFIG_KPROBE_EVENT=y.
 
 Similar to the events tracer, this doesn't need to be activated via
 current_tracer. Instead of that, add probe points via
index 0cb6685..8eda363 100644 (file)
@@ -133,7 +133,7 @@ character devices for this group:
 $ lspci -n -s 0000:06:0d.0
 06:0d.0 0401: 1102:0002 (rev 08)
 # echo 0000:06:0d.0 > /sys/bus/pci/devices/0000:06:0d.0/driver/unbind
-# echo 1102 0002 > /sys/bus/pci/drivers/vfio/new_id
+# echo 1102 0002 > /sys/bus/pci/drivers/vfio-pci/new_id
 
 Now we need to look at what other devices are in the group to free
 it for use by VFIO:
index b17587d..9362f54 100644 (file)
@@ -3552,11 +3552,12 @@ K:      \b(ABS|SYN)_MT_
 
 INTEL C600 SERIES SAS CONTROLLER DRIVER
 M:     Intel SCU Linux support <intel-linux-scu@intel.com>
+M:     Lukasz Dorau <lukasz.dorau@intel.com>
+M:     Maciej Patelczyk <maciej.patelczyk@intel.com>
 M:     Dave Jiang <dave.jiang@intel.com>
-M:     Ed Nadolski <edmund.nadolski@intel.com>
 L:     linux-scsi@vger.kernel.org
-T:     git git://git.kernel.org/pub/scm/linux/kernel/git/djbw/isci.git
-S:     Maintained
+T:     git git://git.code.sf.net/p/intel-sas/isci
+S:     Supported
 F:     drivers/scsi/isci/
 F:     firmware/isci/
 
@@ -5321,6 +5322,12 @@ L:       linux-mtd@lists.infradead.org
 S:     Maintained
 F:     drivers/mtd/devices/phram.c
 
+PICOLCD HID DRIVER
+M:     Bruno Prémont <bonbons@linux-vserver.org>
+L:     linux-input@vger.kernel.org
+S:     Maintained
+F:     drivers/hid/hid-picolcd*
+
 PICOXCELL SUPPORT
 M:     Jamie Iles <jamie@jamieiles.com>
 L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
@@ -5544,6 +5551,8 @@ F:        Documentation/devicetree/bindings/pwm/
 F:     include/linux/pwm.h
 F:     include/linux/of_pwm.h
 F:     drivers/pwm/
+F:     drivers/video/backlight/pwm_bl.c
+F:     include/linux/pwm_backlight.h
 
 PXA2xx/PXA3xx SUPPORT
 M:     Eric Miao <eric.y.miao@gmail.com>
index a3c11d5..bb9fff2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 6
 SUBLEVEL = 0
-EXTRAVERSION = -rc7
+EXTRAVERSION =
 NAME = Terrified Chipmunk
 
 # *DOCUMENTATION*
index 8dabfe8..ff886e0 100644 (file)
@@ -261,7 +261,7 @@ static void __init apx4devkit_init(void)
        enable_clk_enet_out();
 
        if (IS_BUILTIN(CONFIG_PHYLIB))
-               phy_register_fixup_for_uid(PHY_ID_KS8051, MICREL_PHY_ID_MASK,
+               phy_register_fixup_for_uid(PHY_ID_KSZ8051, MICREL_PHY_ID_MASK,
                                           apx4devkit_phy_fixup);
 
        mxsfb_pdata.mode_list = apx4devkit_video_modes;
index 410291c..a6cd14a 100644 (file)
@@ -204,6 +204,13 @@ void __init orion5x_wdt_init(void)
 void __init orion5x_init_early(void)
 {
        orion_time_set_base(TIMER_VIRT_BASE);
+
+       /*
+        * Some Orion5x devices allocate their coherent buffers from atomic
+        * context. Increase size of atomic coherent pool to make sure such
+        * the allocations won't fail.
+        */
+       init_dma_coherent_pool_size(SZ_1M);
 }
 
 int orion5x_tclk;
index b7344be..94486e7 100644 (file)
@@ -67,6 +67,13 @@ static struct regulator_init_data ldo0_data = {
                },                                                      \
        }
 
+static struct regulator_init_data sys_data = {
+       .supply_regulator = "vdd_5v0",
+       .constraints = {
+               .name = "vdd_sys",
+       },
+};
+
 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);
@@ -74,7 +81,7 @@ 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(ldo5, "vdd_ldo5", "vdd_sys", 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);
@@ -88,6 +95,7 @@ HARMONY_REGULATOR_INIT(ldo9, "vdd_ldo9", "vdd_sm2", 1250, 3300, 1);
        }
 
 static struct tps6586x_subdev_info tps_devs[] = {
+       TPS_REG(SYS, &sys_data),
        TPS_REG(SM_0, &sm0_data),
        TPS_REG(SM_1, &sm1_data),
        TPS_REG(SM_2, &sm2_data),
@@ -120,7 +128,7 @@ static struct i2c_board_info __initdata harmony_regulators[] = {
 
 int __init harmony_regulator_init(void)
 {
-       regulator_register_always_on(0, "vdd_sys",
+       regulator_register_always_on(0, "vdd_5v0",
                NULL, 0, 5000000);
 
        if (machine_is_harmony()) {
index e59c4ab..13f555d 100644 (file)
@@ -346,6 +346,8 @@ static int __init atomic_pool_init(void)
                       (unsigned)pool->size / 1024);
                return 0;
        }
+
+       kfree(pages);
 no_pages:
        kfree(bitmap);
 no_bitmap:
index 3af601e..f08e891 100644 (file)
@@ -2,6 +2,7 @@ include include/asm-generic/Kbuild.asm
 
 generic-y += atomic.h
 generic-y += auxvec.h
+generic-y += barrier.h
 generic-y += bitsperlong.h
 generic-y += bugs.h
 generic-y += cputime.h
diff --git a/arch/c6x/include/asm/barrier.h b/arch/c6x/include/asm/barrier.h
deleted file mode 100644 (file)
index 538240e..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- *  Port on Texas Instruments TMS320C6x architecture
- *
- *  Copyright (C) 2004, 2009, 2010, 2011 Texas Instruments Incorporated
- *  Author: Aurelien Jacquiot (aurelien.jacquiot@jaluna.com)
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License version 2 as
- *  published by the Free Software Foundation.
- */
-#ifndef _ASM_C6X_BARRIER_H
-#define _ASM_C6X_BARRIER_H
-
-#define nop()                    asm("NOP\n");
-
-#define mb()                     barrier()
-#define rmb()                    barrier()
-#define wmb()                    barrier()
-#define set_mb(var, value)       do { var = value;  mb(); } while (0)
-#define set_wmb(var, value)      do { var = value; wmb(); } while (0)
-
-#define smp_mb()                barrier()
-#define smp_rmb()               barrier()
-#define smp_wmb()               barrier()
-#define smp_read_barrier_depends()     do { } while (0)
-
-#endif /* _ASM_C6X_BARRIER_H */
index 1c1aadc..c32ae5c 100644 (file)
@@ -1,10 +1,6 @@
 addnote
 empty.c
 hack-coff
-infblock.c
-infblock.h
-infcodes.c
-infcodes.h
 inffast.c
 inffast.h
 inffixed.h
index 15fb779..58105c3 100644 (file)
 #include <linux/module.h>
 #include <asm/pgtable.h>
 
-#define GXIO_TRIO_OP_ALLOC_ASIDS       IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_DEALLOC_ASID      IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1400)
+#define GXIO_TRIO_OP_ALLOC_ASIDS       IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1401)
 
-#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1402)
+#define GXIO_TRIO_OP_ALLOC_MEMORY_MAPS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1404)
 
-#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140e)
-#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x140f)
+#define GXIO_TRIO_OP_ALLOC_PIO_REGIONS IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1412)
 
-#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1417)
-#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1418)
-#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1419)
-#define GXIO_TRIO_OP_CONFIG_MSI_INTR   IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x141a)
+#define GXIO_TRIO_OP_INIT_PIO_REGION_AUX IORPC_OPCODE(IORPC_FORMAT_NONE, 0x1414)
 
-#define GXIO_TRIO_OP_SET_MPS_MRS       IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141c)
-#define GXIO_TRIO_OP_FORCE_RC_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141d)
-#define GXIO_TRIO_OP_FORCE_EP_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_INIT_MEMORY_MAP_MMU_AUX IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141e)
+#define GXIO_TRIO_OP_GET_PORT_PROPERTY IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x141f)
+#define GXIO_TRIO_OP_CONFIG_LEGACY_INTR IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1420)
+#define GXIO_TRIO_OP_CONFIG_MSI_INTR   IORPC_OPCODE(IORPC_FORMAT_KERNEL_INTERRUPT, 0x1421)
+
+#define GXIO_TRIO_OP_SET_MPS_MRS       IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1423)
+#define GXIO_TRIO_OP_FORCE_RC_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1424)
+#define GXIO_TRIO_OP_FORCE_EP_LINK_UP  IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x1425)
 #define GXIO_TRIO_OP_GET_MMIO_BASE     IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8000)
 #define GXIO_TRIO_OP_CHECK_MMIO_OFFSET IORPC_OPCODE(IORPC_FORMAT_NONE_NOUSER, 0x8001)
 
index 69f1c57..33a6a24 100644 (file)
@@ -20,14 +20,6 @@ struct mm_struct;
 
 struct thread_struct {
        struct task_struct *saved_task;
-       /*
-        * This flag is set to 1 before calling do_fork (and analyzed in
-        * copy_thread) to mark that we are begin called from userspace (fork /
-        * vfork / clone), and reset to 0 after. It is left to 0 when called
-        * from kernelspace (i.e. kernel_thread() or fork_idle(),
-        * as of 2.6.11).
-        */
-       int forking;
        struct pt_regs regs;
        int singlestep_syscall;
        void *fault_addr;
@@ -58,7 +50,6 @@ struct thread_struct {
 
 #define INIT_THREAD \
 { \
-       .forking                = 0, \
        .regs                   = EMPTY_REGS,   \
        .fault_addr             = NULL, \
        .prev_sched             = NULL, \
index 40db8f7..2df313b 100644 (file)
@@ -7,16 +7,6 @@ DEFINE(UM_KERN_PAGE_MASK, PAGE_MASK);
 DEFINE(UM_KERN_PAGE_SHIFT, PAGE_SHIFT);
 DEFINE(UM_NSEC_PER_SEC, NSEC_PER_SEC);
 
-DEFINE_STR(UM_KERN_EMERG, KERN_EMERG);
-DEFINE_STR(UM_KERN_ALERT, KERN_ALERT);
-DEFINE_STR(UM_KERN_CRIT, KERN_CRIT);
-DEFINE_STR(UM_KERN_ERR, KERN_ERR);
-DEFINE_STR(UM_KERN_WARNING, KERN_WARNING);
-DEFINE_STR(UM_KERN_NOTICE, KERN_NOTICE);
-DEFINE_STR(UM_KERN_INFO, KERN_INFO);
-DEFINE_STR(UM_KERN_DEBUG, KERN_DEBUG);
-DEFINE_STR(UM_KERN_CONT, KERN_CONT);
-
 DEFINE(UM_ELF_CLASS, ELF_CLASS);
 DEFINE(UM_ELFCLASS32, ELFCLASS32);
 DEFINE(UM_ELFCLASS64, ELFCLASS64);
index 4fa82c0..cef0685 100644 (file)
 extern void panic(const char *fmt, ...)
        __attribute__ ((format (printf, 1, 2)));
 
+/* Requires preincluding include/linux/kern_levels.h */
+#define UM_KERN_EMERG  KERN_EMERG
+#define UM_KERN_ALERT  KERN_ALERT
+#define UM_KERN_CRIT   KERN_CRIT
+#define UM_KERN_ERR    KERN_ERR
+#define UM_KERN_WARNING        KERN_WARNING
+#define UM_KERN_NOTICE KERN_NOTICE
+#define UM_KERN_INFO   KERN_INFO
+#define UM_KERN_DEBUG  KERN_DEBUG
+#define UM_KERN_CONT   KERN_CONT
+
 #ifdef UML_CONFIG_PRINTK
 extern int printk(const char *fmt, ...)
        __attribute__ ((format (printf, 1, 2)));
index 6cade93..8c82786 100644 (file)
@@ -39,34 +39,21 @@ void flush_thread(void)
 
 void start_thread(struct pt_regs *regs, unsigned long eip, unsigned long esp)
 {
+       get_safe_registers(regs->regs.gp, regs->regs.fp);
        PT_REGS_IP(regs) = eip;
        PT_REGS_SP(regs) = esp;
-}
-EXPORT_SYMBOL(start_thread);
-
-static long execve1(const char *file,
-                   const char __user *const __user *argv,
-                   const char __user *const __user *env)
-{
-       long error;
-
-       error = do_execve(file, argv, env, &current->thread.regs);
-       if (error == 0) {
-               task_lock(current);
-               current->ptrace &= ~PT_DTRACE;
+       current->ptrace &= ~PT_DTRACE;
 #ifdef SUBARCH_EXECVE1
-               SUBARCH_EXECVE1(&current->thread.regs.regs);
+       SUBARCH_EXECVE1(regs->regs);
 #endif
-               task_unlock(current);
-       }
-       return error;
 }
+EXPORT_SYMBOL(start_thread);
 
 long um_execve(const char *file, const char __user *const __user *argv, const char __user *const __user *env)
 {
        long err;
 
-       err = execve1(file, argv, env);
+       err = do_execve(file, argv, env, &current->thread.regs);
        if (!err)
                UML_LONGJMP(current->thread.exec_buf, 1);
        return err;
@@ -81,7 +68,7 @@ long sys_execve(const char __user *file, const char __user *const __user *argv,
        filename = getname(file);
        error = PTR_ERR(filename);
        if (IS_ERR(filename)) goto out;
-       error = execve1(filename, argv, env);
+       error = do_execve(filename, argv, env, &current->thread.regs);
        putname(filename);
  out:
        return error;
index 57fc702..c5f5afa 100644 (file)
@@ -181,11 +181,12 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
                struct pt_regs *regs)
 {
        void (*handler)(void);
+       int kthread = current->flags & PF_KTHREAD;
        int ret = 0;
 
        p->thread = (struct thread_struct) INIT_THREAD;
 
-       if (current->thread.forking) {
+       if (!kthread) {
                memcpy(&p->thread.regs.regs, &regs->regs,
                       sizeof(p->thread.regs.regs));
                PT_REGS_SET_SYSCALL_RETURN(&p->thread.regs, 0);
@@ -195,8 +196,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
                handler = fork_handler;
 
                arch_copy_thread(&current->thread.arch, &p->thread.arch);
-       }
-       else {
+       } else {
                get_safe_registers(p->thread.regs.regs.gp, p->thread.regs.regs.fp);
                p->thread.request.u.thread = current->thread.request.u.thread;
                handler = new_thread_handler;
@@ -204,7 +204,7 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
 
        new_thread(task_stack_page(p), &p->thread.switch_buf, handler);
 
-       if (current->thread.forking) {
+       if (!kthread) {
                clear_flushed_tls(p);
 
                /*
index 7362d58..cc9c235 100644 (file)
@@ -22,9 +22,13 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
                         struct k_sigaction *ka, siginfo_t *info)
 {
        sigset_t *oldset = sigmask_to_save();
+       int singlestep = 0;
        unsigned long sp;
        int err;
 
+       if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
+               singlestep = 1;
+
        /* Did we come from a system call? */
        if (PT_REGS_SYSCALL_NR(regs) >= 0) {
                /* If so, check system call restarting.. */
@@ -61,7 +65,7 @@ static void handle_signal(struct pt_regs *regs, unsigned long signr,
        if (err)
                force_sigsegv(signr, current);
        else
-               signal_delivered(signr, info, ka, regs, 0);
+               signal_delivered(signr, info, ka, regs, singlestep);
 }
 
 static int kern_do_signal(struct pt_regs *regs)
index f958cb8..a4c6d8e 100644 (file)
 
 long sys_fork(void)
 {
-       long ret;
-
-       current->thread.forking = 1;
-       ret = do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
+       return do_fork(SIGCHLD, UPT_SP(&current->thread.regs.regs),
                      &current->thread.regs, 0, NULL, NULL);
-       current->thread.forking = 0;
-       return ret;
 }
 
 long sys_vfork(void)
 {
-       long ret;
-
-       current->thread.forking = 1;
-       ret = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
+       return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD,
                      UPT_SP(&current->thread.regs.regs),
                      &current->thread.regs, 0, NULL, NULL);
-       current->thread.forking = 0;
-       return ret;
+}
+
+long sys_clone(unsigned long clone_flags, unsigned long newsp,
+              void __user *parent_tid, void __user *child_tid)
+{
+       if (!newsp)
+               newsp = UPT_SP(&current->thread.regs.regs);
+
+       return do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
+                     child_tid);
 }
 
 long old_mmap(unsigned long addr, unsigned long len,
index d50270d..15889df 100644 (file)
@@ -8,7 +8,7 @@ USER_OBJS += $(filter %_user.o,$(obj-y) $(obj-m)  $(USER_SINGLE_OBJS))
 USER_OBJS := $(foreach file,$(USER_OBJS),$(obj)/$(file))
 
 $(USER_OBJS:.o=.%): \
-       c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include user.h $(CFLAGS_$(basetarget).o)
+       c_flags = -Wp,-MD,$(depfile) $(USER_CFLAGS) -include $(srctree)/include/linux/kern_levels.h -include user.h $(CFLAGS_$(basetarget).o)
 
 # These are like USER_OBJS but filter USER_CFLAGS through unprofile instead of
 # using it directly.
index 2c392d6..434e210 100644 (file)
@@ -35,8 +35,6 @@
 #define        HPET_ID_NUMBER_SHIFT    8
 #define HPET_ID_VENDOR_SHIFT   16
 
-#define HPET_ID_VENDOR_8086    0x8086
-
 #define HPET_CFG_ENABLE                0x001
 #define HPET_CFG_LEGACY                0x002
 #define        HPET_LEGACY_8254        2
index 9926e11..aeaff8b 100644 (file)
@@ -21,6 +21,7 @@ config 64BIT
 config X86_32
        def_bool !64BIT
        select HAVE_AOUT
+       select ARCH_WANT_IPC_PARSE_VERSION
 
 config X86_64
        def_bool 64BIT
index 5868526..46a9df9 100644 (file)
@@ -7,9 +7,6 @@
 #define DEFINE(sym, val) \
        asm volatile("\n->" #sym " %0 " #val : : "i" (val))
 
-#define STR(x) #x
-#define DEFINE_STR(sym, val) asm volatile("\n->" #sym " " STR(val) " " #val: : )
-
 #define BLANK() asm volatile("\n->" : : )
 
 #define OFFSET(sym, str, mem) \
index bd9a89b..ca255a8 100644 (file)
@@ -1,3 +1,5 @@
+extern long sys_clone(unsigned long clone_flags, unsigned long newsp,
+              void __user *parent_tid, void __user *child_tid);
 #ifdef __i386__
 #include "syscalls_32.h"
 #else
index a508cea..ba7363e 100644 (file)
@@ -416,9 +416,6 @@ int setup_signal_stack_sc(unsigned long stack_top, int sig,
        PT_REGS_AX(regs) = (unsigned long) sig;
        PT_REGS_DX(regs) = (unsigned long) 0;
        PT_REGS_CX(regs) = (unsigned long) 0;
-
-       if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
-               ptrace_notify(SIGTRAP);
        return 0;
 }
 
@@ -466,9 +463,6 @@ int setup_signal_stack_si(unsigned long stack_top, int sig,
        PT_REGS_AX(regs) = (unsigned long) sig;
        PT_REGS_DX(regs) = (unsigned long) &frame->info;
        PT_REGS_CX(regs) = (unsigned long) &frame->uc;
-
-       if ((current->ptrace & PT_DTRACE) && (current->ptrace & PT_PTRACED))
-               ptrace_notify(SIGTRAP);
        return 0;
 }
 
index 68d1dc9..b5408ce 100644 (file)
@@ -28,7 +28,7 @@
 #define ptregs_execve sys_execve
 #define ptregs_iopl sys_iopl
 #define ptregs_vm86old sys_vm86old
-#define ptregs_clone sys_clone
+#define ptregs_clone i386_clone
 #define ptregs_vm86 sys_vm86
 #define ptregs_sigaltstack sys_sigaltstack
 #define ptregs_vfork sys_vfork
index b853e86..db444c7 100644 (file)
@@ -3,37 +3,24 @@
  * Licensed under the GPL
  */
 
-#include "linux/sched.h"
-#include "linux/shm.h"
-#include "linux/ipc.h"
-#include "linux/syscalls.h"
-#include "asm/mman.h"
-#include "asm/uaccess.h"
-#include "asm/unistd.h"
+#include <linux/syscalls.h>
+#include <sysdep/syscalls.h>
 
 /*
  * The prototype on i386 is:
  *
- *     int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls, int * child_tidptr)
+ *     int clone(int flags, void * child_stack, int * parent_tidptr, struct user_desc * newtls
  *
  * and the "newtls" arg. on i386 is read by copy_thread directly from the
  * register saved on the stack.
  */
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
-              int __user *parent_tid, void *newtls, int __user *child_tid)
+long i386_clone(unsigned long clone_flags, unsigned long newsp,
+               int __user *parent_tid, void *newtls, int __user *child_tid)
 {
-       long ret;
-
-       if (!newsp)
-               newsp = UPT_SP(&current->thread.regs.regs);
-
-       current->thread.forking = 1;
-       ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
-                     child_tid);
-       current->thread.forking = 0;
-       return ret;
+       return sys_clone(clone_flags, newsp, parent_tid, child_tid);
 }
 
+
 long sys_sigaction(int sig, const struct old_sigaction __user *act,
                         struct old_sigaction __user *oact)
 {
index f3d82bb..adb08eb 100644 (file)
@@ -5,12 +5,9 @@
  * Licensed under the GPL
  */
 
-#include "linux/linkage.h"
-#include "linux/personality.h"
-#include "linux/utsname.h"
-#include "asm/prctl.h" /* XXX This should get the constants from libc */
-#include "asm/uaccess.h"
-#include "os.h"
+#include <linux/sched.h>
+#include <asm/prctl.h> /* XXX This should get the constants from libc */
+#include <os.h>
 
 long arch_prctl(struct task_struct *task, int code, unsigned long __user *addr)
 {
@@ -79,20 +76,6 @@ long sys_arch_prctl(int code, unsigned long addr)
        return arch_prctl(current, code, (unsigned long __user *) addr);
 }
 
-long sys_clone(unsigned long clone_flags, unsigned long newsp,
-              void __user *parent_tid, void __user *child_tid)
-{
-       long ret;
-
-       if (!newsp)
-               newsp = UPT_SP(&current->thread.regs.regs);
-       current->thread.forking = 1;
-       ret = do_fork(clone_flags, newsp, &current->thread.regs, 0, parent_tid,
-                     child_tid);
-       current->thread.forking = 0;
-       return ret;
-}
-
 void arch_switch_to(struct task_struct *to)
 {
        if ((to->thread.arch.fs == 0) || (to->mm == NULL))
index d11ca11..e2d62d6 100644 (file)
@@ -17,6 +17,7 @@
 #include <asm/e820.h>
 #include <asm/setup.h>
 #include <asm/acpi.h>
+#include <asm/numa.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/xen/hypercall.h>
 
@@ -544,4 +545,7 @@ void __init xen_arch_setup(void)
        disable_cpufreq();
        WARN_ON(set_pm_idle_to_default());
        fiddle_vdso();
+#ifdef CONFIG_NUMA
+       numa_off = 1;
+#endif
 }
index a897346..5b6b1d8 100644 (file)
 #include <linux/irq.h>
 #include <linux/interrupt.h>
 #include <linux/irqdomain.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 
 #include "internal.h"
 
 struct regmap_irq_chip_data {
        struct mutex lock;
+       struct irq_chip irq_chip;
 
        struct regmap *map;
        const struct regmap_irq_chip *chip;
@@ -59,6 +61,14 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
        struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
        struct regmap *map = d->map;
        int i, ret;
+       u32 reg;
+
+       if (d->chip->runtime_pm) {
+               ret = pm_runtime_get_sync(map->dev);
+               if (ret < 0)
+                       dev_err(map->dev, "IRQ sync failed to resume: %d\n",
+                               ret);
+       }
 
        /*
         * If there's been a change in the mask write it back to the
@@ -66,15 +76,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
         * suppress pointless writes.
         */
        for (i = 0; i < d->chip->num_regs; i++) {
-               ret = regmap_update_bits(d->map, d->chip->mask_base +
-                                               (i * map->reg_stride *
-                                               d->irq_reg_stride),
+               reg = d->chip->mask_base +
+                       (i * map->reg_stride * d->irq_reg_stride);
+               if (d->chip->mask_invert)
+                       ret = regmap_update_bits(d->map, reg,
+                                        d->mask_buf_def[i], ~d->mask_buf[i]);
+               else
+                       ret = regmap_update_bits(d->map, reg,
                                         d->mask_buf_def[i], d->mask_buf[i]);
                if (ret != 0)
                        dev_err(d->map->dev, "Failed to sync masks in %x\n",
-                               d->chip->mask_base + (i * map->reg_stride));
+                               reg);
        }
 
+       if (d->chip->runtime_pm)
+               pm_runtime_put(map->dev);
+
        /* If we've changed our wakeup count propagate it to the parent */
        if (d->wake_count < 0)
                for (i = d->wake_count; i < 0; i++)
@@ -128,8 +145,7 @@ static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
        return 0;
 }
 
-static struct irq_chip regmap_irq_chip = {
-       .name                   = "regmap",
+static const struct irq_chip regmap_irq_chip = {
        .irq_bus_lock           = regmap_irq_lock,
        .irq_bus_sync_unlock    = regmap_irq_sync_unlock,
        .irq_disable            = regmap_irq_disable,
@@ -144,6 +160,16 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
        struct regmap *map = data->map;
        int ret, i;
        bool handled = false;
+       u32 reg;
+
+       if (chip->runtime_pm) {
+               ret = pm_runtime_get_sync(map->dev);
+               if (ret < 0) {
+                       dev_err(map->dev, "IRQ thread failed to resume: %d\n",
+                               ret);
+                       return IRQ_NONE;
+               }
+       }
 
        /*
         * Ignore masked IRQs and ack if we need to; we ack early so
@@ -160,20 +186,20 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
                if (ret != 0) {
                        dev_err(map->dev, "Failed to read IRQ status: %d\n",
                                        ret);
+                       if (chip->runtime_pm)
+                               pm_runtime_put(map->dev);
                        return IRQ_NONE;
                }
 
                data->status_buf[i] &= ~data->mask_buf[i];
 
                if (data->status_buf[i] && chip->ack_base) {
-                       ret = regmap_write(map, chip->ack_base +
-                                               (i * map->reg_stride *
-                                               data->irq_reg_stride),
-                                          data->status_buf[i]);
+                       reg = chip->ack_base +
+                               (i * map->reg_stride * data->irq_reg_stride);
+                       ret = regmap_write(map, reg, data->status_buf[i]);
                        if (ret != 0)
                                dev_err(map->dev, "Failed to ack 0x%x: %d\n",
-                                       chip->ack_base + (i * map->reg_stride),
-                                       ret);
+                                       reg, ret);
                }
        }
 
@@ -185,6 +211,9 @@ static irqreturn_t regmap_irq_thread(int irq, void *d)
                }
        }
 
+       if (chip->runtime_pm)
+               pm_runtime_put(map->dev);
+
        if (handled)
                return IRQ_HANDLED;
        else
@@ -197,7 +226,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
        struct regmap_irq_chip_data *data = h->host_data;
 
        irq_set_chip_data(virq, data);
-       irq_set_chip_and_handler(virq, &regmap_irq_chip, handle_edge_irq);
+       irq_set_chip(virq, &data->irq_chip);
        irq_set_nested_thread(virq, 1);
 
        /* ARM needs us to explicitly flag the IRQ as valid
@@ -238,6 +267,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        struct regmap_irq_chip_data *d;
        int i;
        int ret = -ENOMEM;
+       u32 reg;
 
        for (i = 0; i < chip->num_irqs; i++) {
                if (chip->irqs[i].reg_offset % map->reg_stride)
@@ -284,6 +314,13 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                        goto err_alloc;
        }
 
+       d->irq_chip = regmap_irq_chip;
+       d->irq_chip.name = chip->name;
+       if (!chip->wake_base) {
+               d->irq_chip.irq_set_wake = NULL;
+               d->irq_chip.flags |= IRQCHIP_MASK_ON_SUSPEND |
+                                    IRQCHIP_SKIP_SET_WAKE;
+       }
        d->irq = irq;
        d->map = map;
        d->chip = chip;
@@ -303,16 +340,37 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        /* Mask all the interrupts by default */
        for (i = 0; i < chip->num_regs; i++) {
                d->mask_buf[i] = d->mask_buf_def[i];
-               ret = regmap_write(map, chip->mask_base + (i * map->reg_stride
-                                  * d->irq_reg_stride),
-                                  d->mask_buf[i]);
+               reg = chip->mask_base +
+                       (i * map->reg_stride * d->irq_reg_stride);
+               if (chip->mask_invert)
+                       ret = regmap_update_bits(map, reg,
+                                        d->mask_buf[i], ~d->mask_buf[i]);
+               else
+                       ret = regmap_update_bits(map, reg,
+                                        d->mask_buf[i], d->mask_buf[i]);
                if (ret != 0) {
                        dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
-                               chip->mask_base + (i * map->reg_stride), ret);
+                               reg, ret);
                        goto err_alloc;
                }
        }
 
+       /* Wake is disabled by default */
+       if (d->wake_buf) {
+               for (i = 0; i < chip->num_regs; i++) {
+                       d->wake_buf[i] = d->mask_buf_def[i];
+                       reg = chip->wake_base +
+                               (i * map->reg_stride * d->irq_reg_stride);
+                       ret = regmap_update_bits(map, reg, d->wake_buf[i],
+                                                d->wake_buf[i]);
+                       if (ret != 0) {
+                               dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
+                                       reg, ret);
+                               goto err_alloc;
+                       }
+               }
+       }
+
        if (irq_base)
                d->domain = irq_domain_add_legacy(map->dev->of_node,
                                                  chip->num_irqs, irq_base, 0,
index c241ae2..52069d2 100644 (file)
@@ -659,13 +659,12 @@ EXPORT_SYMBOL_GPL(devm_regmap_init);
  * new cache.  This can be used to restore the cache to defaults or to
  * update the cache configuration to reflect runtime discovery of the
  * hardware.
+ *
+ * No explicit locking is done here, the user needs to ensure that
+ * this function will not race with other calls to regmap.
  */
 int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
 {
-       int ret;
-
-       map->lock(map);
-
        regcache_exit(map);
        regmap_debugfs_exit(map);
 
@@ -681,11 +680,7 @@ int regmap_reinit_cache(struct regmap *map, const struct regmap_config *config)
        map->cache_bypass = false;
        map->cache_only = false;
 
-       ret = regcache_init(map, config);
-
-       map->unlock(map);
-
-       return ret;
+       return regcache_init(map, config);
 }
 EXPORT_SYMBOL_GPL(regmap_reinit_cache);
 
index 38a2d06..ad16c68 100644 (file)
@@ -79,6 +79,7 @@ struct nvme_dev {
        char serial[20];
        char model[40];
        char firmware_rev[8];
+       u32 max_hw_sectors;
 };
 
 /*
@@ -835,15 +836,15 @@ static int nvme_identify(struct nvme_dev *dev, unsigned nsid, unsigned cns,
 }
 
 static int nvme_get_features(struct nvme_dev *dev, unsigned fid,
-                               unsigned dword11, dma_addr_t dma_addr)
+                               unsigned nsid, dma_addr_t dma_addr)
 {
        struct nvme_command c;
 
        memset(&c, 0, sizeof(c));
        c.features.opcode = nvme_admin_get_features;
+       c.features.nsid = cpu_to_le32(nsid);
        c.features.prp1 = cpu_to_le64(dma_addr);
        c.features.fid = cpu_to_le32(fid);
-       c.features.dword11 = cpu_to_le32(dword11);
 
        return nvme_submit_admin_cmd(dev, &c, NULL);
 }
@@ -862,11 +863,51 @@ static int nvme_set_features(struct nvme_dev *dev, unsigned fid,
        return nvme_submit_admin_cmd(dev, &c, result);
 }
 
+/**
+ * nvme_cancel_ios - Cancel outstanding I/Os
+ * @queue: The queue to cancel I/Os on
+ * @timeout: True to only cancel I/Os which have timed out
+ */
+static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout)
+{
+       int depth = nvmeq->q_depth - 1;
+       struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
+       unsigned long now = jiffies;
+       int cmdid;
+
+       for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
+               void *ctx;
+               nvme_completion_fn fn;
+               static struct nvme_completion cqe = {
+                       .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1,
+               };
+
+               if (timeout && !time_after(now, info[cmdid].timeout))
+                       continue;
+               dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d\n", cmdid);
+               ctx = cancel_cmdid(nvmeq, cmdid, &fn);
+               fn(nvmeq->dev, ctx, &cqe);
+       }
+}
+
+static void nvme_free_queue_mem(struct nvme_queue *nvmeq)
+{
+       dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
+                               (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
+       dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
+                                       nvmeq->sq_cmds, nvmeq->sq_dma_addr);
+       kfree(nvmeq);
+}
+
 static void nvme_free_queue(struct nvme_dev *dev, int qid)
 {
        struct nvme_queue *nvmeq = dev->queues[qid];
        int vector = dev->entry[nvmeq->cq_vector].vector;
 
+       spin_lock_irq(&nvmeq->q_lock);
+       nvme_cancel_ios(nvmeq, false);
+       spin_unlock_irq(&nvmeq->q_lock);
+
        irq_set_affinity_hint(vector, NULL);
        free_irq(vector, nvmeq);
 
@@ -876,18 +917,15 @@ static void nvme_free_queue(struct nvme_dev *dev, int qid)
                adapter_delete_cq(dev, qid);
        }
 
-       dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth),
-                               (void *)nvmeq->cqes, nvmeq->cq_dma_addr);
-       dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth),
-                                       nvmeq->sq_cmds, nvmeq->sq_dma_addr);
-       kfree(nvmeq);
+       nvme_free_queue_mem(nvmeq);
 }
 
 static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid,
                                                        int depth, int vector)
 {
        struct device *dmadev = &dev->pci_dev->dev;
-       unsigned extra = (depth / 8) + (depth * sizeof(struct nvme_cmd_info));
+       unsigned extra = DIV_ROUND_UP(depth, 8) + (depth *
+                                               sizeof(struct nvme_cmd_info));
        struct nvme_queue *nvmeq = kzalloc(sizeof(*nvmeq) + extra, GFP_KERNEL);
        if (!nvmeq)
                return NULL;
@@ -975,7 +1013,7 @@ static __devinit struct nvme_queue *nvme_create_queue(struct nvme_dev *dev,
 
 static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
 {
-       int result;
+       int result = 0;
        u32 aqa;
        u64 cap;
        unsigned long timeout;
@@ -1005,17 +1043,22 @@ static int __devinit nvme_configure_admin_queue(struct nvme_dev *dev)
        timeout = ((NVME_CAP_TIMEOUT(cap) + 1) * HZ / 2) + jiffies;
        dev->db_stride = NVME_CAP_STRIDE(cap);
 
-       while (!(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
+       while (!result && !(readl(&dev->bar->csts) & NVME_CSTS_RDY)) {
                msleep(100);
                if (fatal_signal_pending(current))
-                       return -EINTR;
+                       result = -EINTR;
                if (time_after(jiffies, timeout)) {
                        dev_err(&dev->pci_dev->dev,
                                "Device not ready; aborting initialisation\n");
-                       return -ENODEV;
+                       result = -ENODEV;
                }
        }
 
+       if (result) {
+               nvme_free_queue_mem(nvmeq);
+               return result;
+       }
+
        result = queue_request_irq(dev, nvmeq, "nvme admin");
        dev->queues[0] = nvmeq;
        return result;
@@ -1037,6 +1080,8 @@ static struct nvme_iod *nvme_map_user_pages(struct nvme_dev *dev, int write,
        offset = offset_in_page(addr);
        count = DIV_ROUND_UP(offset + length, PAGE_SIZE);
        pages = kcalloc(count, sizeof(*pages), GFP_KERNEL);
+       if (!pages)
+               return ERR_PTR(-ENOMEM);
 
        err = get_user_pages_fast(addr, count, 1, pages);
        if (err < count) {
@@ -1146,14 +1191,13 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio)
        return status;
 }
 
-static int nvme_user_admin_cmd(struct nvme_ns *ns,
+static int nvme_user_admin_cmd(struct nvme_dev *dev,
                                        struct nvme_admin_cmd __user *ucmd)
 {
-       struct nvme_dev *dev = ns->dev;
        struct nvme_admin_cmd cmd;
        struct nvme_command c;
        int status, length;
-       struct nvme_iod *iod;
+       struct nvme_iod *uninitialized_var(iod);
 
        if (!capable(CAP_SYS_ADMIN))
                return -EACCES;
@@ -1204,7 +1248,7 @@ static int nvme_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd,
        case NVME_IOCTL_ID:
                return ns->ns_id;
        case NVME_IOCTL_ADMIN_CMD:
-               return nvme_user_admin_cmd(ns, (void __user *)arg);
+               return nvme_user_admin_cmd(ns->dev, (void __user *)arg);
        case NVME_IOCTL_SUBMIT_IO:
                return nvme_submit_io(ns, (void __user *)arg);
        default:
@@ -1218,26 +1262,6 @@ static const struct block_device_operations nvme_fops = {
        .compat_ioctl   = nvme_ioctl,
 };
 
-static void nvme_timeout_ios(struct nvme_queue *nvmeq)
-{
-       int depth = nvmeq->q_depth - 1;
-       struct nvme_cmd_info *info = nvme_cmd_info(nvmeq);
-       unsigned long now = jiffies;
-       int cmdid;
-
-       for_each_set_bit(cmdid, nvmeq->cmdid_data, depth) {
-               void *ctx;
-               nvme_completion_fn fn;
-               static struct nvme_completion cqe = { .status = cpu_to_le16(NVME_SC_ABORT_REQ) << 1, };
-
-               if (!time_after(now, info[cmdid].timeout))
-                       continue;
-               dev_warn(nvmeq->q_dmadev, "Timing out I/O %d\n", cmdid);
-               ctx = cancel_cmdid(nvmeq, cmdid, &fn);
-               fn(nvmeq->dev, ctx, &cqe);
-       }
-}
-
 static void nvme_resubmit_bios(struct nvme_queue *nvmeq)
 {
        while (bio_list_peek(&nvmeq->sq_cong)) {
@@ -1269,7 +1293,7 @@ static int nvme_kthread(void *data)
                                spin_lock_irq(&nvmeq->q_lock);
                                if (nvme_process_cq(nvmeq))
                                        printk("process_cq did something\n");
-                               nvme_timeout_ios(nvmeq);
+                               nvme_cancel_ios(nvmeq, true);
                                nvme_resubmit_bios(nvmeq);
                                spin_unlock_irq(&nvmeq->q_lock);
                        }
@@ -1339,6 +1363,9 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, int nsid,
        ns->disk = disk;
        lbaf = id->flbas & 0xf;
        ns->lba_shift = id->lbaf[lbaf].ds;
+       blk_queue_logical_block_size(ns->queue, 1 << ns->lba_shift);
+       if (dev->max_hw_sectors)
+               blk_queue_max_hw_sectors(ns->queue, dev->max_hw_sectors);
 
        disk->major = nvme_major;
        disk->minors = NVME_MINORS;
@@ -1383,7 +1410,7 @@ static int set_queue_count(struct nvme_dev *dev, int count)
 
 static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
 {
-       int result, cpu, i, nr_io_queues, db_bar_size;
+       int result, cpu, i, nr_io_queues, db_bar_size, q_depth;
 
        nr_io_queues = num_online_cpus();
        result = set_queue_count(dev, nr_io_queues);
@@ -1429,9 +1456,10 @@ static int __devinit nvme_setup_io_queues(struct nvme_dev *dev)
                cpu = cpumask_next(cpu, cpu_online_mask);
        }
 
+       q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1,
+                                                               NVME_Q_DEPTH);
        for (i = 0; i < nr_io_queues; i++) {
-               dev->queues[i + 1] = nvme_create_queue(dev, i + 1,
-                                                       NVME_Q_DEPTH, i);
+               dev->queues[i + 1] = nvme_create_queue(dev, i + 1, q_depth, i);
                if (IS_ERR(dev->queues[i + 1]))
                        return PTR_ERR(dev->queues[i + 1]);
                dev->queue_count++;
@@ -1480,6 +1508,10 @@ static int __devinit nvme_dev_add(struct nvme_dev *dev)
        memcpy(dev->serial, ctrl->sn, sizeof(ctrl->sn));
        memcpy(dev->model, ctrl->mn, sizeof(ctrl->mn));
        memcpy(dev->firmware_rev, ctrl->fr, sizeof(ctrl->fr));
+       if (ctrl->mdts) {
+               int shift = NVME_CAP_MPSMIN(readq(&dev->bar->cap)) + 12;
+               dev->max_hw_sectors = 1 << (ctrl->mdts + shift - 9);
+       }
 
        id_ns = mem;
        for (i = 1; i <= nn; i++) {
@@ -1523,8 +1555,6 @@ static int nvme_dev_remove(struct nvme_dev *dev)
        list_del(&dev->node);
        spin_unlock(&dev_list_lock);
 
-       /* TODO: wait all I/O finished or cancel them */
-
        list_for_each_entry_safe(ns, next, &dev->namespaces, list) {
                list_del(&ns->list);
                del_gendisk(ns->disk);
@@ -1560,15 +1590,33 @@ static void nvme_release_prp_pools(struct nvme_dev *dev)
        dma_pool_destroy(dev->prp_small_pool);
 }
 
-/* XXX: Use an ida or something to let remove / add work correctly */
-static void nvme_set_instance(struct nvme_dev *dev)
+static DEFINE_IDA(nvme_instance_ida);
+
+static int nvme_set_instance(struct nvme_dev *dev)
 {
-       static int instance;
-       dev->instance = instance++;
+       int instance, error;
+
+       do {
+               if (!ida_pre_get(&nvme_instance_ida, GFP_KERNEL))
+                       return -ENODEV;
+
+               spin_lock(&dev_list_lock);
+               error = ida_get_new(&nvme_instance_ida, &instance);
+               spin_unlock(&dev_list_lock);
+       } while (error == -EAGAIN);
+
+       if (error)
+               return -ENODEV;
+
+       dev->instance = instance;
+       return 0;
 }
 
 static void nvme_release_instance(struct nvme_dev *dev)
 {
+       spin_lock(&dev_list_lock);
+       ida_remove(&nvme_instance_ida, dev->instance);
+       spin_unlock(&dev_list_lock);
 }
 
 static int __devinit nvme_probe(struct pci_dev *pdev,
@@ -1601,7 +1649,10 @@ static int __devinit nvme_probe(struct pci_dev *pdev,
        pci_set_drvdata(pdev, dev);
        dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
        dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
-       nvme_set_instance(dev);
+       result = nvme_set_instance(dev);
+       if (result)
+               goto disable;
+
        dev->entry[0].vector = pdev->irq;
 
        result = nvme_setup_prp_pools(dev);
@@ -1704,15 +1755,17 @@ static struct pci_driver nvme_driver = {
 
 static int __init nvme_init(void)
 {
-       int result = -EBUSY;
+       int result;
 
        nvme_thread = kthread_run(nvme_kthread, NULL, "nvme");
        if (IS_ERR(nvme_thread))
                return PTR_ERR(nvme_thread);
 
-       nvme_major = register_blkdev(nvme_major, "nvme");
-       if (nvme_major <= 0)
+       result = register_blkdev(nvme_major, "nvme");
+       if (result < 0)
                goto kill_kthread;
+       else if (result > 0)
+               nvme_major = result;
 
        result = pci_register_driver(&nvme_driver);
        if (result)
index 9917943..54a55f0 100644 (file)
@@ -246,13 +246,12 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
 {
        struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
 
-       rbd_get_dev(rbd_dev);
-
-       set_device_ro(bdev, rbd_dev->read_only);
-
        if ((mode & FMODE_WRITE) && rbd_dev->read_only)
                return -EROFS;
 
+       rbd_get_dev(rbd_dev);
+       set_device_ro(bdev, rbd_dev->read_only);
+
        return 0;
 }
 
index 7ab6e26..17d6958 100644 (file)
@@ -168,9 +168,9 @@ static void atc_desc_put(struct at_dma_chan *atchan, struct at_desc *desc)
 }
 
 /**
- * atc_desc_chain - build chain adding a descripor
- * @first: address of first descripor of the chain
- * @prev: address of previous descripor of the chain
+ * atc_desc_chain - build chain adding a descriptor
+ * @first: address of first descriptor of the chain
+ * @prev: address of previous descriptor of the chain
  * @desc: descriptor to queue
  *
  * Called from prep_* functions
@@ -796,7 +796,7 @@ err_out:
 }
 
 /**
- * atc_dma_cyclic_fill_desc - Fill one period decriptor
+ * atc_dma_cyclic_fill_desc - Fill one period descriptor
  */
 static int
 atc_dma_cyclic_fill_desc(struct dma_chan *chan, struct at_desc *desc,
index c64917e..bb02fd9 100644 (file)
@@ -1118,7 +1118,7 @@ fail:
  * @chan: channel
  * @dma_addr: DMA mapped address of the buffer
  * @buf_len: length of the buffer (in bytes)
- * @period_len: lenght of a single period
+ * @period_len: length of a single period
  * @dir: direction of the operation
  * @context: operation context (ignored)
  *
index 8f84761..094437b 100644 (file)
@@ -1015,7 +1015,7 @@ static irqreturn_t fsldma_chan_irq(int irq, void *data)
        /*
         * Programming Error
         * The DMA_INTERRUPT async_tx is a NULL transfer, which will
-        * triger a PE interrupt.
+        * trigger a PE interrupt.
         */
        if (stat & FSL_DMA_SR_PE) {
                chan_dbg(chan, "irq: Programming Error INT\n");
index 5084975..54f580b 100644 (file)
@@ -572,8 +572,8 @@ static void imxdma_tasklet(unsigned long data)
        if (desc->desc.callback)
                desc->desc.callback(desc->desc.callback_param);
 
-       /* If we are dealing with a cyclic descriptor keep it on ld_active
-        * and dont mark the descripor as complete.
+       /* If we are dealing with a cyclic descriptor, keep it on ld_active
+        * and dont mark the descriptor as complete.
         * Only in non-cyclic cases it would be marked as complete
         */
        if (imxdma_chan_is_doing_cyclic(imxdmac))
index 222e907..02b21d7 100644 (file)
@@ -427,7 +427,7 @@ DMA engine callback Functions*/
  * intel_mid_dma_tx_submit -   callback to submit DMA transaction
  * @tx: dma engine descriptor
  *
- * Submit the DMA trasaction for this descriptor, start if ch idle
+ * Submit the DMA transaction for this descriptor, start if ch idle
  */
 static dma_cookie_t intel_mid_dma_tx_submit(struct dma_async_tx_descriptor *tx)
 {
index 1bfa926..17b4219 100644 (file)
@@ -168,9 +168,9 @@ union intel_mid_dma_cfg_hi {
  * @active_list: current active descriptors
  * @queue: current queued up descriptors
  * @free_list: current free descriptors
- * @slave: dma slave struture
- * @descs_allocated: total number of decsiptors allocated
- * @dma: dma device struture pointer
+ * @slave: dma slave structure
+ * @descs_allocated: total number of descriptors allocated
+ * @dma: dma device structure pointer
  * @busy: bool representing if ch is busy (active txn) or not
  * @in_use: bool representing if ch is in use or not
  * @raw_tfr: raw trf interrupt received
index 60e6754..d2ff3fd 100644 (file)
@@ -22,7 +22,6 @@
 #define _IOAT_HW_H_
 
 /* PCI Configuration Space Values */
-#define IOAT_PCI_VID            0x8086
 #define IOAT_MMIO_BAR          0
 
 /* CB device ID's */
@@ -31,9 +30,6 @@
 #define IOAT_PCI_DID_SCNB       0x65FF
 #define IOAT_PCI_DID_SNB        0x402F
 
-#define IOAT_PCI_RID            0x00
-#define IOAT_PCI_SVID           0x8086
-#define IOAT_PCI_SID            0x8086
 #define IOAT_VER_1_2            0x12    /* Version 1.2 */
 #define IOAT_VER_2_0            0x20    /* Version 2.0 */
 #define IOAT_VER_3_0            0x30    /* Version 3.0 */
index f5843bc..5d3bbcd 100644 (file)
@@ -522,7 +522,7 @@ enum desc_status {
        /* In the DMAC pool */
        FREE,
        /*
-        * Allocted to some channel during prep_xxx
+        * Allocated to some channel during prep_xxx
         * Also may be sitting on the work_list.
         */
        PREP,
index ced9882..f72348d 100644 (file)
@@ -4446,7 +4446,7 @@ static int __devinit ppc440spe_adma_probe(struct platform_device *ofdev)
                ret = -ENOMEM;
                goto err_dma_alloc;
        }
-       dev_dbg(&ofdev->dev, "allocted descriptor pool virt 0x%p phys 0x%llx\n",
+       dev_dbg(&ofdev->dev, "allocated descriptor pool virt 0x%p phys 0x%llx\n",
                adev->dma_desc_pool_virt, (u64)adev->dma_desc_pool);
 
        regs = ioremap(res.start, resource_size(&res));
index 51e8e53..6d47373 100644 (file)
 /* LLI related structures */
 
 /**
- * struct d40_phy_lli - The basic configration register for each physical
+ * struct d40_phy_lli - The basic configuration register for each physical
  * channel.
  *
  * @reg_cfg: The configuration register.
index 47180a0..b6653a6 100644 (file)
@@ -391,7 +391,7 @@ static int i3200_probe1(struct pci_dev *pdev, int dev_idx)
                for (j = 0; j < nr_channels; j++) {
                        struct dimm_info *dimm = csrow->channels[j]->dimm;
 
-                       dimm->nr_pages = nr_pages / nr_channels;
+                       dimm->nr_pages = nr_pages;
                        dimm->grain = nr_pages << PAGE_SHIFT;
                        dimm->mtype = MEM_DDR2;
                        dimm->dtype = DEV_UNKNOWN;
index 39c6375..6a49dd0 100644 (file)
@@ -1012,6 +1012,10 @@ static void handle_channel(struct i5000_pvt *pvt, int slot, int channel,
                        /* add the number of COLUMN bits */
                        addrBits += MTR_DIMM_COLS_ADDR_BITS(mtr);
 
+                       /* Dual-rank memories have twice the size */
+                       if (dinfo->dual_rank)
+                               addrBits++;
+
                        addrBits += 6;  /* add 64 bits per DIMM */
                        addrBits -= 20; /* divide by 2^^20 */
                        addrBits -= 3;  /* 8 bits per bytes */
index f3b1f9f..5715b7c 100644 (file)
@@ -513,7 +513,8 @@ static int get_dimm_config(struct mem_ctl_info *mci)
 {
        struct sbridge_pvt *pvt = mci->pvt_info;
        struct dimm_info *dimm;
-       int i, j, banks, ranks, rows, cols, size, npages;
+       unsigned i, j, banks, ranks, rows, cols, npages;
+       u64 size;
        u32 reg;
        enum edac_type mode;
        enum mem_type mtype;
@@ -585,10 +586,10 @@ static int get_dimm_config(struct mem_ctl_info *mci)
                                cols = numcol(mtr);
 
                                /* DDR3 has 8 I/O banks */
-                               size = (rows * cols * banks * ranks) >> (20 - 3);
+                               size = ((u64)rows * cols * banks * ranks) >> (20 - 3);
                                npages = MiB_TO_PAGES(size);
 
-                               edac_dbg(0, "mc#%d: channel %d, dimm %d, %d Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
+                               edac_dbg(0, "mc#%d: channel %d, dimm %d, %Ld Mb (%d pages) bank: %d, rank: %d, row: %#x, col: %#x\n",
                                         pvt->sbridge_dev->mc, i, j,
                                         size, npages,
                                         banks, ranks, rows, cols);
index 427a289..6c19833 100644 (file)
@@ -434,6 +434,11 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
        regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
                           ARIZONA_JD1_ENA, ARIZONA_JD1_ENA);
 
+       ret = regulator_allow_bypass(info->micvdd, true);
+       if (ret != 0)
+               dev_warn(arizona->dev, "Failed to set MICVDD to bypass: %d\n",
+                        ret);
+
        pm_runtime_put(&pdev->dev);
 
        return 0;
index 8a420f1..ed94b4e 100644 (file)
@@ -308,6 +308,7 @@ static int lpc32xx_gpio_dir_output_p012(struct gpio_chip *chip, unsigned pin,
 {
        struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
 
+       __set_gpio_level_p012(group, pin, value);
        __set_gpio_dir_p012(group, pin, 0);
 
        return 0;
@@ -318,6 +319,7 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
 {
        struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
 
+       __set_gpio_level_p3(group, pin, value);
        __set_gpio_dir_p3(group, pin, 0);
 
        return 0;
@@ -326,6 +328,9 @@ static int lpc32xx_gpio_dir_output_p3(struct gpio_chip *chip, unsigned pin,
 static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin,
        int value)
 {
+       struct lpc32xx_gpio_chip *group = to_lpc32xx_gpio(chip);
+
+       __set_gpo_level_p3(group, pin, value);
        return 0;
 }
 
index ff23d88..3ca240b 100644 (file)
@@ -179,7 +179,7 @@ nouveau_abi16_ioctl_grobj_alloc(ABI16_IOCTL_ARGS)
                        return 0;
        } else
        if (init->class == 0x906e) {
-               NV_ERROR(dev, "906e not supported yet\n");
+               NV_DEBUG(dev, "906e not supported yet\n");
                return -EINVAL;
        }
 
index f704e94..f376c39 100644 (file)
@@ -124,6 +124,7 @@ nvc0_fb_init(struct drm_device *dev)
        priv = dev_priv->engine.fb.priv;
 
        nv_wr32(dev, 0x100c10, priv->r100c10 >> 8);
+       nv_mask(dev, 0x17e820, 0x00100000, 0x00000000); /* NV_PLTCG_INTR_EN */
        return 0;
 }
 
index 7d85553..cd39eb9 100644 (file)
@@ -373,7 +373,8 @@ nvc0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
 static void
 nvc0_fifo_isr(struct drm_device *dev)
 {
-       u32 stat = nv_rd32(dev, 0x002100);
+       u32 mask = nv_rd32(dev, 0x002140);
+       u32 stat = nv_rd32(dev, 0x002100) & mask;
 
        if (stat & 0x00000100) {
                NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
index e98d144..281bece 100644 (file)
@@ -345,7 +345,8 @@ nve0_fifo_isr_subfifo_intr(struct drm_device *dev, int unit)
 static void
 nve0_fifo_isr(struct drm_device *dev)
 {
-       u32 stat = nv_rd32(dev, 0x002100);
+       u32 mask = nv_rd32(dev, 0x002140);
+       u32 stat = nv_rd32(dev, 0x002100) & mask;
 
        if (stat & 0x00000100) {
                NV_INFO(dev, "PFIFO: unknown status 0x00000100\n");
index ba055e9..8d9dc44 100644 (file)
@@ -69,6 +69,13 @@ static int udl_get_modes(struct drm_connector *connector)
 static int udl_mode_valid(struct drm_connector *connector,
                          struct drm_display_mode *mode)
 {
+       struct udl_device *udl = connector->dev->dev_private;
+       if (!udl->sku_pixel_limit)
+               return 0;
+
+       if (mode->vdisplay * mode->hdisplay > udl->sku_pixel_limit)
+               return MODE_VIRTUAL_Y;
+
        return 0;
 }
 
index f2fb8f1..7e07433 100644 (file)
@@ -1018,7 +1018,7 @@ int vmw_event_fence_action_create(struct drm_file *file_priv,
        }
 
 
-       event = kzalloc(sizeof(event->event), GFP_KERNEL);
+       event = kzalloc(sizeof(*event), GFP_KERNEL);
        if (unlikely(event == NULL)) {
                DRM_ERROR("Failed to allocate an event.\n");
                ret = -ENOMEM;
index fbf4950..2af774a 100644 (file)
@@ -307,7 +307,6 @@ config HID_LOGITECH
 config HID_LOGITECH_DJ
        tristate "Logitech Unifying receivers full support"
        depends on HID_LOGITECH
-       default m
        ---help---
        Say Y if you want support for Logitech Unifying receivers and devices.
        Unifying receivers are capable of pairing up to 6 Logitech compliant
@@ -527,6 +526,14 @@ config HID_PICOLCD_LEDS
        ---help---
          Provide access to PicoLCD's GPO pins via leds class.
 
+config HID_PICOLCD_CIR
+       bool "CIR via RC class" if EXPERT
+       default !EXPERT
+       depends on HID_PICOLCD
+       depends on HID_PICOLCD=RC_CORE || RC_CORE=y
+       ---help---
+         Provide access to PicoLCD's CIR interface via remote control (LIRC).
+
 config HID_PRIMAX
        tristate "Primax non-fully HID-compliant devices"
        depends on USB_HID
@@ -534,6 +541,15 @@ config HID_PRIMAX
        Support for Primax devices that are not fully compliant with the
        HID standard.
 
+config HID_PS3REMOTE
+       tristate "Sony PS3 BD Remote Control"
+       depends on BT_HIDP
+       ---help---
+       Support for the Sony PS3 Blue-ray Disk Remote Control and Logitech
+       Harmony Adapter for PS3, which connect over Bluetooth.
+
+       Support for the 6-axis controllers is provided by HID_SONY.
+
 config HID_ROCCAT
        tristate "Roccat device support"
        depends on USB_HID
@@ -561,7 +577,9 @@ config HID_SONY
        tristate "Sony PS3 controller"
        depends on USB_HID
        ---help---
-       Support for Sony PS3 controller.
+       Support for Sony PS3 6-axis controllers.
+
+       Support for the Sony PS3 BD Remote is provided by HID_PS3REMOTE.
 
 config HID_SPEEDLINK
        tristate "Speedlink VAD Cezanne mouse support"
index f975485..5a3690f 100644 (file)
@@ -69,7 +69,28 @@ obj-$(CONFIG_HID_PRODIKEYS)  += hid-prodikeys.o
 obj-$(CONFIG_HID_PANTHERLORD)  += hid-pl.o
 obj-$(CONFIG_HID_PETALYNX)     += hid-petalynx.o
 obj-$(CONFIG_HID_PICOLCD)      += hid-picolcd.o
+hid-picolcd-y                  += hid-picolcd_core.o
+ifdef CONFIG_HID_PICOLCD_FB
+hid-picolcd-y                  += hid-picolcd_fb.o
+endif
+ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+hid-picolcd-y                  += hid-picolcd_backlight.o
+endif
+ifdef CONFIG_HID_PICOLCD_LCD
+hid-picolcd-y                  += hid-picolcd_lcd.o
+endif
+ifdef CONFIG_HID_PICOLCD_LEDS
+hid-picolcd-y                  += hid-picolcd_leds.o
+endif
+ifdef CONFIG_HID_PICOLCD_CIR
+hid-picolcd-y                  += hid-picolcd_cir.o
+endif
+ifdef CONFIG_DEBUG_FS
+hid-picolcd-y                  += hid-picolcd_debugfs.o
+endif
+
 obj-$(CONFIG_HID_PRIMAX)       += hid-primax.o
+obj-$(CONFIG_HID_PS3REMOTE)    += hid-ps3remote.o
 obj-$(CONFIG_HID_ROCCAT)       += hid-roccat.o hid-roccat-common.o \
        hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \
        hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \
index 902d1df..0a23988 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 585344b..06ebdbb 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
  */
 
index ba64b04..7968187 100644 (file)
@@ -9,7 +9,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 #include <linux/device.h>
index a1a765a..a1a5a12 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 888ece6..af034d3 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 8bcd168..2cd6880 100644 (file)
@@ -126,7 +126,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
 
        if (parser->collection_stack_ptr == HID_COLLECTION_STACK_SIZE) {
                hid_err(parser->device, "collection stack overflow\n");
-               return -1;
+               return -EINVAL;
        }
 
        if (parser->device->maxcollection == parser->device->collection_size) {
@@ -134,7 +134,7 @@ static int open_collection(struct hid_parser *parser, unsigned type)
                                parser->device->collection_size * 2, GFP_KERNEL);
                if (collection == NULL) {
                        hid_err(parser->device, "failed to reallocate collection array\n");
-                       return -1;
+                       return -ENOMEM;
                }
                memcpy(collection, parser->device->collection,
                        sizeof(struct hid_collection) *
@@ -170,7 +170,7 @@ static int close_collection(struct hid_parser *parser)
 {
        if (!parser->collection_stack_ptr) {
                hid_err(parser->device, "collection stack underflow\n");
-               return -1;
+               return -EINVAL;
        }
        parser->collection_stack_ptr--;
        return 0;
@@ -374,7 +374,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
 
        case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
                parser->global.report_size = item_udata(item);
-               if (parser->global.report_size > 96) {
+               if (parser->global.report_size > 128) {
                        hid_err(parser->device, "invalid report_size %d\n",
                                        parser->global.report_size);
                        return -1;
@@ -757,6 +757,7 @@ int hid_open_report(struct hid_device *device)
        struct hid_item item;
        unsigned int size;
        __u8 *start;
+       __u8 *buf;
        __u8 *end;
        int ret;
        static int (*dispatch_type[])(struct hid_parser *parser,
@@ -775,12 +776,21 @@ int hid_open_report(struct hid_device *device)
                return -ENODEV;
        size = device->dev_rsize;
 
+       buf = kmemdup(start, size, GFP_KERNEL);
+       if (buf == NULL)
+               return -ENOMEM;
+
        if (device->driver->report_fixup)
-               start = device->driver->report_fixup(device, start, &size);
+               start = device->driver->report_fixup(device, buf, &size);
+       else
+               start = buf;
 
-       device->rdesc = kmemdup(start, size, GFP_KERNEL);
-       if (device->rdesc == NULL)
+       start = kmemdup(start, size, GFP_KERNEL);
+       kfree(buf);
+       if (start == NULL)
                return -ENOMEM;
+
+       device->rdesc = start;
        device->rsize = size;
 
        parser = vzalloc(sizeof(struct hid_parser));
@@ -1448,7 +1458,14 @@ void hid_disconnect(struct hid_device *hdev)
 }
 EXPORT_SYMBOL_GPL(hid_disconnect);
 
-/* a list of devices for which there is a specialized driver on HID bus */
+/*
+ * A list of devices for which there is a specialized driver on HID bus.
+ *
+ * Please note that for multitouch devices (driven by hid-multitouch driver),
+ * there is a proper autodetection and autoloading in place (based on presence
+ * of HID_DG_CONTACTID), so those devices don't need to be added to this list,
+ * as we are doing the right thing in hid_scan_usage().
+ */
 static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
        { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
@@ -1566,6 +1583,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER_2) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RECEIVER) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_DESKTOP) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_EDGE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_DINOVO_MINI) },
@@ -1627,6 +1645,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ORTEK, USB_DEVICE_ID_ORTEK_WKB2000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PETALYNX, USB_DEVICE_ID_PETALYNX_MAXTER_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
+#if IS_ENABLED(CONFIG_HID_ROCCAT)
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
@@ -1635,10 +1654,12 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) },
+#endif
        { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SKYCABLE, USB_DEVICE_ID_SKYCABLE_WIRELESS_PRESENTER) },
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
        { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER) },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_CONTROLLER) },
@@ -1663,6 +1684,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SMARTJOY_PLUS) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_SUPER_JOY_BOX_3) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD) },
index 9e43aac..3e159a5 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 01dd9a7..933fff0 100644 (file)
@@ -911,15 +911,21 @@ static void hid_dump_input_mapping(struct hid_device *hid, struct seq_file *f)
 
 }
 
-
 static int hid_debug_rdesc_show(struct seq_file *f, void *p)
 {
        struct hid_device *hdev = f->private;
+       const __u8 *rdesc = hdev->rdesc;
+       unsigned rsize = hdev->rsize;
        int i;
 
+       if (!rdesc) {
+               rdesc = hdev->dev_rdesc;
+               rsize = hdev->dev_rsize;
+       }
+
        /* dump HID report descriptor */
-       for (i = 0; i < hdev->rsize; i++)
-               seq_printf(f, "%02x ", hdev->rdesc[i]);
+       for (i = 0; i < rsize; i++)
+               seq_printf(f, "%02x ", rdesc[i]);
        seq_printf(f, "\n\n");
 
        /* dump parsed data and input mappings */
index ca1163e..6540af2 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index e88b951..4442c30 100644 (file)
@@ -4,7 +4,6 @@
  *  Copyright (c) 1999 Andreas Gal
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2006-2008 Jiri Kosina
  */
index 4e75421..ff295e6 100644 (file)
@@ -100,8 +100,7 @@ static void holtekff_send(struct holtekff_device *holtekff,
                holtekff->field->value[i] = data[i];
        }
 
-       dbg_hid("sending %02x %02x %02x %02x %02x %02x %02x\n", data[0],
-               data[1], data[2], data[3], data[4], data[5], data[6]);
+       dbg_hid("sending %*ph\n", 7, data);
 
        usbhid_submit_report(hid, holtekff->field->report, USB_DIR_OUT);
 }
index 1dcb76f..ca4d83e 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  */
 
 /*
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72FA      0x72fa
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7302      0x7302
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349      0x7349
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7      0x73f7
 #define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001      0xa001
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224      0x7224
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0      0x72d0
+#define USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4      0x72c4
 
 #define USB_VENDOR_ID_ELECOM           0x056e
 #define USB_DEVICE_ID_ELECOM_BM084     0x0061
 #define USB_VENDOR_ID_EMS              0x2006
 #define USB_DEVICE_ID_EMS_TRIO_LINKER_PLUS_II 0x0118
 
+#define USB_VENDOR_ID_FLATFROG         0x25b5
+#define USB_DEVICE_ID_MULTITOUCH_3200  0x0002
+
 #define USB_VENDOR_ID_ESSENTIAL_REALITY        0x0d7f
 #define USB_DEVICE_ID_ESSENTIAL_REALITY_P5 0x0100
 
 #define USB_VENDOR_ID_EZKEY            0x0518
 #define USB_DEVICE_ID_BTC_8193         0x0002
 
+#define USB_VENDOR_ID_FREESCALE                0x15A2
+#define USB_DEVICE_ID_FREESCALE_MX28   0x004F
+
 #define USB_VENDOR_ID_FRUCTEL  0x25B6
 #define USB_DEVICE_ID_GAMETEL_MT_MODE  0x0002
 
 
 #define USB_VENDOR_ID_GENERAL_TOUCH    0x0dfc
 #define USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS 0x0003
+#define USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS 0x0100
 
 #define USB_VENDOR_ID_GLAB             0x06c2
 #define USB_DEVICE_ID_4_PHIDGETSERVO_30        0x0038
 #define USB_DEVICE_ID_LOGITECH_RECEIVER        0xc101
 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST  0xc110
 #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
+#define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD_CORD  0xc20a
 #define USB_DEVICE_ID_LOGITECH_RUMBLEPAD       0xc211
 #define USB_DEVICE_ID_LOGITECH_EXTREME_3D      0xc215
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH             0x3000
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001                0x3001
 #define USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008                0x3008
-#define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN  0x3001
 
 #define USB_VENDOR_ID_ROCCAT           0x1e7d
 #define USB_DEVICE_ID_ROCCAT_ARVO      0x30d4
 
 #define USB_VENDOR_ID_SONY                     0x054c
 #define USB_DEVICE_ID_SONY_VAIO_VGX_MOUSE      0x024b
+#define USB_DEVICE_ID_SONY_PS3_BDREMOTE                0x0306
 #define USB_DEVICE_ID_SONY_PS3_CONTROLLER      0x0268
 #define USB_DEVICE_ID_SONY_NAVIGATION_CONTROLLER       0x042f
 
 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U   0x0005
 #define USB_DEVICE_ID_UCLOGIC_TABLET_WP1062    0x0064
 #define USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850  0x0522
+#define USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60    0x0781
 
 #define USB_VENDOR_ID_UNITEC   0x227d
 #define USB_DEVICE_ID_UNITEC_USB_TOUCH_0709    0x0709
index 811bfad..d917c0d 100644 (file)
@@ -1154,6 +1154,7 @@ static void report_features(struct hid_device *hid)
 
 int hidinput_connect(struct hid_device *hid, unsigned int force)
 {
+       struct hid_driver *drv = hid->driver;
        struct hid_report *report;
        struct hid_input *hidinput = NULL;
        struct input_dev *input_dev;
@@ -1228,6 +1229,8 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                                 * UGCI) cram a lot of unrelated inputs into the
                                 * same interface. */
                                hidinput->report = report;
+                               if (drv->input_configured)
+                                       drv->input_configured(hid, hidinput);
                                if (input_register_device(hidinput->input))
                                        goto out_cleanup;
                                hidinput = NULL;
@@ -1235,8 +1238,12 @@ int hidinput_connect(struct hid_device *hid, unsigned int force)
                }
        }
 
-       if (hidinput && input_register_device(hidinput->input))
-               goto out_cleanup;
+       if (hidinput) {
+               if (drv->input_configured)
+                       drv->input_configured(hid, hidinput);
+               if (input_register_device(hidinput->input))
+                       goto out_cleanup;
+       }
 
        return 0;
 
index c4fe9bd..22bc14a 100644 (file)
@@ -24,7 +24,7 @@ static int ts_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
-       if ((usage->hid & HID_USAGE_PAGE) != 0x0ffbc0000)
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_LOGIVENDOR)
                return 0;
 
        switch (usage->hid & HID_USAGE) {
index 60c4e1e..cea016e 100644 (file)
@@ -56,9 +56,8 @@ static int tpkbd_input_mapping(struct hid_device *hdev,
 static int tpkbd_features_set(struct hid_device *hdev)
 {
        struct hid_report *report;
-       struct tpkbd_data_pointer *data_pointer;
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
        report = hdev->report_enum[HID_FEATURE_REPORT].report_id_hash[4];
 
        report->field[0]->value[0]  = data_pointer->press_to_select   ? 0x01 : 0x02;
@@ -77,14 +76,8 @@ static ssize_t pointer_press_to_select_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
-
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->press_to_select);
 }
@@ -94,16 +87,10 @@ static ssize_t pointer_press_to_select_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value))
                return -EINVAL;
        if (value < 0 || value > 1)
@@ -119,14 +106,8 @@ static ssize_t pointer_dragging_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
-
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->dragging);
 }
@@ -136,16 +117,10 @@ static ssize_t pointer_dragging_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value))
                return -EINVAL;
        if (value < 0 || value > 1)
@@ -161,14 +136,8 @@ static ssize_t pointer_release_to_select_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
-
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->release_to_select);
 }
@@ -178,16 +147,10 @@ static ssize_t pointer_release_to_select_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value))
                return -EINVAL;
        if (value < 0 || value > 1)
@@ -203,14 +166,8 @@ static ssize_t pointer_select_right_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
-
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n", data_pointer->select_right);
 }
@@ -220,16 +177,10 @@ static ssize_t pointer_select_right_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value))
                return -EINVAL;
        if (value < 0 || value > 1)
@@ -245,14 +196,8 @@ static ssize_t pointer_sensitivity_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
-
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n",
                data_pointer->sensitivity);
@@ -263,16 +208,10 @@ static ssize_t pointer_sensitivity_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
                return -EINVAL;
 
@@ -286,14 +225,10 @@ static ssize_t pointer_press_speed_show(struct device *dev,
                struct device_attribute *attr,
                char *buf)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
+       data_pointer = hid_get_drvdata(hdev);
 
        return snprintf(buf, PAGE_SIZE, "%u\n",
                data_pointer->press_speed);
@@ -304,16 +239,10 @@ static ssize_t pointer_press_speed_store(struct device *dev,
                const char *buf,
                size_t count)
 {
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int value;
 
-       hdev = container_of(dev, struct hid_device, dev);
-       if (hdev == NULL)
-               return -ENODEV;
-
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (kstrtoint(buf, 10, &value) || value < 1 || value > 255)
                return -EINVAL;
 
@@ -370,15 +299,11 @@ static const struct attribute_group tpkbd_attr_group_pointer = {
 static enum led_brightness tpkbd_led_brightness_get(
                        struct led_classdev *led_cdev)
 {
-       struct device *dev;
-       struct hid_device *hdev;
-       struct tpkbd_data_pointer *data_pointer;
+       struct device *dev = led_cdev->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        int led_nr = 0;
 
-       dev = led_cdev->dev->parent;
-       hdev = container_of(dev, struct hid_device, dev);
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (led_cdev == &data_pointer->led_micmute)
                led_nr = 1;
 
@@ -390,16 +315,12 @@ static enum led_brightness tpkbd_led_brightness_get(
 static void tpkbd_led_brightness_set(struct led_classdev *led_cdev,
                        enum led_brightness value)
 {
-       struct device *dev;
-       struct hid_device *hdev;
+       struct device *dev = led_cdev->dev->parent;
+       struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
        struct hid_report *report;
-       struct tpkbd_data_pointer *data_pointer;
        int led_nr = 0;
 
-       dev = led_cdev->dev->parent;
-       hdev = container_of(dev, struct hid_device, dev);
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        if (led_cdev == &data_pointer->led_micmute)
                led_nr = 1;
 
@@ -508,13 +429,11 @@ err_free:
 
 static void tpkbd_remove_tp(struct hid_device *hdev)
 {
-       struct tpkbd_data_pointer *data_pointer;
+       struct tpkbd_data_pointer *data_pointer = hid_get_drvdata(hdev);
 
        sysfs_remove_group(&hdev->dev.kobj,
                        &tpkbd_attr_group_pointer);
 
-       data_pointer = (struct tpkbd_data_pointer *) hid_get_drvdata(hdev);
-
        led_classdev_unregister(&data_pointer->led_micmute);
        led_classdev_unregister(&data_pointer->led_mute);
 
index fc37ed6..a2f8e88 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2010 Hendrik Iben
  */
@@ -109,7 +108,7 @@ static __u8 dfp_rdesc_fixed[] = {
 static __u8 *lg_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 
        if ((drv_data->quirks & LG_RDESC) && *rsize >= 90 && rdesc[83] == 0x26 &&
                        rdesc[84] == 0x8c && rdesc[85] == 0x02) {
@@ -278,7 +277,7 @@ static int lg_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                  0,  0,  0,  0,  0,183,184,185,186,187,
                188,189,190,191,192,193,194,  0,  0,  0
        };
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
        unsigned int hid = usage->hid;
 
        if (hdev->product == USB_DEVICE_ID_LOGITECH_RECEIVER &&
@@ -319,7 +318,7 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 
        if ((drv_data->quirks & LG_BAD_RELATIVE_KEYS) && usage->type == EV_KEY &&
                        (field->flags & HID_MAIN_ITEM_RELATIVE))
@@ -335,13 +334,16 @@ static int lg_input_mapped(struct hid_device *hdev, struct hid_input *hi,
 static int lg_event(struct hid_device *hdev, struct hid_field *field,
                struct hid_usage *usage, __s32 value)
 {
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
 
        if ((drv_data->quirks & LG_INVERT_HWHEEL) && usage->code == REL_HWHEEL) {
                input_event(field->hidinput->input, usage->type, usage->code,
                                -value);
                return 1;
        }
+       if (drv_data->quirks & LG_FF4) {
+               return lg4ff_adjust_input_event(hdev, field, usage, value, drv_data);
+       }
 
        return 0;
 }
@@ -358,7 +360,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
                return -ENOMEM;
        }
        drv_data->quirks = id->driver_data;
-       
+
        hid_set_drvdata(hdev, (void *)drv_data);
 
        if (drv_data->quirks & LG_NOGET)
@@ -380,7 +382,7 @@ static int lg_probe(struct hid_device *hdev, const struct hid_device_id *id)
        }
 
        /* Setup wireless link with Logitech Wii wheel */
-       if(hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
+       if (hdev->product == USB_DEVICE_ID_LOGITECH_WII_WHEEL) {
                unsigned char buf[] = { 0x00, 0xAF,  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
 
                ret = hdev->hid_output_raw_report(hdev, buf, sizeof(buf), HID_FEATURE_REPORT);
@@ -416,7 +418,7 @@ err_free:
 
 static void lg_remove(struct hid_device *hdev)
 {
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hdev);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hdev);
        if (drv_data->quirks & LG_FF4)
                lg4ff_deinit(hdev);
 
@@ -476,7 +478,7 @@ static const struct hid_device_id lg_devices[] = {
                .driver_data = LG_NOGET | LG_FF4 },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WII_WHEEL),
                .driver_data = LG_FF4 },
-       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG ),
+       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_WINGMAN_FFG),
                .driver_data = LG_FF },
        { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_RUMBLEPAD2),
                .driver_data = LG_FF2 },
index d64cf8d..142ce3f 100644 (file)
@@ -25,9 +25,13 @@ static inline int lg3ff_init(struct hid_device *hdev) { return -1; }
 #endif
 
 #ifdef CONFIG_LOGIWHEELS_FF
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+                            struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data);
 int lg4ff_init(struct hid_device *hdev);
 int lg4ff_deinit(struct hid_device *hdev);
 #else
+static inline int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+                                          struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data) { return 0; }
 static inline int lg4ff_init(struct hid_device *hdev) { return -1; }
 static inline int lg4ff_deinit(struct hid_device *hdev) { return -1; }
 #endif
index f3390ee..d7947c7 100644 (file)
 #define G27_REV_MAJ 0x12
 #define G27_REV_MIN 0x38
 
+#define DFP_X_MIN 0
+#define DFP_X_MAX 16383
+#define DFP_PEDAL_MIN 0
+#define DFP_PEDAL_MAX 255
+
 #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
 
 static void hid_lg4ff_set_range_dfp(struct hid_device *hid, u16 range);
@@ -53,6 +58,7 @@ static ssize_t lg4ff_range_store(struct device *dev, struct device_attribute *at
 static DEVICE_ATTR(range, S_IRWXU | S_IRWXG | S_IRWXO, lg4ff_range_show, lg4ff_range_store);
 
 struct lg4ff_device_entry {
+       __u32 product_id;
        __u16 range;
        __u16 min_range;
        __u16 max_range;
@@ -129,26 +135,77 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
        {G27_REV_MAJ,  G27_REV_MIN,  &native_g27},      /* G27 */
 };
 
+/* Recalculates X axis value accordingly to currently selected range */
+static __s32 lg4ff_adjust_dfp_x_axis(__s32 value, __u16 range)
+{
+       __u16 max_range;
+       __s32 new_value;
+
+       if (range == 900)
+               return value;
+       else if (range == 200)
+               return value;
+       else if (range < 200)
+               max_range = 200;
+       else
+               max_range = 900;
+
+       new_value = 8192 + mult_frac(value - 8192, max_range, range);
+       if (new_value < 0)
+               return 0;
+       else if (new_value > 16383)
+               return 16383;
+       else
+               return new_value;
+}
+
+int lg4ff_adjust_input_event(struct hid_device *hid, struct hid_field *field,
+                            struct hid_usage *usage, __s32 value, struct lg_drv_data *drv_data)
+{
+       struct lg4ff_device_entry *entry = drv_data->device_props;
+       __s32 new_value = 0;
+
+       if (!entry) {
+               hid_err(hid, "Device properties not found");
+               return 0;
+       }
+
+       switch (entry->product_id) {
+       case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+               switch (usage->code) {
+               case ABS_X:
+                       new_value = lg4ff_adjust_dfp_x_axis(value, entry->range);
+                       input_event(field->hidinput->input, usage->type, usage->code, new_value);
+                       return 1;
+               default:
+                       return 0;
+               }
+       default:
+               return 0;
+       }
+}
+
 static int hid_lg4ff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
 {
        struct hid_device *hid = input_get_drvdata(dev);
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
        int x;
 
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+#define CLAMP(x) do { if (x < 0) x = 0; else if (x > 0xff) x = 0xff; } while (0)
 
        switch (effect->type) {
        case FF_CONSTANT:
                x = effect->u.ramp.start_level + 0x80;  /* 0x80 is no force */
                CLAMP(x);
-               report->field[0]->value[0] = 0x11;      /* Slot 1 */
-               report->field[0]->value[1] = 0x08;
-               report->field[0]->value[2] = x;
-               report->field[0]->value[3] = 0x80;
-               report->field[0]->value[4] = 0x00;
-               report->field[0]->value[5] = 0x00;
-               report->field[0]->value[6] = 0x00;
+               value[0] = 0x11;        /* Slot 1 */
+               value[1] = 0x08;
+               value[2] = x;
+               value[3] = 0x80;
+               value[4] = 0x00;
+               value[5] = 0x00;
+               value[6] = 0x00;
 
                usbhid_submit_report(hid, report, USB_DIR_OUT);
                break;
@@ -163,14 +220,15 @@ static void hid_lg4ff_set_autocenter_default(struct input_dev *dev, u16 magnitud
        struct hid_device *hid = input_get_drvdata(dev);
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
 
-       report->field[0]->value[0] = 0xfe;
-       report->field[0]->value[1] = 0x0d;
-       report->field[0]->value[2] = magnitude >> 13;
-       report->field[0]->value[3] = magnitude >> 13;
-       report->field[0]->value[4] = magnitude >> 8;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       value[0] = 0xfe;
+       value[1] = 0x0d;
+       value[2] = magnitude >> 13;
+       value[3] = magnitude >> 13;
+       value[4] = magnitude >> 8;
+       value[5] = 0x00;
+       value[6] = 0x00;
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 }
@@ -181,16 +239,16 @@ static void hid_lg4ff_set_autocenter_ffex(struct input_dev *dev, u16 magnitude)
        struct hid_device *hid = input_get_drvdata(dev);
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
        magnitude = magnitude * 90 / 65535;
-       
 
-       report->field[0]->value[0] = 0xfe;
-       report->field[0]->value[1] = 0x03;
-       report->field[0]->value[2] = magnitude >> 14;
-       report->field[0]->value[3] = magnitude >> 14;
-       report->field[0]->value[4] = magnitude;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       value[0] = 0xfe;
+       value[1] = 0x03;
+       value[2] = magnitude >> 14;
+       value[3] = magnitude >> 14;
+       value[4] = magnitude;
+       value[5] = 0x00;
+       value[6] = 0x00;
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 }
@@ -200,15 +258,17 @@ static void hid_lg4ff_set_range_g25(struct hid_device *hid, u16 range)
 {
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+       __s32 *value = report->field[0]->value;
+
        dbg_hid("G25/G27/DFGT: setting range to %u\n", range);
 
-       report->field[0]->value[0] = 0xf8;
-       report->field[0]->value[1] = 0x81;
-       report->field[0]->value[2] = range & 0x00ff;
-       report->field[0]->value[3] = (range & 0xff00) >> 8;
-       report->field[0]->value[4] = 0x00;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       value[0] = 0xf8;
+       value[1] = 0x81;
+       value[2] = range & 0x00ff;
+       value[3] = (range & 0xff00) >> 8;
+       value[4] = 0x00;
+       value[5] = 0x00;
+       value[6] = 0x00;
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 }
@@ -219,16 +279,18 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
        int start_left, start_right, full_range;
+       __s32 *value = report->field[0]->value;
+
        dbg_hid("Driving Force Pro: setting range to %u\n", range);
 
        /* Prepare "coarse" limit command */
-       report->field[0]->value[0] = 0xf8;
-       report->field[0]->value[1] = 0x00;      /* Set later */
-       report->field[0]->value[2] = 0x00;
-       report->field[0]->value[3] = 0x00;
-       report->field[0]->value[4] = 0x00;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       value[0] = 0xf8;
+       value[1] = 0x00;        /* Set later */
+       value[2] = 0x00;
+       value[3] = 0x00;
+       value[4] = 0x00;
+       value[5] = 0x00;
+       value[6] = 0x00;
 
        if (range > 200) {
                report->field[0]->value[1] = 0x03;
@@ -240,13 +302,13 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 
        /* Prepare "fine" limit command */
-       report->field[0]->value[0] = 0x81;
-       report->field[0]->value[1] = 0x0b;
-       report->field[0]->value[2] = 0x00;
-       report->field[0]->value[3] = 0x00;
-       report->field[0]->value[4] = 0x00;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       value[0] = 0x81;
+       value[1] = 0x0b;
+       value[2] = 0x00;
+       value[3] = 0x00;
+       value[4] = 0x00;
+       value[5] = 0x00;
+       value[6] = 0x00;
 
        if (range == 200 || range == 900) {     /* Do not apply any fine limit */
                usbhid_submit_report(hid, report, USB_DIR_OUT);
@@ -257,11 +319,11 @@ static void hid_lg4ff_set_range_dfp(struct hid_device *hid, __u16 range)
        start_left = (((full_range - range + 1) * 2047) / full_range);
        start_right = 0xfff - start_left;
 
-       report->field[0]->value[2] = start_left >> 4;
-       report->field[0]->value[3] = start_right >> 4;
-       report->field[0]->value[4] = 0xff;
-       report->field[0]->value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
-       report->field[0]->value[6] = 0xff;
+       value[2] = start_left >> 4;
+       value[3] = start_right >> 4;
+       value[4] = 0xff;
+       value[5] = (start_right & 0xe) << 4 | (start_left & 0xe);
+       value[6] = 0xff;
 
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 }
@@ -344,14 +406,15 @@ static void lg4ff_set_leds(struct hid_device *hid, __u8 leds)
 {
        struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
        struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
-
-       report->field[0]->value[0] = 0xf8;
-       report->field[0]->value[1] = 0x12;
-       report->field[0]->value[2] = leds;
-       report->field[0]->value[3] = 0x00;
-       report->field[0]->value[4] = 0x00;
-       report->field[0]->value[5] = 0x00;
-       report->field[0]->value[6] = 0x00;
+       __s32 *value = report->field[0]->value;
+
+       value[0] = 0xf8;
+       value[1] = 0x12;
+       value[2] = leds;
+       value[3] = 0x00;
+       value[4] = 0x00;
+       value[5] = 0x00;
+       value[6] = 0x00;
        usbhid_submit_report(hid, report, USB_DIR_OUT);
 }
 
@@ -360,7 +423,7 @@ static void lg4ff_led_set_brightness(struct led_classdev *led_cdev,
 {
        struct device *dev = led_cdev->dev->parent;
        struct hid_device *hid = container_of(dev, struct hid_device, dev);
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hid);
        struct lg4ff_device_entry *entry;
        int i, state = 0;
 
@@ -395,7 +458,7 @@ static enum led_brightness lg4ff_led_get_brightness(struct led_classdev *led_cde
 {
        struct device *dev = led_cdev->dev->parent;
        struct hid_device *hid = container_of(dev, struct hid_device, dev);
-       struct lg_drv_data *drv_data = (struct lg_drv_data *)hid_get_drvdata(hid);
+       struct lg_drv_data *drv_data = hid_get_drvdata(hid);
        struct lg4ff_device_entry *entry;
        int i, value = 0;
 
@@ -501,7 +564,7 @@ int lg4ff_init(struct hid_device *hid)
        /* Check if autocentering is available and
         * set the centering force to zero by default */
        if (test_bit(FF_AUTOCENTER, dev->ffbit)) {
-               if(rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN)  /* Formula Force EX expects different autocentering command */
+               if (rev_maj == FFEX_REV_MAJ && rev_min == FFEX_REV_MIN) /* Formula Force EX expects different autocentering command */
                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_ffex;
                else
                        dev->ff->set_autocenter = hid_lg4ff_set_autocenter_default;
@@ -524,6 +587,7 @@ int lg4ff_init(struct hid_device *hid)
        }
        drv_data->device_props = entry;
 
+       entry->product_id = lg4ff_devices[i].product_id;
        entry->min_range = lg4ff_devices[i].min_range;
        entry->max_range = lg4ff_devices[i].max_range;
        entry->set_range = lg4ff_devices[i].set_range;
@@ -534,6 +598,18 @@ int lg4ff_init(struct hid_device *hid)
                return error;
        dbg_hid("sysfs interface created\n");
 
+       /* Set default axes parameters */
+       switch (lg4ff_devices[i].product_id) {
+       case USB_DEVICE_ID_LOGITECH_DFP_WHEEL:
+               dbg_hid("Setting axes parameters for Driving Force Pro\n");
+               input_set_abs_params(dev, ABS_X, DFP_X_MIN, DFP_X_MAX, 0, 0);
+               input_set_abs_params(dev, ABS_Y, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+               input_set_abs_params(dev, ABS_RZ, DFP_PEDAL_MIN, DFP_PEDAL_MAX, 0, 0);
+               break;
+       default:
+               break;
+       }
+
        /* Set the maximum range to start with */
        entry->range = entry->max_range;
        if (entry->set_range != NULL)
@@ -594,6 +670,8 @@ out:
        return 0;
 }
 
+
+
 int lg4ff_deinit(struct hid_device *hid)
 {
        struct lg4ff_device_entry *entry;
index 7364726..25ddf3e 100644 (file)
@@ -392,7 +392,7 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
 
        __set_bit(EV_ABS, input->evbit);
 
-       error = input_mt_init_slots(input, 16);
+       error = input_mt_init_slots(input, 16, 0);
        if (error)
                return error;
        input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
index e5c699b..3acdcfc 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index dedf757..cd3643e 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 59c8b5c..3eb02b9 100644 (file)
@@ -51,12 +51,12 @@ MODULE_LICENSE("GPL");
 #define MT_QUIRK_VALID_IS_INRANGE      (1 << 5)
 #define MT_QUIRK_VALID_IS_CONFIDENCE   (1 << 6)
 #define MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE   (1 << 8)
+#define MT_QUIRK_NO_AREA               (1 << 9)
 
 struct mt_slot {
        __s32 x, y, p, w, h;
        __s32 contactid;        /* the device ContactID assigned to this slot */
        bool touch_state;       /* is the touch valid? */
-       bool seen_in_this_frame;/* has this slot been updated */
 };
 
 struct mt_class {
@@ -92,8 +92,9 @@ struct mt_device {
        __u8 touches_by_report; /* how many touches are present in one report:
                                * 1 means we should use a serial protocol
                                * > 1 means hybrid (multitouch) protocol */
+       bool serial_maybe;      /* need to check for serial protocol */
        bool curvalid;          /* is the current contact valid? */
-       struct mt_slot *slots;
+       unsigned mt_flags;      /* flags to pass to input-mt */
 };
 
 /* classes of device behavior */
@@ -115,6 +116,9 @@ struct mt_device {
 #define MT_CLS_EGALAX_SERIAL                   0x0104
 #define MT_CLS_TOPSEED                         0x0105
 #define MT_CLS_PANASONIC                       0x0106
+#define MT_CLS_FLATFROG                                0x0107
+#define MT_CLS_GENERALTOUCH_TWOFINGERS         0x0108
+#define MT_CLS_GENERALTOUCH_PWT_TENFINGERS     0x0109
 
 #define MT_DEFAULT_MAXCONTACT  10
 
@@ -134,25 +138,6 @@ static int cypress_compute_slot(struct mt_device *td)
                return -1;
 }
 
-static int find_slot_from_contactid(struct mt_device *td)
-{
-       int i;
-       for (i = 0; i < td->maxcontacts; ++i) {
-               if (td->slots[i].contactid == td->curdata.contactid &&
-                       td->slots[i].touch_state)
-                       return i;
-       }
-       for (i = 0; i < td->maxcontacts; ++i) {
-               if (!td->slots[i].seen_in_this_frame &&
-                       !td->slots[i].touch_state)
-                       return i;
-       }
-       /* should not occurs. If this happens that means
-        * that the device sent more touches that it says
-        * in the report descriptor. It is ignored then. */
-       return -1;
-}
-
 static struct mt_class mt_classes[] = {
        { .name = MT_CLS_DEFAULT,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP },
@@ -190,7 +175,9 @@ static struct mt_class mt_classes[] = {
                        MT_QUIRK_SLOT_IS_CONTACTID,
                .sn_move = 2048,
                .sn_width = 128,
-               .sn_height = 128 },
+               .sn_height = 128,
+               .maxcontacts = 60,
+       },
        { .name = MT_CLS_CYPRESS,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
                        MT_QUIRK_CYPRESS,
@@ -215,7 +202,24 @@ static struct mt_class mt_classes[] = {
        { .name = MT_CLS_PANASONIC,
                .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP,
                .maxcontacts = 4 },
+       { .name = MT_CLS_GENERALTOUCH_TWOFINGERS,
+               .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+                       MT_QUIRK_VALID_IS_INRANGE |
+                       MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+               .maxcontacts = 2
+       },
+       { .name = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+                       MT_QUIRK_SLOT_IS_CONTACTNUMBER,
+               .maxcontacts = 10
+       },
 
+       { .name = MT_CLS_FLATFROG,
+               .quirks = MT_QUIRK_NOT_SEEN_MEANS_UP |
+                       MT_QUIRK_NO_AREA,
+               .sn_move = 2048,
+               .maxcontacts = 40,
+       },
        { }
 };
 
@@ -319,24 +323,16 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
        * We need to ignore fields that belong to other collections
        * such as Mouse that might have the same GenericDesktop usages. */
        if (field->application == HID_DG_TOUCHSCREEN)
-               set_bit(INPUT_PROP_DIRECT, hi->input->propbit);
+               td->mt_flags |= INPUT_MT_DIRECT;
        else if (field->application != HID_DG_TOUCHPAD)
                return 0;
 
-       /* In case of an indirect device (touchpad), we need to add
-        * specific BTN_TOOL_* to be handled by the synaptics xorg
-        * driver.
-        * We also consider that touchscreens providing buttons are touchpads.
+       /*
+        * Model touchscreens providing buttons as touchpads.
         */
        if (field->application == HID_DG_TOUCHPAD ||
-           (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON ||
-           cls->is_indirect) {
-               set_bit(INPUT_PROP_POINTER, hi->input->propbit);
-               set_bit(BTN_TOOL_FINGER, hi->input->keybit);
-               set_bit(BTN_TOOL_DOUBLETAP, hi->input->keybit);
-               set_bit(BTN_TOOL_TRIPLETAP, hi->input->keybit);
-               set_bit(BTN_TOOL_QUADTAP, hi->input->keybit);
-       }
+           (usage->hid & HID_USAGE_PAGE) == HID_UP_BUTTON)
+               td->mt_flags |= INPUT_MT_POINTER;
 
        /* eGalax devices provide a Digitizer.Stylus input which overrides
         * the correct Digitizers.Finger X/Y ranges.
@@ -353,8 +349,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                        EV_ABS, ABS_MT_POSITION_X);
                        set_abs(hi->input, ABS_MT_POSITION_X, field,
                                cls->sn_move);
-                       /* touchscreen emulation */
-                       set_abs(hi->input, ABS_X, field, cls->sn_move);
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
@@ -363,8 +357,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                        EV_ABS, ABS_MT_POSITION_Y);
                        set_abs(hi->input, ABS_MT_POSITION_Y, field,
                                cls->sn_move);
-                       /* touchscreen emulation */
-                       set_abs(hi->input, ABS_Y, field, cls->sn_move);
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
@@ -388,9 +380,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_CONTACTID:
-                       if (!td->maxcontacts)
-                               td->maxcontacts = MT_DEFAULT_MAXCONTACT;
-                       input_mt_init_slots(hi->input, td->maxcontacts);
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        td->touches_by_report++;
@@ -398,18 +387,21 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                case HID_DG_WIDTH:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MAJOR);
-                       set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
-                               cls->sn_width);
+                       if (!(cls->quirks & MT_QUIRK_NO_AREA))
+                               set_abs(hi->input, ABS_MT_TOUCH_MAJOR, field,
+                                       cls->sn_width);
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
                case HID_DG_HEIGHT:
                        hid_map_usage(hi, usage, bit, max,
                                        EV_ABS, ABS_MT_TOUCH_MINOR);
-                       set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
-                               cls->sn_height);
-                       input_set_abs_params(hi->input,
+                       if (!(cls->quirks & MT_QUIRK_NO_AREA)) {
+                               set_abs(hi->input, ABS_MT_TOUCH_MINOR, field,
+                                       cls->sn_height);
+                               input_set_abs_params(hi->input,
                                        ABS_MT_ORIENTATION, 0, 1, 0, 0);
+                       }
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
@@ -418,9 +410,6 @@ static int mt_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                                        EV_ABS, ABS_MT_PRESSURE);
                        set_abs(hi->input, ABS_MT_PRESSURE, field,
                                cls->sn_pressure);
-                       /* touchscreen emulation */
-                       set_abs(hi->input, ABS_PRESSURE, field,
-                               cls->sn_pressure);
                        mt_store_field(usage, td, hi);
                        td->last_field_index = field->index;
                        return 1;
@@ -464,7 +453,7 @@ static int mt_input_mapped(struct hid_device *hdev, struct hid_input *hi,
        return -1;
 }
 
-static int mt_compute_slot(struct mt_device *td)
+static int mt_compute_slot(struct mt_device *td, struct input_dev *input)
 {
        __s32 quirks = td->mtclass.quirks;
 
@@ -480,42 +469,23 @@ static int mt_compute_slot(struct mt_device *td)
        if (quirks & MT_QUIRK_SLOT_IS_CONTACTID_MINUS_ONE)
                return td->curdata.contactid - 1;
 
-       return find_slot_from_contactid(td);
+       return input_mt_get_slot_by_key(input, td->curdata.contactid);
 }
 
 /*
  * this function is called when a whole contact has been processed,
  * so that it can assign it to a slot and store the data there
  */
-static void mt_complete_slot(struct mt_device *td)
+static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
 {
-       td->curdata.seen_in_this_frame = true;
        if (td->curvalid) {
-               int slotnum = mt_compute_slot(td);
-
-               if (slotnum >= 0 && slotnum < td->maxcontacts)
-                       td->slots[slotnum] = td->curdata;
-       }
-       td->num_received++;
-}
+               int slotnum = mt_compute_slot(td, input);
+               struct mt_slot *s = &td->curdata;
 
+               if (slotnum < 0 || slotnum >= td->maxcontacts)
+                       return;
 
-/*
- * this function is called when a whole packet has been received and processed,
- * so that it can decide what to send to the input layer.
- */
-static void mt_emit_event(struct mt_device *td, struct input_dev *input)
-{
-       int i;
-
-       for (i = 0; i < td->maxcontacts; ++i) {
-               struct mt_slot *s = &(td->slots[i]);
-               if ((td->mtclass.quirks & MT_QUIRK_NOT_SEEN_MEANS_UP) &&
-                       !s->seen_in_this_frame) {
-                       s->touch_state = false;
-               }
-
-               input_mt_slot(input, i);
+               input_mt_slot(input, slotnum);
                input_mt_report_slot_state(input, MT_TOOL_FINGER,
                        s->touch_state);
                if (s->touch_state) {
@@ -532,24 +502,29 @@ static void mt_emit_event(struct mt_device *td, struct input_dev *input)
                        input_event(input, EV_ABS, ABS_MT_TOUCH_MAJOR, major);
                        input_event(input, EV_ABS, ABS_MT_TOUCH_MINOR, minor);
                }
-               s->seen_in_this_frame = false;
-
        }
 
-       input_mt_report_pointer_emulation(input, true);
+       td->num_received++;
+}
+
+/*
+ * this function is called when a whole packet has been received and processed,
+ * so that it can decide what to send to the input layer.
+ */
+static void mt_sync_frame(struct mt_device *td, struct input_dev *input)
+{
+       input_mt_sync_frame(input);
        input_sync(input);
        td->num_received = 0;
 }
 
-
-
 static int mt_event(struct hid_device *hid, struct hid_field *field,
                                struct hid_usage *usage, __s32 value)
 {
        struct mt_device *td = hid_get_drvdata(hid);
        __s32 quirks = td->mtclass.quirks;
 
-       if (hid->claimed & HID_CLAIMED_INPUT && td->slots) {
+       if (hid->claimed & HID_CLAIMED_INPUT) {
                switch (usage->hid) {
                case HID_DG_INRANGE:
                        if (quirks & MT_QUIRK_ALWAYS_VALID)
@@ -602,11 +577,11 @@ static int mt_event(struct hid_device *hid, struct hid_field *field,
                }
 
                if (usage->hid == td->last_slot_field)
-                       mt_complete_slot(td);
+                       mt_complete_slot(td, field->hidinput->input);
 
                if (field->index == td->last_field_index
                        && td->num_received >= td->num_expected)
-                       mt_emit_event(td, field->hidinput->input);
+                       mt_sync_frame(td, field->hidinput->input);
 
        }
 
@@ -685,18 +660,45 @@ static void mt_post_parse(struct mt_device *td)
        }
 }
 
+static void mt_input_configured(struct hid_device *hdev, struct hid_input *hi)
+
+{
+       struct mt_device *td = hid_get_drvdata(hdev);
+       struct mt_class *cls = &td->mtclass;
+       struct input_dev *input = hi->input;
+
+       /* Only initialize slots for MT input devices */
+       if (!test_bit(ABS_MT_POSITION_X, input->absbit))
+               return;
+
+       if (!td->maxcontacts)
+               td->maxcontacts = MT_DEFAULT_MAXCONTACT;
+
+       mt_post_parse(td);
+       if (td->serial_maybe)
+               mt_post_parse_default_settings(td);
+
+       if (cls->is_indirect)
+               td->mt_flags |= INPUT_MT_POINTER;
+
+       if (cls->quirks & MT_QUIRK_NOT_SEEN_MEANS_UP)
+               td->mt_flags |= INPUT_MT_DROP_UNUSED;
+
+       input_mt_init_slots(input, td->maxcontacts, td->mt_flags);
+
+       td->mt_flags = 0;
+}
+
 static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
        int ret, i;
        struct mt_device *td;
        struct mt_class *mtclass = mt_classes; /* MT_CLS_DEFAULT */
 
-       if (id) {
-               for (i = 0; mt_classes[i].name ; i++) {
-                       if (id->driver_data == mt_classes[i].name) {
-                               mtclass = &(mt_classes[i]);
-                               break;
-                       }
+       for (i = 0; mt_classes[i].name ; i++) {
+               if (id->driver_data == mt_classes[i].name) {
+                       mtclass = &(mt_classes[i]);
+                       break;
                }
        }
 
@@ -722,6 +724,9 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
                goto fail;
        }
 
+       if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
+               td->serial_maybe = true;
+
        ret = hid_parse(hdev);
        if (ret != 0)
                goto fail;
@@ -730,20 +735,6 @@ static int mt_probe(struct hid_device *hdev, const struct hid_device_id *id)
        if (ret)
                goto fail;
 
-       mt_post_parse(td);
-
-       if (id->vendor == HID_ANY_ID && id->product == HID_ANY_ID)
-               mt_post_parse_default_settings(td);
-
-       td->slots = kzalloc(td->maxcontacts * sizeof(struct mt_slot),
-                               GFP_KERNEL);
-       if (!td->slots) {
-               dev_err(&hdev->dev, "cannot allocate multitouch slots\n");
-               hid_hw_stop(hdev);
-               ret = -ENOMEM;
-               goto fail;
-       }
-
        ret = sysfs_create_group(&hdev->dev.kobj, &mt_attribute_group);
 
        mt_set_maxcontacts(hdev);
@@ -767,6 +758,32 @@ static int mt_reset_resume(struct hid_device *hdev)
        mt_set_input_mode(hdev);
        return 0;
 }
+
+static int mt_resume(struct hid_device *hdev)
+{
+       struct usb_interface *intf;
+       struct usb_host_interface *interface;
+       struct usb_device *dev;
+
+       if (hdev->bus != BUS_USB)
+               return 0;
+
+       intf = to_usb_interface(hdev->dev.parent);
+       interface = intf->cur_altsetting;
+       dev = hid_to_usb_dev(hdev);
+
+       /* Some Elan legacy devices require SET_IDLE to be set on resume.
+        * It should be safe to send it to other devices too.
+        * Tested on 3M, Stantum, Cypress, Zytronic, eGalax, and Elan panels. */
+
+       usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                       HID_REQ_SET_IDLE,
+                       USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+                       0, interface->desc.bInterfaceNumber,
+                       NULL, 0, USB_CTRL_SET_TIMEOUT);
+
+       return 0;
+}
 #endif
 
 static void mt_remove(struct hid_device *hdev)
@@ -774,7 +791,6 @@ static void mt_remove(struct hid_device *hdev)
        struct mt_device *td = hid_get_drvdata(hdev);
        sysfs_remove_group(&hdev->dev.kobj, &mt_attribute_group);
        hid_hw_stop(hdev);
-       kfree(td->slots);
        kfree(td);
        hid_set_drvdata(hdev, NULL);
 }
@@ -885,17 +901,37 @@ static const struct hid_device_id mt_devices[] = {
                        USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7349) },
        { .driver_data = MT_CLS_EGALAX_SERIAL,
                MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_73F7) },
+       { .driver_data = MT_CLS_EGALAX_SERIAL,
+               MT_USB_DEVICE(USB_VENDOR_ID_DWAV,
                        USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_A001) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_7224) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72D0) },
+       { .driver_data = MT_CLS_EGALAX,
+               HID_USB_DEVICE(USB_VENDOR_ID_DWAV,
+                       USB_DEVICE_ID_DWAV_EGALAX_MULTITOUCH_72C4) },
 
        /* Elo TouchSystems IntelliTouch Plus panel */
        { .driver_data = MT_CLS_DUAL_NSMU_CONTACTID,
                MT_USB_DEVICE(USB_VENDOR_ID_ELO,
                        USB_DEVICE_ID_ELO_TS2515) },
 
+       /* Flatfrog Panels */
+       { .driver_data = MT_CLS_FLATFROG,
+               MT_USB_DEVICE(USB_VENDOR_ID_FLATFROG,
+                       USB_DEVICE_ID_MULTITOUCH_3200) },
+
        /* GeneralTouch panel */
-       { .driver_data = MT_CLS_DUAL_INRANGE_CONTACTNUMBER,
+       { .driver_data = MT_CLS_GENERALTOUCH_TWOFINGERS,
                MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
                        USB_DEVICE_ID_GENERAL_TOUCH_WIN7_TWOFINGERS) },
+       { .driver_data = MT_CLS_GENERALTOUCH_PWT_TENFINGERS,
+               MT_USB_DEVICE(USB_VENDOR_ID_GENERAL_TOUCH,
+                       USB_DEVICE_ID_GENERAL_TOUCH_WIN8_PWT_TENFINGERS) },
 
        /* Gametel game controller */
        { .driver_data = MT_CLS_DEFAULT,
@@ -1087,11 +1123,13 @@ static struct hid_driver mt_driver = {
        .remove = mt_remove,
        .input_mapping = mt_input_mapping,
        .input_mapped = mt_input_mapped,
+       .input_configured = mt_input_configured,
        .feature_mapping = mt_feature_mapping,
        .usage_table = mt_grabbed_usages,
        .event = mt_event,
 #ifdef CONFIG_PM
        .reset_resume = mt_reset_resume,
+       .resume = mt_resume,
 #endif
 };
 
index 9fae2eb..86a969f 100644 (file)
@@ -882,10 +882,10 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
        nd->activate_slack = activate_slack;
        nd->act_state = activate_slack;
        nd->deactivate_slack = -deactivate_slack;
-       nd->sensor_logical_width = 0;
-       nd->sensor_logical_height = 0;
-       nd->sensor_physical_width = 0;
-       nd->sensor_physical_height = 0;
+       nd->sensor_logical_width = 1;
+       nd->sensor_logical_height = 1;
+       nd->sensor_physical_width = 1;
+       nd->sensor_physical_height = 1;
 
        hid_set_drvdata(hdev, nd);
 
index f1ea3ff..4c521de 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
diff --git a/drivers/hid/hid-picolcd.c b/drivers/hid/hid-picolcd.c
deleted file mode 100644 (file)
index 27c8ebd..0000000
+++ /dev/null
@@ -1,2748 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2010 by Bruno Prémont <bonbons@linux-vserver.org>       *
- *                                                                         *
- *   Based on Logitech G13 driver (v0.4)                                   *
- *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
- *                                                                         *
- *   This program is free software: you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation, version 2 of the License.               *
- *                                                                         *
- *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
- ***************************************************************************/
-
-#include <linux/hid.h>
-#include <linux/hid-debug.h>
-#include <linux/input.h>
-#include "hid-ids.h"
-#include "usbhid/usbhid.h"
-#include <linux/usb.h>
-
-#include <linux/fb.h>
-#include <linux/vmalloc.h>
-#include <linux/backlight.h>
-#include <linux/lcd.h>
-
-#include <linux/leds.h>
-
-#include <linux/seq_file.h>
-#include <linux/debugfs.h>
-
-#include <linux/completion.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-
-#define PICOLCD_NAME "PicoLCD (graphic)"
-
-/* Report numbers */
-#define REPORT_ERROR_CODE      0x10 /* LCD: IN[16]  */
-#define   ERR_SUCCESS            0x00
-#define   ERR_PARAMETER_MISSING  0x01
-#define   ERR_DATA_MISSING       0x02
-#define   ERR_BLOCK_READ_ONLY    0x03
-#define   ERR_BLOCK_NOT_ERASABLE 0x04
-#define   ERR_BLOCK_TOO_BIG      0x05
-#define   ERR_SECTION_OVERFLOW   0x06
-#define   ERR_INVALID_CMD_LEN    0x07
-#define   ERR_INVALID_DATA_LEN   0x08
-#define REPORT_KEY_STATE       0x11 /* LCD: IN[2]   */
-#define REPORT_IR_DATA         0x21 /* LCD: IN[63]  */
-#define REPORT_EE_DATA         0x32 /* LCD: IN[63]  */
-#define REPORT_MEMORY          0x41 /* LCD: IN[63]  */
-#define REPORT_LED_STATE       0x81 /* LCD: OUT[1]  */
-#define REPORT_BRIGHTNESS      0x91 /* LCD: OUT[1]  */
-#define REPORT_CONTRAST        0x92 /* LCD: OUT[1]  */
-#define REPORT_RESET           0x93 /* LCD: OUT[2]  */
-#define REPORT_LCD_CMD         0x94 /* LCD: OUT[63] */
-#define REPORT_LCD_DATA        0x95 /* LCD: OUT[63] */
-#define REPORT_LCD_CMD_DATA    0x96 /* LCD: OUT[63] */
-#define        REPORT_EE_READ         0xa3 /* LCD: OUT[63] */
-#define REPORT_EE_WRITE        0xa4 /* LCD: OUT[63] */
-#define REPORT_ERASE_MEMORY    0xb2 /* LCD: OUT[2]  */
-#define REPORT_READ_MEMORY     0xb3 /* LCD: OUT[3]  */
-#define REPORT_WRITE_MEMORY    0xb4 /* LCD: OUT[63] */
-#define REPORT_SPLASH_RESTART  0xc1 /* LCD: OUT[1]  */
-#define REPORT_EXIT_KEYBOARD   0xef /* LCD: OUT[2]  */
-#define REPORT_VERSION         0xf1 /* LCD: IN[2],OUT[1]    Bootloader: IN[2],OUT[1]   */
-#define REPORT_BL_ERASE_MEMORY 0xf2 /*                      Bootloader: IN[36],OUT[4]  */
-#define REPORT_BL_READ_MEMORY  0xf3 /*                      Bootloader: IN[36],OUT[4]  */
-#define REPORT_BL_WRITE_MEMORY 0xf4 /*                      Bootloader: IN[36],OUT[36] */
-#define REPORT_DEVID           0xf5 /* LCD: IN[5], OUT[1]   Bootloader: IN[5],OUT[1]   */
-#define REPORT_SPLASH_SIZE     0xf6 /* LCD: IN[4], OUT[1]   */
-#define REPORT_HOOK_VERSION    0xf7 /* LCD: IN[2], OUT[1]   */
-#define REPORT_EXIT_FLASHER    0xff /*                      Bootloader: OUT[2]         */
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Framebuffer
- *
- * The PicoLCD use a Topway LCD module of 256x64 pixel
- * This display area is tiled over 4 controllers with 8 tiles
- * each. Each tile has 8x64 pixel, each data byte representing
- * a 1-bit wide vertical line of the tile.
- *
- * The display can be updated at a tile granularity.
- *
- *       Chip 1           Chip 2           Chip 3           Chip 4
- * +----------------+----------------+----------------+----------------+
- * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
- * +----------------+----------------+----------------+----------------+
- * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
- * +----------------+----------------+----------------+----------------+
- *                                  ...
- * +----------------+----------------+----------------+----------------+
- * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
- * +----------------+----------------+----------------+----------------+
- */
-#define PICOLCDFB_NAME "picolcdfb"
-#define PICOLCDFB_WIDTH (256)
-#define PICOLCDFB_HEIGHT (64)
-#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
-
-#define PICOLCDFB_UPDATE_RATE_LIMIT   10
-#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
-
-/* Framebuffer visual structures */
-static const struct fb_fix_screeninfo picolcdfb_fix = {
-       .id          = PICOLCDFB_NAME,
-       .type        = FB_TYPE_PACKED_PIXELS,
-       .visual      = FB_VISUAL_MONO01,
-       .xpanstep    = 0,
-       .ypanstep    = 0,
-       .ywrapstep   = 0,
-       .line_length = PICOLCDFB_WIDTH / 8,
-       .accel       = FB_ACCEL_NONE,
-};
-
-static const struct fb_var_screeninfo picolcdfb_var = {
-       .xres           = PICOLCDFB_WIDTH,
-       .yres           = PICOLCDFB_HEIGHT,
-       .xres_virtual   = PICOLCDFB_WIDTH,
-       .yres_virtual   = PICOLCDFB_HEIGHT,
-       .width          = 103,
-       .height         = 26,
-       .bits_per_pixel = 1,
-       .grayscale      = 1,
-       .red            = {
-               .offset = 0,
-               .length = 1,
-               .msb_right = 0,
-       },
-       .green          = {
-               .offset = 0,
-               .length = 1,
-               .msb_right = 0,
-       },
-       .blue           = {
-               .offset = 0,
-               .length = 1,
-               .msb_right = 0,
-       },
-       .transp         = {
-               .offset = 0,
-               .length = 0,
-               .msb_right = 0,
-       },
-};
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-/* Input device
- *
- * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
- * and header for 4x4 key matrix. The built-in keys are part of the matrix.
- */
-static const unsigned short def_keymap[] = {
-       KEY_RESERVED,   /* none */
-       KEY_BACK,       /* col 4 + row 1 */
-       KEY_HOMEPAGE,   /* col 3 + row 1 */
-       KEY_RESERVED,   /* col 2 + row 1 */
-       KEY_RESERVED,   /* col 1 + row 1 */
-       KEY_SCROLLUP,   /* col 4 + row 2 */
-       KEY_OK,         /* col 3 + row 2 */
-       KEY_SCROLLDOWN, /* col 2 + row 2 */
-       KEY_RESERVED,   /* col 1 + row 2 */
-       KEY_RESERVED,   /* col 4 + row 3 */
-       KEY_RESERVED,   /* col 3 + row 3 */
-       KEY_RESERVED,   /* col 2 + row 3 */
-       KEY_RESERVED,   /* col 1 + row 3 */
-       KEY_RESERVED,   /* col 4 + row 4 */
-       KEY_RESERVED,   /* col 3 + row 4 */
-       KEY_RESERVED,   /* col 2 + row 4 */
-       KEY_RESERVED,   /* col 1 + row 4 */
-};
-#define PICOLCD_KEYS ARRAY_SIZE(def_keymap)
-
-/* Description of in-progress IO operation, used for operations
- * that trigger response from device */
-struct picolcd_pending {
-       struct hid_report *out_report;
-       struct hid_report *in_report;
-       struct completion ready;
-       int raw_size;
-       u8 raw_data[64];
-};
-
-/* Per device data structure */
-struct picolcd_data {
-       struct hid_device *hdev;
-#ifdef CONFIG_DEBUG_FS
-       struct dentry *debug_reset;
-       struct dentry *debug_eeprom;
-       struct dentry *debug_flash;
-       struct mutex mutex_flash;
-       int addr_sz;
-#endif
-       u8 version[2];
-       unsigned short opmode_delay;
-       /* input stuff */
-       u8 pressed_keys[2];
-       struct input_dev *input_keys;
-       struct input_dev *input_cir;
-       unsigned short keycode[PICOLCD_KEYS];
-
-#ifdef CONFIG_HID_PICOLCD_FB
-       /* Framebuffer stuff */
-       u8 fb_update_rate;
-       u8 fb_bpp;
-       u8 fb_force;
-       u8 *fb_vbitmap;         /* local copy of what was sent to PicoLCD */
-       u8 *fb_bitmap;          /* framebuffer */
-       struct fb_info *fb_info;
-       struct fb_deferred_io fb_defio;
-#endif /* CONFIG_HID_PICOLCD_FB */
-#ifdef CONFIG_HID_PICOLCD_LCD
-       struct lcd_device *lcd;
-       u8 lcd_contrast;
-#endif /* CONFIG_HID_PICOLCD_LCD */
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
-       struct backlight_device *backlight;
-       u8 lcd_brightness;
-       u8 lcd_power;
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-#ifdef CONFIG_HID_PICOLCD_LEDS
-       /* LED stuff */
-       u8 led_state;
-       struct led_classdev *led[8];
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
-       /* Housekeeping stuff */
-       spinlock_t lock;
-       struct mutex mutex;
-       struct picolcd_pending *pending;
-       int status;
-#define PICOLCD_BOOTLOADER 1
-#define PICOLCD_FAILED 2
-#define PICOLCD_READY_FB 4
-};
-
-
-/* Find a given report */
-#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
-#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
-
-static struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
-{
-       struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
-       struct hid_report *report = NULL;
-
-       list_for_each_entry(report, feature_report_list, list) {
-               if (report->id == id)
-                       return report;
-       }
-       hid_warn(hdev, "No report with id 0x%x found\n", id);
-       return NULL;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void picolcd_debug_out_report(struct picolcd_data *data,
-               struct hid_device *hdev, struct hid_report *report);
-#define usbhid_submit_report(a, b, c) \
-       do { \
-               picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
-               usbhid_submit_report(a, b, c); \
-       } while (0)
-#endif
-
-/* Submit a report and wait for a reply from device - if device fades away
- * or does not respond in time, return NULL */
-static struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
-               int report_id, const u8 *raw_data, int size)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       struct picolcd_pending *work;
-       struct hid_report *report = picolcd_out_report(report_id, hdev);
-       unsigned long flags;
-       int i, j, k;
-
-       if (!report || !data)
-               return NULL;
-       if (data->status & PICOLCD_FAILED)
-               return NULL;
-       work = kzalloc(sizeof(*work), GFP_KERNEL);
-       if (!work)
-               return NULL;
-
-       init_completion(&work->ready);
-       work->out_report = report;
-       work->in_report  = NULL;
-       work->raw_size   = 0;
-
-       mutex_lock(&data->mutex);
-       spin_lock_irqsave(&data->lock, flags);
-       for (i = k = 0; i < report->maxfield; i++)
-               for (j = 0; j < report->field[i]->report_count; j++) {
-                       hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
-                       k++;
-               }
-       data->pending = work;
-       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-       wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
-       spin_lock_irqsave(&data->lock, flags);
-       data->pending = NULL;
-       spin_unlock_irqrestore(&data->lock, flags);
-       mutex_unlock(&data->mutex);
-       return work;
-}
-
-#ifdef CONFIG_HID_PICOLCD_FB
-/* Send a given tile to PicoLCD */
-static int picolcd_fb_send_tile(struct hid_device *hdev, int chip, int tile)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       struct hid_report *report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, hdev);
-       struct hid_report *report2 = picolcd_out_report(REPORT_LCD_DATA, hdev);
-       unsigned long flags;
-       u8 *tdata;
-       int i;
-
-       if (!report1 || report1->maxfield != 1 || !report2 || report2->maxfield != 1)
-               return -ENODEV;
-
-       spin_lock_irqsave(&data->lock, flags);
-       hid_set_field(report1->field[0],  0, chip << 2);
-       hid_set_field(report1->field[0],  1, 0x02);
-       hid_set_field(report1->field[0],  2, 0x00);
-       hid_set_field(report1->field[0],  3, 0x00);
-       hid_set_field(report1->field[0],  4, 0xb8 | tile);
-       hid_set_field(report1->field[0],  5, 0x00);
-       hid_set_field(report1->field[0],  6, 0x00);
-       hid_set_field(report1->field[0],  7, 0x40);
-       hid_set_field(report1->field[0],  8, 0x00);
-       hid_set_field(report1->field[0],  9, 0x00);
-       hid_set_field(report1->field[0], 10,   32);
-
-       hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
-       hid_set_field(report2->field[0],  1, 0x00);
-       hid_set_field(report2->field[0],  2, 0x00);
-       hid_set_field(report2->field[0],  3,   32);
-
-       tdata = data->fb_vbitmap + (tile * 4 + chip) * 64;
-       for (i = 0; i < 64; i++)
-               if (i < 32)
-                       hid_set_field(report1->field[0], 11 + i, tdata[i]);
-               else
-                       hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
-
-       usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
-       usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-       return 0;
-}
-
-/* Translate a single tile*/
-static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
-               int chip, int tile)
-{
-       int i, b, changed = 0;
-       u8 tdata[64];
-       u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
-
-       if (bpp == 1) {
-               for (b = 7; b >= 0; b--) {
-                       const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
-                       for (i = 0; i < 64; i++) {
-                               tdata[i] <<= 1;
-                               tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
-                       }
-               }
-       } else if (bpp == 8) {
-               for (b = 7; b >= 0; b--) {
-                       const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
-                       for (i = 0; i < 64; i++) {
-                               tdata[i] <<= 1;
-                               tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
-                       }
-               }
-       } else {
-               /* Oops, we should never get here! */
-               WARN_ON(1);
-               return 0;
-       }
-
-       for (i = 0; i < 64; i++)
-               if (tdata[i] != vdata[i]) {
-                       changed = 1;
-                       vdata[i] = tdata[i];
-               }
-       return changed;
-}
-
-/* Reconfigure LCD display */
-static int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
-       struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
-       int i, j;
-       unsigned long flags;
-       static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
-
-       if (!report || report->maxfield != 1)
-               return -ENODEV;
-
-       spin_lock_irqsave(&data->lock, flags);
-       for (i = 0; i < 4; i++) {
-               for (j = 0; j < report->field[0]->maxusage; j++)
-                       if (j == 0)
-                               hid_set_field(report->field[0], j, i << 2);
-                       else if (j < sizeof(mapcmd))
-                               hid_set_field(report->field[0], j, mapcmd[j]);
-                       else
-                               hid_set_field(report->field[0], j, 0);
-               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       }
-
-       data->status |= PICOLCD_READY_FB;
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       if (data->fb_bitmap) {
-               if (clear) {
-                       memset(data->fb_vbitmap, 0, PICOLCDFB_SIZE);
-                       memset(data->fb_bitmap, 0, PICOLCDFB_SIZE*data->fb_bpp);
-               }
-               data->fb_force = 1;
-       }
-
-       /* schedule first output of framebuffer */
-       if (data->fb_info)
-               schedule_delayed_work(&data->fb_info->deferred_work, 0);
-
-       return 0;
-}
-
-/* Update fb_vbitmap from the screen_base and send changed tiles to device */
-static void picolcd_fb_update(struct picolcd_data *data)
-{
-       int chip, tile, n;
-       unsigned long flags;
-
-       if (!data)
-               return;
-
-       spin_lock_irqsave(&data->lock, flags);
-       if (!(data->status & PICOLCD_READY_FB)) {
-               spin_unlock_irqrestore(&data->lock, flags);
-               picolcd_fb_reset(data, 0);
-       } else {
-               spin_unlock_irqrestore(&data->lock, flags);
-       }
-
-       /*
-        * Translate the framebuffer into the format needed by the PicoLCD.
-        * See display layout above.
-        * Do this one tile after the other and push those tiles that changed.
-        *
-        * Wait for our IO to complete as otherwise we might flood the queue!
-        */
-       n = 0;
-       for (chip = 0; chip < 4; chip++)
-               for (tile = 0; tile < 8; tile++)
-                       if (picolcd_fb_update_tile(data->fb_vbitmap,
-                                       data->fb_bitmap, data->fb_bpp, chip, tile) ||
-                               data->fb_force) {
-                               n += 2;
-                               if (!data->fb_info->par)
-                                       return; /* device lost! */
-                               if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
-                                       usbhid_wait_io(data->hdev);
-                                       n = 0;
-                               }
-                               picolcd_fb_send_tile(data->hdev, chip, tile);
-                       }
-       data->fb_force = false;
-       if (n)
-               usbhid_wait_io(data->hdev);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_fillrect(struct fb_info *info,
-               const struct fb_fillrect *rect)
-{
-       if (!info->par)
-               return;
-       sys_fillrect(info, rect);
-
-       schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_copyarea(struct fb_info *info,
-               const struct fb_copyarea *area)
-{
-       if (!info->par)
-               return;
-       sys_copyarea(info, area);
-
-       schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/* Stub to call the system default and update the image on the picoLCD */
-static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
-{
-       if (!info->par)
-               return;
-       sys_imageblit(info, image);
-
-       schedule_delayed_work(&info->deferred_work, 0);
-}
-
-/*
- * this is the slow path from userspace. they can seek and write to
- * the fb. it's inefficient to do anything less than a full screen draw
- */
-static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
-               size_t count, loff_t *ppos)
-{
-       ssize_t ret;
-       if (!info->par)
-               return -ENODEV;
-       ret = fb_sys_write(info, buf, count, ppos);
-       if (ret >= 0)
-               schedule_delayed_work(&info->deferred_work, 0);
-       return ret;
-}
-
-static int picolcd_fb_blank(int blank, struct fb_info *info)
-{
-       if (!info->par)
-               return -ENODEV;
-       /* We let fb notification do this for us via lcd/backlight device */
-       return 0;
-}
-
-static void picolcd_fb_destroy(struct fb_info *info)
-{
-       struct picolcd_data *data = info->par;
-       u32 *ref_cnt = info->pseudo_palette;
-       int may_release;
-
-       info->par = NULL;
-       if (data)
-               data->fb_info = NULL;
-       fb_deferred_io_cleanup(info);
-
-       ref_cnt--;
-       mutex_lock(&info->lock);
-       (*ref_cnt)--;
-       may_release = !*ref_cnt;
-       mutex_unlock(&info->lock);
-       if (may_release) {
-               vfree((u8 *)info->fix.smem_start);
-               framebuffer_release(info);
-       }
-}
-
-static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
-{
-       __u32 bpp      = var->bits_per_pixel;
-       __u32 activate = var->activate;
-
-       /* only allow 1/8 bit depth (8-bit is grayscale) */
-       *var = picolcdfb_var;
-       var->activate = activate;
-       if (bpp >= 8) {
-               var->bits_per_pixel = 8;
-               var->red.length     = 8;
-               var->green.length   = 8;
-               var->blue.length    = 8;
-       } else {
-               var->bits_per_pixel = 1;
-               var->red.length     = 1;
-               var->green.length   = 1;
-               var->blue.length    = 1;
-       }
-       return 0;
-}
-
-static int picolcd_set_par(struct fb_info *info)
-{
-       struct picolcd_data *data = info->par;
-       u8 *tmp_fb, *o_fb;
-       if (!data)
-               return -ENODEV;
-       if (info->var.bits_per_pixel == data->fb_bpp)
-               return 0;
-       /* switch between 1/8 bit depths */
-       if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
-               return -EINVAL;
-
-       o_fb   = data->fb_bitmap;
-       tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
-       if (!tmp_fb)
-               return -ENOMEM;
-
-       /* translate FB content to new bits-per-pixel */
-       if (info->var.bits_per_pixel == 1) {
-               int i, b;
-               for (i = 0; i < PICOLCDFB_SIZE; i++) {
-                       u8 p = 0;
-                       for (b = 0; b < 8; b++) {
-                               p <<= 1;
-                               p |= o_fb[i*8+b] ? 0x01 : 0x00;
-                       }
-                       tmp_fb[i] = p;
-               }
-               memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
-               info->fix.visual = FB_VISUAL_MONO01;
-               info->fix.line_length = PICOLCDFB_WIDTH / 8;
-       } else {
-               int i;
-               memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
-               for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
-                       o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
-               info->fix.visual = FB_VISUAL_DIRECTCOLOR;
-               info->fix.line_length = PICOLCDFB_WIDTH;
-       }
-
-       kfree(tmp_fb);
-       data->fb_bpp      = info->var.bits_per_pixel;
-       return 0;
-}
-
-/* Do refcounting on our FB and cleanup per worker if FB is
- * closed after unplug of our device
- * (fb_release holds info->lock and still touches info after
- *  we return so we can't release it immediately.
- */
-struct picolcd_fb_cleanup_item {
-       struct fb_info *info;
-       struct picolcd_fb_cleanup_item *next;
-};
-static struct picolcd_fb_cleanup_item *fb_pending;
-static DEFINE_SPINLOCK(fb_pending_lock);
-
-static void picolcd_fb_do_cleanup(struct work_struct *data)
-{
-       struct picolcd_fb_cleanup_item *item;
-       unsigned long flags;
-
-       do {
-               spin_lock_irqsave(&fb_pending_lock, flags);
-               item = fb_pending;
-               fb_pending = item ? item->next : NULL;
-               spin_unlock_irqrestore(&fb_pending_lock, flags);
-
-               if (item) {
-                       u8 *fb = (u8 *)item->info->fix.smem_start;
-                       /* make sure we do not race against fb core when
-                        * releasing */
-                       mutex_lock(&item->info->lock);
-                       mutex_unlock(&item->info->lock);
-                       framebuffer_release(item->info);
-                       vfree(fb);
-               }
-       } while (item);
-}
-
-static DECLARE_WORK(picolcd_fb_cleanup, picolcd_fb_do_cleanup);
-
-static int picolcd_fb_open(struct fb_info *info, int u)
-{
-       u32 *ref_cnt = info->pseudo_palette;
-       ref_cnt--;
-
-       (*ref_cnt)++;
-       return 0;
-}
-
-static int picolcd_fb_release(struct fb_info *info, int u)
-{
-       u32 *ref_cnt = info->pseudo_palette;
-       ref_cnt--;
-
-       (*ref_cnt)++;
-       if (!*ref_cnt) {
-               unsigned long flags;
-               struct picolcd_fb_cleanup_item *item = (struct picolcd_fb_cleanup_item *)ref_cnt;
-               item--;
-               spin_lock_irqsave(&fb_pending_lock, flags);
-               item->next = fb_pending;
-               fb_pending = item;
-               spin_unlock_irqrestore(&fb_pending_lock, flags);
-               schedule_work(&picolcd_fb_cleanup);
-       }
-       return 0;
-}
-
-/* Note this can't be const because of struct fb_info definition */
-static struct fb_ops picolcdfb_ops = {
-       .owner        = THIS_MODULE,
-       .fb_destroy   = picolcd_fb_destroy,
-       .fb_open      = picolcd_fb_open,
-       .fb_release   = picolcd_fb_release,
-       .fb_read      = fb_sys_read,
-       .fb_write     = picolcd_fb_write,
-       .fb_blank     = picolcd_fb_blank,
-       .fb_fillrect  = picolcd_fb_fillrect,
-       .fb_copyarea  = picolcd_fb_copyarea,
-       .fb_imageblit = picolcd_fb_imageblit,
-       .fb_check_var = picolcd_fb_check_var,
-       .fb_set_par   = picolcd_set_par,
-};
-
-
-/* Callback from deferred IO workqueue */
-static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
-{
-       picolcd_fb_update(info->par);
-}
-
-static const struct fb_deferred_io picolcd_fb_defio = {
-       .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
-       .deferred_io = picolcd_fb_deferred_io,
-};
-
-
-/*
- * The "fb_update_rate" sysfs attribute
- */
-static ssize_t picolcd_fb_update_rate_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-       unsigned i, fb_update_rate = data->fb_update_rate;
-       size_t ret = 0;
-
-       for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
-               if (ret >= PAGE_SIZE)
-                       break;
-               else if (i == fb_update_rate)
-                       ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
-               else
-                       ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
-       if (ret > 0)
-               buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
-       return ret;
-}
-
-static ssize_t picolcd_fb_update_rate_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-       int i;
-       unsigned u;
-
-       if (count < 1 || count > 10)
-               return -EINVAL;
-
-       i = sscanf(buf, "%u", &u);
-       if (i != 1)
-               return -EINVAL;
-
-       if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
-               return -ERANGE;
-       else if (u == 0)
-               u = PICOLCDFB_UPDATE_RATE_DEFAULT;
-
-       data->fb_update_rate = u;
-       data->fb_defio.delay = HZ / data->fb_update_rate;
-       return count;
-}
-
-static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
-               picolcd_fb_update_rate_store);
-
-/* initialize Framebuffer device */
-static int picolcd_init_framebuffer(struct picolcd_data *data)
-{
-       struct device *dev = &data->hdev->dev;
-       struct fb_info *info = NULL;
-       int i, error = -ENOMEM;
-       u8 *fb_vbitmap = NULL;
-       u8 *fb_bitmap  = NULL;
-       u32 *palette;
-
-       fb_bitmap = vmalloc(PICOLCDFB_SIZE*8);
-       if (fb_bitmap == NULL) {
-               dev_err(dev, "can't get a free page for framebuffer\n");
-               goto err_nomem;
-       }
-
-       fb_vbitmap = kmalloc(PICOLCDFB_SIZE, GFP_KERNEL);
-       if (fb_vbitmap == NULL) {
-               dev_err(dev, "can't alloc vbitmap image buffer\n");
-               goto err_nomem;
-       }
-
-       data->fb_update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
-       data->fb_defio = picolcd_fb_defio;
-       /* The extra memory is:
-        * - struct picolcd_fb_cleanup_item
-        * - u32 for ref_count
-        * - 256*u32 for pseudo_palette
-        */
-       info = framebuffer_alloc(257 * sizeof(u32) + sizeof(struct picolcd_fb_cleanup_item), dev);
-       if (info == NULL) {
-               dev_err(dev, "failed to allocate a framebuffer\n");
-               goto err_nomem;
-       }
-
-       palette  = info->par + sizeof(struct picolcd_fb_cleanup_item);
-       *palette = 1;
-       palette++;
-       for (i = 0; i < 256; i++)
-               palette[i] = i > 0 && i < 16 ? 0xff : 0;
-       info->pseudo_palette = palette;
-       info->fbdefio = &data->fb_defio;
-       info->screen_base = (char __force __iomem *)fb_bitmap;
-       info->fbops = &picolcdfb_ops;
-       info->var = picolcdfb_var;
-       info->fix = picolcdfb_fix;
-       info->fix.smem_len   = PICOLCDFB_SIZE*8;
-       info->fix.smem_start = (unsigned long)fb_bitmap;
-       info->par = data;
-       info->flags = FBINFO_FLAG_DEFAULT;
-
-       data->fb_vbitmap = fb_vbitmap;
-       data->fb_bitmap  = fb_bitmap;
-       data->fb_bpp     = picolcdfb_var.bits_per_pixel;
-       error = picolcd_fb_reset(data, 1);
-       if (error) {
-               dev_err(dev, "failed to configure display\n");
-               goto err_cleanup;
-       }
-       error = device_create_file(dev, &dev_attr_fb_update_rate);
-       if (error) {
-               dev_err(dev, "failed to create sysfs attributes\n");
-               goto err_cleanup;
-       }
-       fb_deferred_io_init(info);
-       data->fb_info    = info;
-       error = register_framebuffer(info);
-       if (error) {
-               dev_err(dev, "failed to register framebuffer\n");
-               goto err_sysfs;
-       }
-       /* schedule first output of framebuffer */
-       data->fb_force = 1;
-       schedule_delayed_work(&info->deferred_work, 0);
-       return 0;
-
-err_sysfs:
-       fb_deferred_io_cleanup(info);
-       device_remove_file(dev, &dev_attr_fb_update_rate);
-err_cleanup:
-       data->fb_vbitmap = NULL;
-       data->fb_bitmap  = NULL;
-       data->fb_bpp     = 0;
-       data->fb_info    = NULL;
-
-err_nomem:
-       framebuffer_release(info);
-       vfree(fb_bitmap);
-       kfree(fb_vbitmap);
-       return error;
-}
-
-static void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
-       struct fb_info *info = data->fb_info;
-       u8 *fb_vbitmap = data->fb_vbitmap;
-
-       if (!info)
-               return;
-
-       info->par = NULL;
-       device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
-       unregister_framebuffer(info);
-       data->fb_vbitmap = NULL;
-       data->fb_bitmap  = NULL;
-       data->fb_bpp     = 0;
-       data->fb_info    = NULL;
-       kfree(fb_vbitmap);
-}
-
-#define picolcd_fbinfo(d) ((d)->fb_info)
-#else
-static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
-{
-       return 0;
-}
-static inline int picolcd_init_framebuffer(struct picolcd_data *data)
-{
-       return 0;
-}
-static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
-{
-}
-#define picolcd_fbinfo(d) NULL
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
-/*
- * backlight class device
- */
-static int picolcd_get_brightness(struct backlight_device *bdev)
-{
-       struct picolcd_data *data = bl_get_data(bdev);
-       return data->lcd_brightness;
-}
-
-static int picolcd_set_brightness(struct backlight_device *bdev)
-{
-       struct picolcd_data *data = bl_get_data(bdev);
-       struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
-       unsigned long flags;
-
-       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
-               return -ENODEV;
-
-       data->lcd_brightness = bdev->props.brightness & 0x0ff;
-       data->lcd_power      = bdev->props.power;
-       spin_lock_irqsave(&data->lock, flags);
-       hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
-       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-       return 0;
-}
-
-static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
-{
-       return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
-}
-
-static const struct backlight_ops picolcd_blops = {
-       .update_status  = picolcd_set_brightness,
-       .get_brightness = picolcd_get_brightness,
-       .check_fb       = picolcd_check_bl_fb,
-};
-
-static int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
-{
-       struct device *dev = &data->hdev->dev;
-       struct backlight_device *bdev;
-       struct backlight_properties props;
-       if (!report)
-               return -ENODEV;
-       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
-                       report->field[0]->report_size != 8) {
-               dev_err(dev, "unsupported BRIGHTNESS report");
-               return -EINVAL;
-       }
-
-       memset(&props, 0, sizeof(props));
-       props.type = BACKLIGHT_RAW;
-       props.max_brightness = 0xff;
-       bdev = backlight_device_register(dev_name(dev), dev, data,
-                       &picolcd_blops, &props);
-       if (IS_ERR(bdev)) {
-               dev_err(dev, "failed to register backlight\n");
-               return PTR_ERR(bdev);
-       }
-       bdev->props.brightness     = 0xff;
-       data->lcd_brightness       = 0xff;
-       data->backlight            = bdev;
-       picolcd_set_brightness(bdev);
-       return 0;
-}
-
-static void picolcd_exit_backlight(struct picolcd_data *data)
-{
-       struct backlight_device *bdev = data->backlight;
-
-       data->backlight = NULL;
-       if (bdev)
-               backlight_device_unregister(bdev);
-}
-
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
-       if (!data->backlight)
-               return 0;
-       return picolcd_set_brightness(data->backlight);
-}
-
-#ifdef CONFIG_PM
-static void picolcd_suspend_backlight(struct picolcd_data *data)
-{
-       int bl_power = data->lcd_power;
-       if (!data->backlight)
-               return;
-
-       data->backlight->props.power = FB_BLANK_POWERDOWN;
-       picolcd_set_brightness(data->backlight);
-       data->lcd_power = data->backlight->props.power = bl_power;
-}
-#endif /* CONFIG_PM */
-#else
-static inline int picolcd_init_backlight(struct picolcd_data *data,
-               struct hid_report *report)
-{
-       return 0;
-}
-static inline void picolcd_exit_backlight(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_backlight(struct picolcd_data *data)
-{
-       return 0;
-}
-static inline void picolcd_suspend_backlight(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
-
-#ifdef CONFIG_HID_PICOLCD_LCD
-/*
- * lcd class device
- */
-static int picolcd_get_contrast(struct lcd_device *ldev)
-{
-       struct picolcd_data *data = lcd_get_data(ldev);
-       return data->lcd_contrast;
-}
-
-static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
-{
-       struct picolcd_data *data = lcd_get_data(ldev);
-       struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
-       unsigned long flags;
-
-       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
-               return -ENODEV;
-
-       data->lcd_contrast = contrast & 0x0ff;
-       spin_lock_irqsave(&data->lock, flags);
-       hid_set_field(report->field[0], 0, data->lcd_contrast);
-       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-       return 0;
-}
-
-static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
-{
-       return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
-}
-
-static struct lcd_ops picolcd_lcdops = {
-       .get_contrast   = picolcd_get_contrast,
-       .set_contrast   = picolcd_set_contrast,
-       .check_fb       = picolcd_check_lcd_fb,
-};
-
-static int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
-{
-       struct device *dev = &data->hdev->dev;
-       struct lcd_device *ldev;
-
-       if (!report)
-               return -ENODEV;
-       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
-                       report->field[0]->report_size != 8) {
-               dev_err(dev, "unsupported CONTRAST report");
-               return -EINVAL;
-       }
-
-       ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
-       if (IS_ERR(ldev)) {
-               dev_err(dev, "failed to register LCD\n");
-               return PTR_ERR(ldev);
-       }
-       ldev->props.max_contrast = 0x0ff;
-       data->lcd_contrast = 0xe5;
-       data->lcd = ldev;
-       picolcd_set_contrast(ldev, 0xe5);
-       return 0;
-}
-
-static void picolcd_exit_lcd(struct picolcd_data *data)
-{
-       struct lcd_device *ldev = data->lcd;
-
-       data->lcd = NULL;
-       if (ldev)
-               lcd_device_unregister(ldev);
-}
-
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
-       if (!data->lcd)
-               return 0;
-       return picolcd_set_contrast(data->lcd, data->lcd_contrast);
-}
-#else
-static inline int picolcd_init_lcd(struct picolcd_data *data,
-               struct hid_report *report)
-{
-       return 0;
-}
-static inline void picolcd_exit_lcd(struct picolcd_data *data)
-{
-}
-static inline int picolcd_resume_lcd(struct picolcd_data *data)
-{
-       return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LCD */
-
-#ifdef CONFIG_HID_PICOLCD_LEDS
-/**
- * LED class device
- */
-static void picolcd_leds_set(struct picolcd_data *data)
-{
-       struct hid_report *report;
-       unsigned long flags;
-
-       if (!data->led[0])
-               return;
-       report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
-       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
-               return;
-
-       spin_lock_irqsave(&data->lock, flags);
-       hid_set_field(report->field[0], 0, data->led_state);
-       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-}
-
-static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
-                       enum led_brightness value)
-{
-       struct device *dev;
-       struct hid_device *hdev;
-       struct picolcd_data *data;
-       int i, state = 0;
-
-       dev  = led_cdev->dev->parent;
-       hdev = container_of(dev, struct hid_device, dev);
-       data = hid_get_drvdata(hdev);
-       for (i = 0; i < 8; i++) {
-               if (led_cdev != data->led[i])
-                       continue;
-               state = (data->led_state >> i) & 1;
-               if (value == LED_OFF && state) {
-                       data->led_state &= ~(1 << i);
-                       picolcd_leds_set(data);
-               } else if (value != LED_OFF && !state) {
-                       data->led_state |= 1 << i;
-                       picolcd_leds_set(data);
-               }
-               break;
-       }
-}
-
-static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
-{
-       struct device *dev;
-       struct hid_device *hdev;
-       struct picolcd_data *data;
-       int i, value = 0;
-
-       dev  = led_cdev->dev->parent;
-       hdev = container_of(dev, struct hid_device, dev);
-       data = hid_get_drvdata(hdev);
-       for (i = 0; i < 8; i++)
-               if (led_cdev == data->led[i]) {
-                       value = (data->led_state >> i) & 1;
-                       break;
-               }
-       return value ? LED_FULL : LED_OFF;
-}
-
-static int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
-{
-       struct device *dev = &data->hdev->dev;
-       struct led_classdev *led;
-       size_t name_sz = strlen(dev_name(dev)) + 8;
-       char *name;
-       int i, ret = 0;
-
-       if (!report)
-               return -ENODEV;
-       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
-                       report->field[0]->report_size != 8) {
-               dev_err(dev, "unsupported LED_STATE report");
-               return -EINVAL;
-       }
-
-       for (i = 0; i < 8; i++) {
-               led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
-               if (!led) {
-                       dev_err(dev, "can't allocate memory for LED %d\n", i);
-                       ret = -ENOMEM;
-                       goto err;
-               }
-               name = (void *)(&led[1]);
-               snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
-               led->name = name;
-               led->brightness = 0;
-               led->max_brightness = 1;
-               led->brightness_get = picolcd_led_get_brightness;
-               led->brightness_set = picolcd_led_set_brightness;
-
-               data->led[i] = led;
-               ret = led_classdev_register(dev, data->led[i]);
-               if (ret) {
-                       data->led[i] = NULL;
-                       kfree(led);
-                       dev_err(dev, "can't register LED %d\n", i);
-                       goto err;
-               }
-       }
-       return 0;
-err:
-       for (i = 0; i < 8; i++)
-               if (data->led[i]) {
-                       led = data->led[i];
-                       data->led[i] = NULL;
-                       led_classdev_unregister(led);
-                       kfree(led);
-               }
-       return ret;
-}
-
-static void picolcd_exit_leds(struct picolcd_data *data)
-{
-       struct led_classdev *led;
-       int i;
-
-       for (i = 0; i < 8; i++) {
-               led = data->led[i];
-               data->led[i] = NULL;
-               if (!led)
-                       continue;
-               led_classdev_unregister(led);
-               kfree(led);
-       }
-}
-
-#else
-static inline int picolcd_init_leds(struct picolcd_data *data,
-               struct hid_report *report)
-{
-       return 0;
-}
-static inline void picolcd_exit_leds(struct picolcd_data *data)
-{
-}
-static inline int picolcd_leds_set(struct picolcd_data *data)
-{
-       return 0;
-}
-#endif /* CONFIG_HID_PICOLCD_LEDS */
-
-/*
- * input class device
- */
-static int picolcd_raw_keypad(struct picolcd_data *data,
-               struct hid_report *report, u8 *raw_data, int size)
-{
-       /*
-        * Keypad event
-        * First and second data bytes list currently pressed keys,
-        * 0x00 means no key and at most 2 keys may be pressed at same time
-        */
-       int i, j;
-
-       /* determine newly pressed keys */
-       for (i = 0; i < size; i++) {
-               unsigned int key_code;
-               if (raw_data[i] == 0)
-                       continue;
-               for (j = 0; j < sizeof(data->pressed_keys); j++)
-                       if (data->pressed_keys[j] == raw_data[i])
-                               goto key_already_down;
-               for (j = 0; j < sizeof(data->pressed_keys); j++)
-                       if (data->pressed_keys[j] == 0) {
-                               data->pressed_keys[j] = raw_data[i];
-                               break;
-                       }
-               input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
-               if (raw_data[i] < PICOLCD_KEYS)
-                       key_code = data->keycode[raw_data[i]];
-               else
-                       key_code = KEY_UNKNOWN;
-               if (key_code != KEY_UNKNOWN) {
-                       dbg_hid(PICOLCD_NAME " got key press for %u:%d",
-                                       raw_data[i], key_code);
-                       input_report_key(data->input_keys, key_code, 1);
-               }
-               input_sync(data->input_keys);
-key_already_down:
-               continue;
-       }
-
-       /* determine newly released keys */
-       for (j = 0; j < sizeof(data->pressed_keys); j++) {
-               unsigned int key_code;
-               if (data->pressed_keys[j] == 0)
-                       continue;
-               for (i = 0; i < size; i++)
-                       if (data->pressed_keys[j] == raw_data[i])
-                               goto key_still_down;
-               input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
-               if (data->pressed_keys[j] < PICOLCD_KEYS)
-                       key_code = data->keycode[data->pressed_keys[j]];
-               else
-                       key_code = KEY_UNKNOWN;
-               if (key_code != KEY_UNKNOWN) {
-                       dbg_hid(PICOLCD_NAME " got key release for %u:%d",
-                                       data->pressed_keys[j], key_code);
-                       input_report_key(data->input_keys, key_code, 0);
-               }
-               input_sync(data->input_keys);
-               data->pressed_keys[j] = 0;
-key_still_down:
-               continue;
-       }
-       return 1;
-}
-
-static int picolcd_raw_cir(struct picolcd_data *data,
-               struct hid_report *report, u8 *raw_data, int size)
-{
-       /* Need understanding of CIR data format to implement ... */
-       return 1;
-}
-
-static int picolcd_check_version(struct hid_device *hdev)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       struct picolcd_pending *verinfo;
-       int ret = 0;
-
-       if (!data)
-               return -ENODEV;
-
-       verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
-       if (!verinfo) {
-               hid_err(hdev, "no version response from PicoLCD\n");
-               return -ENODEV;
-       }
-
-       if (verinfo->raw_size == 2) {
-               data->version[0] = verinfo->raw_data[1];
-               data->version[1] = verinfo->raw_data[0];
-               if (data->status & PICOLCD_BOOTLOADER) {
-                       hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
-                                verinfo->raw_data[1], verinfo->raw_data[0]);
-               } else {
-                       hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
-                                verinfo->raw_data[1], verinfo->raw_data[0]);
-               }
-       } else {
-               hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
-               ret = -EINVAL;
-       }
-       kfree(verinfo);
-       return ret;
-}
-
-/*
- * Reset our device and wait for answer to VERSION request
- */
-static int picolcd_reset(struct hid_device *hdev)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
-       unsigned long flags;
-       int error;
-
-       if (!data || !report || report->maxfield != 1)
-               return -ENODEV;
-
-       spin_lock_irqsave(&data->lock, flags);
-       if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
-               data->status |= PICOLCD_BOOTLOADER;
-
-       /* perform the reset */
-       hid_set_field(report->field[0], 0, 1);
-       usbhid_submit_report(hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       error = picolcd_check_version(hdev);
-       if (error)
-               return error;
-
-       picolcd_resume_lcd(data);
-       picolcd_resume_backlight(data);
-#ifdef CONFIG_HID_PICOLCD_FB
-       if (data->fb_info)
-               schedule_delayed_work(&data->fb_info->deferred_work, 0);
-#endif /* CONFIG_HID_PICOLCD_FB */
-
-       picolcd_leds_set(data);
-       return 0;
-}
-
-/*
- * The "operation_mode" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-
-       if (data->status & PICOLCD_BOOTLOADER)
-               return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
-       else
-               return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
-}
-
-static ssize_t picolcd_operation_mode_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-       struct hid_report *report = NULL;
-       size_t cnt = count;
-       int timeout = data->opmode_delay;
-       unsigned long flags;
-
-       if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
-               if (data->status & PICOLCD_BOOTLOADER)
-                       report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
-               buf += 3;
-               cnt -= 3;
-       } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
-               if (!(data->status & PICOLCD_BOOTLOADER))
-                       report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
-               buf += 10;
-               cnt -= 10;
-       }
-       if (!report)
-               return -EINVAL;
-
-       while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
-               cnt--;
-       if (cnt != 0)
-               return -EINVAL;
-
-       spin_lock_irqsave(&data->lock, flags);
-       hid_set_field(report->field[0], 0, timeout & 0xff);
-       hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
-       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
-       spin_unlock_irqrestore(&data->lock, flags);
-       return count;
-}
-
-static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
-               picolcd_operation_mode_store);
-
-/*
- * The "operation_mode_delay" sysfs attribute
- */
-static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
-               struct device_attribute *attr, char *buf)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-
-       return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
-}
-
-static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
-               struct device_attribute *attr, const char *buf, size_t count)
-{
-       struct picolcd_data *data = dev_get_drvdata(dev);
-       unsigned u;
-       if (sscanf(buf, "%u", &u) != 1)
-               return -EINVAL;
-       if (u > 30000)
-               return -EINVAL;
-       else
-               data->opmode_delay = u;
-       return count;
-}
-
-static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
-               picolcd_operation_mode_delay_store);
-
-
-#ifdef CONFIG_DEBUG_FS
-/*
- * The "reset" file
- */
-static int picolcd_debug_reset_show(struct seq_file *f, void *p)
-{
-       if (picolcd_fbinfo((struct picolcd_data *)f->private))
-               seq_printf(f, "all fb\n");
-       else
-               seq_printf(f, "all\n");
-       return 0;
-}
-
-static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
-{
-       return single_open(f, picolcd_debug_reset_show, inode->i_private);
-}
-
-static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
-               size_t count, loff_t *ppos)
-{
-       struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
-       char buf[32];
-       size_t cnt = min(count, sizeof(buf)-1);
-       if (copy_from_user(buf, user_buf, cnt))
-               return -EFAULT;
-
-       while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
-               cnt--;
-       buf[cnt] = '\0';
-       if (strcmp(buf, "all") == 0) {
-               picolcd_reset(data->hdev);
-               picolcd_fb_reset(data, 1);
-       } else if (strcmp(buf, "fb") == 0) {
-               picolcd_fb_reset(data, 1);
-       } else {
-               return -EINVAL;
-       }
-       return count;
-}
-
-static const struct file_operations picolcd_debug_reset_fops = {
-       .owner    = THIS_MODULE,
-       .open     = picolcd_debug_reset_open,
-       .read     = seq_read,
-       .llseek   = seq_lseek,
-       .write    = picolcd_debug_reset_write,
-       .release  = single_release,
-};
-
-/*
- * The "eeprom" file
- */
-static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
-               size_t s, loff_t *off)
-{
-       struct picolcd_data *data = f->private_data;
-       struct picolcd_pending *resp;
-       u8 raw_data[3];
-       ssize_t ret = -EIO;
-
-       if (s == 0)
-               return -EINVAL;
-       if (*off > 0x0ff)
-               return 0;
-
-       /* prepare buffer with info about what we want to read (addr & len) */
-       raw_data[0] = *off & 0xff;
-       raw_data[1] = (*off >> 8) & 0xff;
-       raw_data[2] = s < 20 ? s : 20;
-       if (*off + raw_data[2] > 0xff)
-               raw_data[2] = 0x100 - *off;
-       resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
-                       sizeof(raw_data));
-       if (!resp)
-               return -EIO;
-
-       if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
-               /* successful read :) */
-               ret = resp->raw_data[2];
-               if (ret > s)
-                       ret = s;
-               if (copy_to_user(u, resp->raw_data+3, ret))
-                       ret = -EFAULT;
-               else
-                       *off += ret;
-       } /* anything else is some kind of IO error */
-
-       kfree(resp);
-       return ret;
-}
-
-static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
-               size_t s, loff_t *off)
-{
-       struct picolcd_data *data = f->private_data;
-       struct picolcd_pending *resp;
-       ssize_t ret = -EIO;
-       u8 raw_data[23];
-
-       if (s == 0)
-               return -EINVAL;
-       if (*off > 0x0ff)
-               return -ENOSPC;
-
-       memset(raw_data, 0, sizeof(raw_data));
-       raw_data[0] = *off & 0xff;
-       raw_data[1] = (*off >> 8) & 0xff;
-       raw_data[2] = min((size_t)20, s);
-       if (*off + raw_data[2] > 0xff)
-               raw_data[2] = 0x100 - *off;
-
-       if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
-               return -EFAULT;
-       resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
-                       sizeof(raw_data));
-
-       if (!resp)
-               return -EIO;
-
-       if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
-               /* check if written data matches */
-               if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
-                       *off += raw_data[2];
-                       ret = raw_data[2];
-               }
-       }
-       kfree(resp);
-       return ret;
-}
-
-/*
- * Notes:
- * - read/write happens in chunks of at most 20 bytes, it's up to userspace
- *   to loop in order to get more data.
- * - on write errors on otherwise correct write request the bytes
- *   that should have been written are in undefined state.
- */
-static const struct file_operations picolcd_debug_eeprom_fops = {
-       .owner    = THIS_MODULE,
-       .open     = simple_open,
-       .read     = picolcd_debug_eeprom_read,
-       .write    = picolcd_debug_eeprom_write,
-       .llseek   = generic_file_llseek,
-};
-
-/*
- * The "flash" file
- */
-/* record a flash address to buf (bounds check to be done by caller) */
-static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
-{
-       buf[0] = off & 0xff;
-       buf[1] = (off >> 8) & 0xff;
-       if (data->addr_sz == 3)
-               buf[2] = (off >> 16) & 0xff;
-       return data->addr_sz == 2 ? 2 : 3;
-}
-
-/* read a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
-               char __user *u, size_t s, loff_t *off)
-{
-       struct picolcd_pending *resp;
-       u8 raw_data[4];
-       ssize_t ret = 0;
-       int len_off, err = -EIO;
-
-       while (s > 0) {
-               err = -EIO;
-               len_off = _picolcd_flash_setaddr(data, raw_data, *off);
-               raw_data[len_off] = s > 32 ? 32 : s;
-               resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
-               if (!resp || !resp->in_report)
-                       goto skip;
-               if (resp->in_report->id == REPORT_MEMORY ||
-                       resp->in_report->id == REPORT_BL_READ_MEMORY) {
-                       if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
-                               goto skip;
-                       if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
-                               err = -EFAULT;
-                               goto skip;
-                       }
-                       *off += raw_data[len_off];
-                       s    -= raw_data[len_off];
-                       ret  += raw_data[len_off];
-                       err   = 0;
-               }
-skip:
-               kfree(resp);
-               if (err)
-                       return ret > 0 ? ret : err;
-       }
-       return ret;
-}
-
-static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
-               size_t s, loff_t *off)
-{
-       struct picolcd_data *data = f->private_data;
-
-       if (s == 0)
-               return -EINVAL;
-       if (*off > 0x05fff)
-               return 0;
-       if (*off + s > 0x05fff)
-               s = 0x06000 - *off;
-
-       if (data->status & PICOLCD_BOOTLOADER)
-               return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
-       else
-               return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
-}
-
-/* erase block aligned to 64bytes boundary */
-static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
-               loff_t *off)
-{
-       struct picolcd_pending *resp;
-       u8 raw_data[3];
-       int len_off;
-       ssize_t ret = -EIO;
-
-       if (*off & 0x3f)
-               return -EINVAL;
-
-       len_off = _picolcd_flash_setaddr(data, raw_data, *off);
-       resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
-       if (!resp || !resp->in_report)
-               goto skip;
-       if (resp->in_report->id == REPORT_MEMORY ||
-               resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
-               if (memcmp(raw_data, resp->raw_data, len_off) != 0)
-                       goto skip;
-               ret = 0;
-       }
-skip:
-       kfree(resp);
-       return ret;
-}
-
-/* write a given size of data (bounds check to be done by caller) */
-static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
-               const char __user *u, size_t s, loff_t *off)
-{
-       struct picolcd_pending *resp;
-       u8 raw_data[36];
-       ssize_t ret = 0;
-       int len_off, err = -EIO;
-
-       while (s > 0) {
-               err = -EIO;
-               len_off = _picolcd_flash_setaddr(data, raw_data, *off);
-               raw_data[len_off] = s > 32 ? 32 : s;
-               if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
-                       err = -EFAULT;
-                       break;
-               }
-               resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
-                               len_off+1+raw_data[len_off]);
-               if (!resp || !resp->in_report)
-                       goto skip;
-               if (resp->in_report->id == REPORT_MEMORY ||
-                       resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
-                       if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
-                               goto skip;
-                       *off += raw_data[len_off];
-                       s    -= raw_data[len_off];
-                       ret  += raw_data[len_off];
-                       err   = 0;
-               }
-skip:
-               kfree(resp);
-               if (err)
-                       break;
-       }
-       return ret > 0 ? ret : err;
-}
-
-static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
-               size_t s, loff_t *off)
-{
-       struct picolcd_data *data = f->private_data;
-       ssize_t err, ret = 0;
-       int report_erase, report_write;
-
-       if (s == 0)
-               return -EINVAL;
-       if (*off > 0x5fff)
-               return -ENOSPC;
-       if (s & 0x3f)
-               return -EINVAL;
-       if (*off & 0x3f)
-               return -EINVAL;
-
-       if (data->status & PICOLCD_BOOTLOADER) {
-               report_erase = REPORT_BL_ERASE_MEMORY;
-               report_write = REPORT_BL_WRITE_MEMORY;
-       } else {
-               report_erase = REPORT_ERASE_MEMORY;
-               report_write = REPORT_WRITE_MEMORY;
-       }
-       mutex_lock(&data->mutex_flash);
-       while (s > 0) {
-               err = _picolcd_flash_erase64(data, report_erase, off);
-               if (err)
-                       break;
-               err = _picolcd_flash_write(data, report_write, u, 64, off);
-               if (err < 0)
-                       break;
-               ret += err;
-               *off += err;
-               s -= err;
-               if (err != 64)
-                       break;
-       }
-       mutex_unlock(&data->mutex_flash);
-       return ret > 0 ? ret : err;
-}
-
-/*
- * Notes:
- * - concurrent writing is prevented by mutex and all writes must be
- *   n*64 bytes and 64-byte aligned, each write being preceded by an
- *   ERASE which erases a 64byte block.
- *   If less than requested was written or an error is returned for an
- *   otherwise correct write request the next 64-byte block which should
- *   have been written is in undefined state (mostly: original, erased,
- *   (half-)written with write error)
- * - reading can happen without special restriction
- */
-static const struct file_operations picolcd_debug_flash_fops = {
-       .owner    = THIS_MODULE,
-       .open     = simple_open,
-       .read     = picolcd_debug_flash_read,
-       .write    = picolcd_debug_flash_write,
-       .llseek   = generic_file_llseek,
-};
-
-
-/*
- * Helper code for HID report level dumping/debugging
- */
-static const char *error_codes[] = {
-       "success", "parameter missing", "data_missing", "block readonly",
-       "block not erasable", "block too big", "section overflow",
-       "invalid command length", "invalid data length",
-};
-
-static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
-               const size_t data_len)
-{
-       int i, j;
-       for (i = j = 0; i < data_len && j + 3 < dst_sz; i++) {
-               dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
-               dst[j++] = hex_asc[data[i] & 0x0f];
-               dst[j++] = ' ';
-       }
-       if (j < dst_sz) {
-               dst[j--] = '\0';
-               dst[j] = '\n';
-       } else
-               dst[j] = '\0';
-}
-
-static void picolcd_debug_out_report(struct picolcd_data *data,
-               struct hid_device *hdev, struct hid_report *report)
-{
-       u8 raw_data[70];
-       int raw_size = (report->size >> 3) + 1;
-       char *buff;
-#define BUFF_SZ 256
-
-       /* Avoid unnecessary overhead if debugfs is disabled */
-       if (list_empty(&hdev->debug_list))
-               return;
-
-       buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
-       if (!buff)
-               return;
-
-       snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
-                       report->id, raw_size);
-       hid_debug_event(hdev, buff);
-       if (raw_size + 5 > sizeof(raw_data)) {
-               kfree(buff);
-               hid_debug_event(hdev, " TOO BIG\n");
-               return;
-       } else {
-               raw_data[0] = report->id;
-               hid_output_report(report, raw_data);
-               dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
-               hid_debug_event(hdev, buff);
-       }
-
-       switch (report->id) {
-       case REPORT_LED_STATE:
-               /* 1 data byte with GPO state */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_LED_STATE", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_BRIGHTNESS:
-               /* 1 data byte with brightness */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_BRIGHTNESS", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_CONTRAST:
-               /* 1 data byte with contrast */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_CONTRAST", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_RESET:
-               /* 2 data bytes with reset duration in ms */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_RESET", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
-                               raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_LCD_CMD:
-               /* 63 data bytes with LCD commands */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_LCD_CMD", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               /* TODO: format decoding */
-               break;
-       case REPORT_LCD_DATA:
-               /* 63 data bytes with LCD data */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_LCD_CMD", report->id, raw_size-1);
-               /* TODO: format decoding */
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_LCD_CMD_DATA:
-               /* 63 data bytes with LCD commands and data */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_LCD_CMD", report->id, raw_size-1);
-               /* TODO: format decoding */
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_EE_READ:
-               /* 3 data bytes with read area description */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_EE_READ", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_EE_WRITE:
-               /* 3+1..20 data bytes with write area description */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_EE_WRITE", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-               hid_debug_event(hdev, buff);
-               if (raw_data[3] == 0) {
-                       snprintf(buff, BUFF_SZ, "\tNo data\n");
-               } else if (raw_data[3] + 4 <= raw_size) {
-                       snprintf(buff, BUFF_SZ, "\tData: ");
-                       hid_debug_event(hdev, buff);
-                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
-               } else {
-                       snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-               }
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_ERASE_MEMORY:
-       case REPORT_BL_ERASE_MEMORY:
-               /* 3 data bytes with pointer inside erase block */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_ERASE_MEMORY", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               switch (data->addr_sz) {
-               case 2:
-                       snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
-                                       raw_data[2], raw_data[1]);
-                       break;
-               case 3:
-                       snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
-                                       raw_data[3], raw_data[2], raw_data[1]);
-                       break;
-               default:
-                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
-               }
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_READ_MEMORY:
-       case REPORT_BL_READ_MEMORY:
-               /* 4 data bytes with read area description */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_READ_MEMORY", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               switch (data->addr_sz) {
-               case 2:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                                       raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-                       break;
-               case 3:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
-                                       raw_data[3], raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
-                       break;
-               default:
-                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
-               }
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_WRITE_MEMORY:
-       case REPORT_BL_WRITE_MEMORY:
-               /* 4+1..32 data bytes with write adrea description */
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_WRITE_MEMORY", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               switch (data->addr_sz) {
-               case 2:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                                       raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-                       hid_debug_event(hdev, buff);
-                       if (raw_data[3] == 0) {
-                               snprintf(buff, BUFF_SZ, "\tNo data\n");
-                       } else if (raw_data[3] + 4 <= raw_size) {
-                               snprintf(buff, BUFF_SZ, "\tData: ");
-                               hid_debug_event(hdev, buff);
-                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
-                       } else {
-                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-                       }
-                       break;
-               case 3:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
-                                       raw_data[3], raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
-                       hid_debug_event(hdev, buff);
-                       if (raw_data[4] == 0) {
-                               snprintf(buff, BUFF_SZ, "\tNo data\n");
-                       } else if (raw_data[4] + 5 <= raw_size) {
-                               snprintf(buff, BUFF_SZ, "\tData: ");
-                               hid_debug_event(hdev, buff);
-                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
-                       } else {
-                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-                       }
-                       break;
-               default:
-                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
-               }
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_SPLASH_RESTART:
-               /* TODO */
-               break;
-       case REPORT_EXIT_KEYBOARD:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
-                               raw_data[1] | (raw_data[2] << 8),
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_VERSION:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_VERSION", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_DEVID:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_DEVID", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_SPLASH_SIZE:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_SPLASH_SIZE", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_HOOK_VERSION:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_HOOK_VERSION", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_EXIT_FLASHER:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "REPORT_VERSION", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
-                               raw_data[1] | (raw_data[2] << 8),
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       default:
-               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
-                       "<unknown>", report->id, raw_size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       }
-       wake_up_interruptible(&hdev->debug_wait);
-       kfree(buff);
-}
-
-static void picolcd_debug_raw_event(struct picolcd_data *data,
-               struct hid_device *hdev, struct hid_report *report,
-               u8 *raw_data, int size)
-{
-       char *buff;
-
-#define BUFF_SZ 256
-       /* Avoid unnecessary overhead if debugfs is disabled */
-       if (!hdev->debug_events)
-               return;
-
-       buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
-       if (!buff)
-               return;
-
-       switch (report->id) {
-       case REPORT_ERROR_CODE:
-               /* 2 data bytes with affected report and error code */
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_ERROR_CODE", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               if (raw_data[2] < ARRAY_SIZE(error_codes))
-                       snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
-                                       raw_data[2], error_codes[raw_data[2]], raw_data[1]);
-               else
-                       snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
-                                       raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_KEY_STATE:
-               /* 2 data bytes with key state */
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_KEY_STATE", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               if (raw_data[1] == 0)
-                       snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
-               else if (raw_data[2] == 0)
-                       snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
-                                       raw_data[1], raw_data[1]);
-               else
-                       snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
-                                       raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_IR_DATA:
-               /* Up to 20 byes of IR scancode data */
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_IR_DATA", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               if (raw_data[1] == 0) {
-                       snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
-                       hid_debug_event(hdev, buff);
-               } else if (raw_data[1] + 1 <= size) {
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
-                                       raw_data[1]-1);
-                       hid_debug_event(hdev, buff);
-                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]-1);
-                       hid_debug_event(hdev, buff);
-               } else {
-                       snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
-                                       raw_data[1]-1);
-                       hid_debug_event(hdev, buff);
-               }
-               break;
-       case REPORT_EE_DATA:
-               /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_EE_DATA", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-               hid_debug_event(hdev, buff);
-               if (raw_data[3] == 0) {
-                       snprintf(buff, BUFF_SZ, "\tNo data\n");
-                       hid_debug_event(hdev, buff);
-               } else if (raw_data[3] + 4 <= size) {
-                       snprintf(buff, BUFF_SZ, "\tData: ");
-                       hid_debug_event(hdev, buff);
-                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
-                       hid_debug_event(hdev, buff);
-               } else {
-                       snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-                       hid_debug_event(hdev, buff);
-               }
-               break;
-       case REPORT_MEMORY:
-               /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_MEMORY", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               switch (data->addr_sz) {
-               case 2:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
-                                       raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
-                       hid_debug_event(hdev, buff);
-                       if (raw_data[3] == 0) {
-                               snprintf(buff, BUFF_SZ, "\tNo data\n");
-                       } else if (raw_data[3] + 4 <= size) {
-                               snprintf(buff, BUFF_SZ, "\tData: ");
-                               hid_debug_event(hdev, buff);
-                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
-                       } else {
-                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-                       }
-                       break;
-               case 3:
-                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
-                                       raw_data[3], raw_data[2], raw_data[1]);
-                       hid_debug_event(hdev, buff);
-                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
-                       hid_debug_event(hdev, buff);
-                       if (raw_data[4] == 0) {
-                               snprintf(buff, BUFF_SZ, "\tNo data\n");
-                       } else if (raw_data[4] + 5 <= size) {
-                               snprintf(buff, BUFF_SZ, "\tData: ");
-                               hid_debug_event(hdev, buff);
-                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
-                       } else {
-                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
-                       }
-                       break;
-               default:
-                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
-               }
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_VERSION:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_VERSION", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
-                               raw_data[2], raw_data[1]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_BL_ERASE_MEMORY:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_BL_ERASE_MEMORY", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               /* TODO */
-               break;
-       case REPORT_BL_READ_MEMORY:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_BL_READ_MEMORY", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               /* TODO */
-               break;
-       case REPORT_BL_WRITE_MEMORY:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_BL_WRITE_MEMORY", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               /* TODO */
-               break;
-       case REPORT_DEVID:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_DEVID", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
-                               raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
-                               raw_data[5]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_SPLASH_SIZE:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_SPLASH_SIZE", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
-                               (raw_data[2] << 8) | raw_data[1]);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
-                               (raw_data[4] << 8) | raw_data[3]);
-               hid_debug_event(hdev, buff);
-               break;
-       case REPORT_HOOK_VERSION:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "REPORT_HOOK_VERSION", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
-                               raw_data[1], raw_data[2]);
-               hid_debug_event(hdev, buff);
-               break;
-       default:
-               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
-                       "<unknown>", report->id, size-1);
-               hid_debug_event(hdev, buff);
-               break;
-       }
-       wake_up_interruptible(&hdev->debug_wait);
-       kfree(buff);
-}
-
-static void picolcd_init_devfs(struct picolcd_data *data,
-               struct hid_report *eeprom_r, struct hid_report *eeprom_w,
-               struct hid_report *flash_r, struct hid_report *flash_w,
-               struct hid_report *reset)
-{
-       struct hid_device *hdev = data->hdev;
-
-       mutex_init(&data->mutex_flash);
-
-       /* reset */
-       if (reset)
-               data->debug_reset = debugfs_create_file("reset", 0600,
-                               hdev->debug_dir, data, &picolcd_debug_reset_fops);
-
-       /* eeprom */
-       if (eeprom_r || eeprom_w)
-               data->debug_eeprom = debugfs_create_file("eeprom",
-                       (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
-                       hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
-
-       /* flash */
-       if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
-               data->addr_sz = flash_r->field[0]->report_count - 1;
-       else
-               data->addr_sz = -1;
-       if (data->addr_sz == 2 || data->addr_sz == 3) {
-               data->debug_flash = debugfs_create_file("flash",
-                       (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
-                       hdev->debug_dir, data, &picolcd_debug_flash_fops);
-       } else if (flash_r || flash_w)
-               hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
-}
-
-static void picolcd_exit_devfs(struct picolcd_data *data)
-{
-       struct dentry *dent;
-
-       dent = data->debug_reset;
-       data->debug_reset = NULL;
-       if (dent)
-               debugfs_remove(dent);
-       dent = data->debug_eeprom;
-       data->debug_eeprom = NULL;
-       if (dent)
-               debugfs_remove(dent);
-       dent = data->debug_flash;
-       data->debug_flash = NULL;
-       if (dent)
-               debugfs_remove(dent);
-       mutex_destroy(&data->mutex_flash);
-}
-#else
-static inline void picolcd_debug_raw_event(struct picolcd_data *data,
-               struct hid_device *hdev, struct hid_report *report,
-               u8 *raw_data, int size)
-{
-}
-static inline void picolcd_init_devfs(struct picolcd_data *data,
-               struct hid_report *eeprom_r, struct hid_report *eeprom_w,
-               struct hid_report *flash_r, struct hid_report *flash_w,
-               struct hid_report *reset)
-{
-}
-static inline void picolcd_exit_devfs(struct picolcd_data *data)
-{
-}
-#endif /* CONFIG_DEBUG_FS */
-
-/*
- * Handle raw report as sent by device
- */
-static int picolcd_raw_event(struct hid_device *hdev,
-               struct hid_report *report, u8 *raw_data, int size)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       unsigned long flags;
-       int ret = 0;
-
-       if (!data)
-               return 1;
-
-       if (report->id == REPORT_KEY_STATE) {
-               if (data->input_keys)
-                       ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
-       } else if (report->id == REPORT_IR_DATA) {
-               if (data->input_cir)
-                       ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
-       } else {
-               spin_lock_irqsave(&data->lock, flags);
-               /*
-                * We let the caller of picolcd_send_and_wait() check if the
-                * report we got is one of the expected ones or not.
-                */
-               if (data->pending) {
-                       memcpy(data->pending->raw_data, raw_data+1, size-1);
-                       data->pending->raw_size  = size-1;
-                       data->pending->in_report = report;
-                       complete(&data->pending->ready);
-               }
-               spin_unlock_irqrestore(&data->lock, flags);
-       }
-
-       picolcd_debug_raw_event(data, hdev, report, raw_data, size);
-       return 1;
-}
-
-#ifdef CONFIG_PM
-static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
-{
-       if (PMSG_IS_AUTO(message))
-               return 0;
-
-       picolcd_suspend_backlight(hid_get_drvdata(hdev));
-       dbg_hid(PICOLCD_NAME " device ready for suspend\n");
-       return 0;
-}
-
-static int picolcd_resume(struct hid_device *hdev)
-{
-       int ret;
-       ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
-       if (ret)
-               dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
-       return 0;
-}
-
-static int picolcd_reset_resume(struct hid_device *hdev)
-{
-       int ret;
-       ret = picolcd_reset(hdev);
-       if (ret)
-               dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
-       ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
-       if (ret)
-               dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
-       ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
-       if (ret)
-               dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
-       ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
-       if (ret)
-               dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
-       picolcd_leds_set(hid_get_drvdata(hdev));
-       return 0;
-}
-#endif
-
-/* initialize keypad input device */
-static int picolcd_init_keys(struct picolcd_data *data,
-               struct hid_report *report)
-{
-       struct hid_device *hdev = data->hdev;
-       struct input_dev *idev;
-       int error, i;
-
-       if (!report)
-               return -ENODEV;
-       if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
-                       report->field[0]->report_size != 8) {
-               hid_err(hdev, "unsupported KEY_STATE report\n");
-               return -EINVAL;
-       }
-
-       idev = input_allocate_device();
-       if (idev == NULL) {
-               hid_err(hdev, "failed to allocate input device\n");
-               return -ENOMEM;
-       }
-       input_set_drvdata(idev, hdev);
-       memcpy(data->keycode, def_keymap, sizeof(def_keymap));
-       idev->name = hdev->name;
-       idev->phys = hdev->phys;
-       idev->uniq = hdev->uniq;
-       idev->id.bustype = hdev->bus;
-       idev->id.vendor  = hdev->vendor;
-       idev->id.product = hdev->product;
-       idev->id.version = hdev->version;
-       idev->dev.parent = hdev->dev.parent;
-       idev->keycode     = &data->keycode;
-       idev->keycodemax  = PICOLCD_KEYS;
-       idev->keycodesize = sizeof(data->keycode[0]);
-       input_set_capability(idev, EV_MSC, MSC_SCAN);
-       set_bit(EV_REP, idev->evbit);
-       for (i = 0; i < PICOLCD_KEYS; i++)
-               input_set_capability(idev, EV_KEY, data->keycode[i]);
-       error = input_register_device(idev);
-       if (error) {
-               hid_err(hdev, "error registering the input device\n");
-               input_free_device(idev);
-               return error;
-       }
-       data->input_keys = idev;
-       return 0;
-}
-
-static void picolcd_exit_keys(struct picolcd_data *data)
-{
-       struct input_dev *idev = data->input_keys;
-
-       data->input_keys = NULL;
-       if (idev)
-               input_unregister_device(idev);
-}
-
-/* initialize CIR input device */
-static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
-{
-       /* support not implemented yet */
-       return 0;
-}
-
-static inline void picolcd_exit_cir(struct picolcd_data *data)
-{
-}
-
-static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
-{
-       int error;
-
-       error = picolcd_check_version(hdev);
-       if (error)
-               return error;
-
-       if (data->version[0] != 0 && data->version[1] != 3)
-               hid_info(hdev, "Device with untested firmware revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
-                        dev_name(&hdev->dev));
-
-       /* Setup keypad input device */
-       error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
-       if (error)
-               goto err;
-
-       /* Setup CIR input device */
-       error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
-       if (error)
-               goto err;
-
-       /* Set up the framebuffer device */
-       error = picolcd_init_framebuffer(data);
-       if (error)
-               goto err;
-
-       /* Setup lcd class device */
-       error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
-       if (error)
-               goto err;
-
-       /* Setup backlight class device */
-       error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
-       if (error)
-               goto err;
-
-       /* Setup the LED class devices */
-       error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
-       if (error)
-               goto err;
-
-       picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
-                       picolcd_out_report(REPORT_EE_WRITE, hdev),
-                       picolcd_out_report(REPORT_READ_MEMORY, hdev),
-                       picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
-                       picolcd_out_report(REPORT_RESET, hdev));
-       return 0;
-err:
-       picolcd_exit_leds(data);
-       picolcd_exit_backlight(data);
-       picolcd_exit_lcd(data);
-       picolcd_exit_framebuffer(data);
-       picolcd_exit_cir(data);
-       picolcd_exit_keys(data);
-       return error;
-}
-
-static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
-{
-       int error;
-
-       error = picolcd_check_version(hdev);
-       if (error)
-               return error;
-
-       if (data->version[0] != 1 && data->version[1] != 0)
-               hid_info(hdev, "Device with untested bootloader revision, please submit /sys/kernel/debug/hid/%s/rdesc for this device.\n",
-                        dev_name(&hdev->dev));
-
-       picolcd_init_devfs(data, NULL, NULL,
-                       picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
-                       picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
-       return 0;
-}
-
-static int picolcd_probe(struct hid_device *hdev,
-                    const struct hid_device_id *id)
-{
-       struct picolcd_data *data;
-       int error = -ENOMEM;
-
-       dbg_hid(PICOLCD_NAME " hardware probe...\n");
-
-       /*
-        * Let's allocate the picolcd data structure, set some reasonable
-        * defaults, and associate it with the device
-        */
-       data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
-       if (data == NULL) {
-               hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
-               error = -ENOMEM;
-               goto err_no_cleanup;
-       }
-
-       spin_lock_init(&data->lock);
-       mutex_init(&data->mutex);
-       data->hdev = hdev;
-       data->opmode_delay = 5000;
-       if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
-               data->status |= PICOLCD_BOOTLOADER;
-       hid_set_drvdata(hdev, data);
-
-       /* Parse the device reports and start it up */
-       error = hid_parse(hdev);
-       if (error) {
-               hid_err(hdev, "device report parse failed\n");
-               goto err_cleanup_data;
-       }
-
-       error = hid_hw_start(hdev, 0);
-       if (error) {
-               hid_err(hdev, "hardware start failed\n");
-               goto err_cleanup_data;
-       }
-
-       error = hid_hw_open(hdev);
-       if (error) {
-               hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
-               goto err_cleanup_hid_hw;
-       }
-
-       error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
-       if (error) {
-               hid_err(hdev, "failed to create sysfs attributes\n");
-               goto err_cleanup_hid_ll;
-       }
-
-       error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
-       if (error) {
-               hid_err(hdev, "failed to create sysfs attributes\n");
-               goto err_cleanup_sysfs1;
-       }
-
-       if (data->status & PICOLCD_BOOTLOADER)
-               error = picolcd_probe_bootloader(hdev, data);
-       else
-               error = picolcd_probe_lcd(hdev, data);
-       if (error)
-               goto err_cleanup_sysfs2;
-
-       dbg_hid(PICOLCD_NAME " activated and initialized\n");
-       return 0;
-
-err_cleanup_sysfs2:
-       device_remove_file(&hdev->dev, &dev_attr_operation_mode);
-err_cleanup_sysfs1:
-       device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
-err_cleanup_hid_ll:
-       hid_hw_close(hdev);
-err_cleanup_hid_hw:
-       hid_hw_stop(hdev);
-err_cleanup_data:
-       kfree(data);
-err_no_cleanup:
-       hid_set_drvdata(hdev, NULL);
-
-       return error;
-}
-
-static void picolcd_remove(struct hid_device *hdev)
-{
-       struct picolcd_data *data = hid_get_drvdata(hdev);
-       unsigned long flags;
-
-       dbg_hid(PICOLCD_NAME " hardware remove...\n");
-       spin_lock_irqsave(&data->lock, flags);
-       data->status |= PICOLCD_FAILED;
-       spin_unlock_irqrestore(&data->lock, flags);
-#ifdef CONFIG_HID_PICOLCD_FB
-       /* short-circuit FB as early as possible in order to
-        * avoid long delays if we host console.
-        */
-       if (data->fb_info)
-               data->fb_info->par = NULL;
-#endif
-
-       picolcd_exit_devfs(data);
-       device_remove_file(&hdev->dev, &dev_attr_operation_mode);
-       device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
-       hid_hw_close(hdev);
-       hid_hw_stop(hdev);
-       hid_set_drvdata(hdev, NULL);
-
-       /* Shortcut potential pending reply that will never arrive */
-       spin_lock_irqsave(&data->lock, flags);
-       if (data->pending)
-               complete(&data->pending->ready);
-       spin_unlock_irqrestore(&data->lock, flags);
-
-       /* Cleanup LED */
-       picolcd_exit_leds(data);
-       /* Clean up the framebuffer */
-       picolcd_exit_backlight(data);
-       picolcd_exit_lcd(data);
-       picolcd_exit_framebuffer(data);
-       /* Cleanup input */
-       picolcd_exit_cir(data);
-       picolcd_exit_keys(data);
-
-       mutex_destroy(&data->mutex);
-       /* Finally, clean up the picolcd data itself */
-       kfree(data);
-}
-
-static const struct hid_device_id picolcd_devices[] = {
-       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
-       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
-       { }
-};
-MODULE_DEVICE_TABLE(hid, picolcd_devices);
-
-static struct hid_driver picolcd_driver = {
-       .name =          "hid-picolcd",
-       .id_table =      picolcd_devices,
-       .probe =         picolcd_probe,
-       .remove =        picolcd_remove,
-       .raw_event =     picolcd_raw_event,
-#ifdef CONFIG_PM
-       .suspend =       picolcd_suspend,
-       .resume =        picolcd_resume,
-       .reset_resume =  picolcd_reset_resume,
-#endif
-};
-
-static int __init picolcd_init(void)
-{
-       return hid_register_driver(&picolcd_driver);
-}
-
-static void __exit picolcd_exit(void)
-{
-       hid_unregister_driver(&picolcd_driver);
-#ifdef CONFIG_HID_PICOLCD_FB
-       flush_work_sync(&picolcd_fb_cleanup);
-       WARN_ON(fb_pending);
-#endif
-}
-
-module_init(picolcd_init);
-module_exit(picolcd_exit);
-MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd.h b/drivers/hid/hid-picolcd.h
new file mode 100644 (file)
index 0000000..020cef6
--- /dev/null
@@ -0,0 +1,309 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#define PICOLCD_NAME "PicoLCD (graphic)"
+
+/* Report numbers */
+#define REPORT_ERROR_CODE      0x10 /* LCD: IN[16]  */
+#define   ERR_SUCCESS            0x00
+#define   ERR_PARAMETER_MISSING  0x01
+#define   ERR_DATA_MISSING       0x02
+#define   ERR_BLOCK_READ_ONLY    0x03
+#define   ERR_BLOCK_NOT_ERASABLE 0x04
+#define   ERR_BLOCK_TOO_BIG      0x05
+#define   ERR_SECTION_OVERFLOW   0x06
+#define   ERR_INVALID_CMD_LEN    0x07
+#define   ERR_INVALID_DATA_LEN   0x08
+#define REPORT_KEY_STATE       0x11 /* LCD: IN[2]   */
+#define REPORT_IR_DATA         0x21 /* LCD: IN[63]  */
+#define REPORT_EE_DATA         0x32 /* LCD: IN[63]  */
+#define REPORT_MEMORY          0x41 /* LCD: IN[63]  */
+#define REPORT_LED_STATE       0x81 /* LCD: OUT[1]  */
+#define REPORT_BRIGHTNESS      0x91 /* LCD: OUT[1]  */
+#define REPORT_CONTRAST        0x92 /* LCD: OUT[1]  */
+#define REPORT_RESET           0x93 /* LCD: OUT[2]  */
+#define REPORT_LCD_CMD         0x94 /* LCD: OUT[63] */
+#define REPORT_LCD_DATA        0x95 /* LCD: OUT[63] */
+#define REPORT_LCD_CMD_DATA    0x96 /* LCD: OUT[63] */
+#define        REPORT_EE_READ         0xa3 /* LCD: OUT[63] */
+#define REPORT_EE_WRITE        0xa4 /* LCD: OUT[63] */
+#define REPORT_ERASE_MEMORY    0xb2 /* LCD: OUT[2]  */
+#define REPORT_READ_MEMORY     0xb3 /* LCD: OUT[3]  */
+#define REPORT_WRITE_MEMORY    0xb4 /* LCD: OUT[63] */
+#define REPORT_SPLASH_RESTART  0xc1 /* LCD: OUT[1]  */
+#define REPORT_EXIT_KEYBOARD   0xef /* LCD: OUT[2]  */
+#define REPORT_VERSION         0xf1 /* LCD: IN[2],OUT[1]    Bootloader: IN[2],OUT[1]   */
+#define REPORT_BL_ERASE_MEMORY 0xf2 /*                      Bootloader: IN[36],OUT[4]  */
+#define REPORT_BL_READ_MEMORY  0xf3 /*                      Bootloader: IN[36],OUT[4]  */
+#define REPORT_BL_WRITE_MEMORY 0xf4 /*                      Bootloader: IN[36],OUT[36] */
+#define REPORT_DEVID           0xf5 /* LCD: IN[5], OUT[1]   Bootloader: IN[5],OUT[1]   */
+#define REPORT_SPLASH_SIZE     0xf6 /* LCD: IN[4], OUT[1]   */
+#define REPORT_HOOK_VERSION    0xf7 /* LCD: IN[2], OUT[1]   */
+#define REPORT_EXIT_FLASHER    0xff /*                      Bootloader: OUT[2]         */
+
+/* Description of in-progress IO operation, used for operations
+ * that trigger response from device */
+struct picolcd_pending {
+       struct hid_report *out_report;
+       struct hid_report *in_report;
+       struct completion ready;
+       int raw_size;
+       u8 raw_data[64];
+};
+
+
+#define PICOLCD_KEYS 17
+
+/* Per device data structure */
+struct picolcd_data {
+       struct hid_device *hdev;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debug_reset;
+       struct dentry *debug_eeprom;
+       struct dentry *debug_flash;
+       struct mutex mutex_flash;
+       int addr_sz;
+#endif
+       u8 version[2];
+       unsigned short opmode_delay;
+       /* input stuff */
+       u8 pressed_keys[2];
+       struct input_dev *input_keys;
+#ifdef CONFIG_HID_PICOLCD_CIR
+       struct rc_dev *rc_dev;
+#endif
+       unsigned short keycode[PICOLCD_KEYS];
+
+#ifdef CONFIG_HID_PICOLCD_FB
+       /* Framebuffer stuff */
+       struct fb_info *fb_info;
+#endif /* CONFIG_HID_PICOLCD_FB */
+#ifdef CONFIG_HID_PICOLCD_LCD
+       struct lcd_device *lcd;
+       u8 lcd_contrast;
+#endif /* CONFIG_HID_PICOLCD_LCD */
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+       struct backlight_device *backlight;
+       u8 lcd_brightness;
+       u8 lcd_power;
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+#ifdef CONFIG_HID_PICOLCD_LEDS
+       /* LED stuff */
+       u8 led_state;
+       struct led_classdev *led[8];
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+       /* Housekeeping stuff */
+       spinlock_t lock;
+       struct mutex mutex;
+       struct picolcd_pending *pending;
+       int status;
+#define PICOLCD_BOOTLOADER 1
+#define PICOLCD_FAILED 2
+#define PICOLCD_CIR_SHUN 4
+};
+
+#ifdef CONFIG_HID_PICOLCD_FB
+struct picolcd_fb_data {
+       /* Framebuffer stuff */
+       spinlock_t lock;
+       struct picolcd_data *picolcd;
+       u8 update_rate;
+       u8 bpp;
+       u8 force;
+       u8 ready;
+       u8 *vbitmap;            /* local copy of what was sent to PicoLCD */
+       u8 *bitmap;             /* framebuffer */
+};
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+/* Find a given report */
+#define picolcd_in_report(id, dev) picolcd_report(id, dev, HID_INPUT_REPORT)
+#define picolcd_out_report(id, dev) picolcd_report(id, dev, HID_OUTPUT_REPORT)
+
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir);
+
+#ifdef CONFIG_DEBUG_FS
+void picolcd_debug_out_report(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report);
+#define usbhid_submit_report(a, b, c) \
+       do { \
+               picolcd_debug_out_report(hid_get_drvdata(a), a, b); \
+               usbhid_submit_report(a, b, c); \
+       } while (0)
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report,
+               u8 *raw_data, int size);
+
+void picolcd_init_devfs(struct picolcd_data *data,
+               struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+               struct hid_report *flash_r, struct hid_report *flash_w,
+               struct hid_report *reset);
+
+void picolcd_exit_devfs(struct picolcd_data *data);
+#else
+static inline void picolcd_debug_out_report(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report)
+{
+}
+static inline void picolcd_debug_raw_event(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report,
+               u8 *raw_data, int size)
+{
+}
+static inline void picolcd_init_devfs(struct picolcd_data *data,
+               struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+               struct hid_report *flash_r, struct hid_report *flash_w,
+               struct hid_report *reset)
+{
+}
+static inline void picolcd_exit_devfs(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_DEBUG_FS */
+
+
+#ifdef CONFIG_HID_PICOLCD_FB
+int picolcd_fb_reset(struct picolcd_data *data, int clear);
+
+int picolcd_init_framebuffer(struct picolcd_data *data);
+
+void picolcd_exit_framebuffer(struct picolcd_data *data);
+
+void picolcd_fb_refresh(struct picolcd_data *data);
+#define picolcd_fbinfo(d) ((d)->fb_info)
+#else
+static inline int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+       return 0;
+}
+static inline int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+       return 0;
+}
+static inline void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+}
+static inline void picolcd_fb_refresh(struct picolcd_data *data)
+{
+}
+#define picolcd_fbinfo(d) NULL
+#endif /* CONFIG_HID_PICOLCD_FB */
+
+
+#ifdef CONFIG_HID_PICOLCD_BACKLIGHT
+int picolcd_init_backlight(struct picolcd_data *data,
+               struct hid_report *report);
+
+void picolcd_exit_backlight(struct picolcd_data *data);
+
+int picolcd_resume_backlight(struct picolcd_data *data);
+
+void picolcd_suspend_backlight(struct picolcd_data *data);
+#else
+static inline int picolcd_init_backlight(struct picolcd_data *data,
+               struct hid_report *report)
+{
+       return 0;
+}
+static inline void picolcd_exit_backlight(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_backlight(struct picolcd_data *data)
+{
+       return 0;
+}
+static inline void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+}
+
+#endif /* CONFIG_HID_PICOLCD_BACKLIGHT */
+
+
+#ifdef CONFIG_HID_PICOLCD_LCD
+int picolcd_init_lcd(struct picolcd_data *data,
+               struct hid_report *report);
+
+void picolcd_exit_lcd(struct picolcd_data *data);
+
+int picolcd_resume_lcd(struct picolcd_data *data);
+#else
+static inline int picolcd_init_lcd(struct picolcd_data *data,
+               struct hid_report *report)
+{
+       return 0;
+}
+static inline void picolcd_exit_lcd(struct picolcd_data *data)
+{
+}
+static inline int picolcd_resume_lcd(struct picolcd_data *data)
+{
+       return 0;
+}
+#endif /* CONFIG_HID_PICOLCD_LCD */
+
+
+#ifdef CONFIG_HID_PICOLCD_LEDS
+int picolcd_init_leds(struct picolcd_data *data,
+               struct hid_report *report);
+
+void picolcd_exit_leds(struct picolcd_data *data);
+
+void picolcd_leds_set(struct picolcd_data *data);
+#else
+static inline int picolcd_init_leds(struct picolcd_data *data,
+               struct hid_report *report)
+{
+       return 0;
+}
+static inline void picolcd_exit_leds(struct picolcd_data *data)
+{
+}
+static inline void picolcd_leds_set(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LEDS */
+
+
+#ifdef CONFIG_HID_PICOLCD_CIR
+int picolcd_raw_cir(struct picolcd_data *data,
+               struct hid_report *report, u8 *raw_data, int size);
+
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report);
+
+void picolcd_exit_cir(struct picolcd_data *data);
+#else
+static inline int picolcd_raw_cir(struct picolcd_data *data,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       return 1;
+}
+static inline int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+       return 0;
+}
+static inline void picolcd_exit_cir(struct picolcd_data *data)
+{
+}
+#endif /* CONFIG_HID_PICOLCD_LIRC */
+
+int picolcd_reset(struct hid_device *hdev);
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+                       int report_id, const u8 *raw_data, int size);
diff --git a/drivers/hid/hid-picolcd_backlight.c b/drivers/hid/hid-picolcd_backlight.c
new file mode 100644 (file)
index 0000000..b91f309
--- /dev/null
@@ -0,0 +1,122 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/backlight.h>
+
+#include "hid-picolcd.h"
+
+static int picolcd_get_brightness(struct backlight_device *bdev)
+{
+       struct picolcd_data *data = bl_get_data(bdev);
+       return data->lcd_brightness;
+}
+
+static int picolcd_set_brightness(struct backlight_device *bdev)
+{
+       struct picolcd_data *data = bl_get_data(bdev);
+       struct hid_report *report = picolcd_out_report(REPORT_BRIGHTNESS, data->hdev);
+       unsigned long flags;
+
+       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+               return -ENODEV;
+
+       data->lcd_brightness = bdev->props.brightness & 0x0ff;
+       data->lcd_power      = bdev->props.power;
+       spin_lock_irqsave(&data->lock, flags);
+       hid_set_field(report->field[0], 0, data->lcd_power == FB_BLANK_UNBLANK ? data->lcd_brightness : 0);
+       if (!(data->status & PICOLCD_FAILED))
+               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+       return 0;
+}
+
+static int picolcd_check_bl_fb(struct backlight_device *bdev, struct fb_info *fb)
+{
+       return fb && fb == picolcd_fbinfo((struct picolcd_data *)bl_get_data(bdev));
+}
+
+static const struct backlight_ops picolcd_blops = {
+       .update_status  = picolcd_set_brightness,
+       .get_brightness = picolcd_get_brightness,
+       .check_fb       = picolcd_check_bl_fb,
+};
+
+int picolcd_init_backlight(struct picolcd_data *data, struct hid_report *report)
+{
+       struct device *dev = &data->hdev->dev;
+       struct backlight_device *bdev;
+       struct backlight_properties props;
+       if (!report)
+               return -ENODEV;
+       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+                       report->field[0]->report_size != 8) {
+               dev_err(dev, "unsupported BRIGHTNESS report");
+               return -EINVAL;
+       }
+
+       memset(&props, 0, sizeof(props));
+       props.type = BACKLIGHT_RAW;
+       props.max_brightness = 0xff;
+       bdev = backlight_device_register(dev_name(dev), dev, data,
+                       &picolcd_blops, &props);
+       if (IS_ERR(bdev)) {
+               dev_err(dev, "failed to register backlight\n");
+               return PTR_ERR(bdev);
+       }
+       bdev->props.brightness     = 0xff;
+       data->lcd_brightness       = 0xff;
+       data->backlight            = bdev;
+       picolcd_set_brightness(bdev);
+       return 0;
+}
+
+void picolcd_exit_backlight(struct picolcd_data *data)
+{
+       struct backlight_device *bdev = data->backlight;
+
+       data->backlight = NULL;
+       if (bdev)
+               backlight_device_unregister(bdev);
+}
+
+int picolcd_resume_backlight(struct picolcd_data *data)
+{
+       if (!data->backlight)
+               return 0;
+       return picolcd_set_brightness(data->backlight);
+}
+
+#ifdef CONFIG_PM
+void picolcd_suspend_backlight(struct picolcd_data *data)
+{
+       int bl_power = data->lcd_power;
+       if (!data->backlight)
+               return;
+
+       data->backlight->props.power = FB_BLANK_POWERDOWN;
+       picolcd_set_brightness(data->backlight);
+       data->lcd_power = data->backlight->props.power = bl_power;
+}
+#endif /* CONFIG_PM */
+
diff --git a/drivers/hid/hid-picolcd_cir.c b/drivers/hid/hid-picolcd_cir.c
new file mode 100644 (file)
index 0000000..13ca919
--- /dev/null
@@ -0,0 +1,152 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <media/rc-core.h>
+
+#include "hid-picolcd.h"
+
+
+int picolcd_raw_cir(struct picolcd_data *data,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       unsigned long flags;
+       int i, w, sz;
+       DEFINE_IR_RAW_EVENT(rawir);
+
+       /* ignore if rc_dev is NULL or status is shunned */
+       spin_lock_irqsave(&data->lock, flags);
+       if (!data->rc_dev || (data->status & PICOLCD_CIR_SHUN)) {
+               spin_unlock_irqrestore(&data->lock, flags);
+               return 1;
+       }
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       /* PicoLCD USB packets contain 16-bit intervals in network order,
+        * with value negated for pulse. Intervals are in microseconds.
+        *
+        * Note: some userspace LIRC code for PicoLCD says negated values
+        * for space - is it a matter of IR chip? (pulse for my TSOP2236)
+        *
+        * In addition, the first interval seems to be around 15000 + base
+        * interval for non-first report of IR data - thus the quirk below
+        * to get RC_CODE to understand Sony and JVC remotes I have at hand
+        */
+       sz = size > 0 ? min((int)raw_data[0], size-1) : 0;
+       for (i = 0; i+1 < sz; i += 2) {
+               init_ir_raw_event(&rawir);
+               w = (raw_data[i] << 8) | (raw_data[i+1]);
+               rawir.pulse = !!(w & 0x8000);
+               rawir.duration = US_TO_NS(rawir.pulse ? (65536 - w) : w);
+               /* Quirk!! - see above */
+               if (i == 0 && rawir.duration > 15000000)
+                       rawir.duration -= 15000000;
+               ir_raw_event_store(data->rc_dev, &rawir);
+       }
+       ir_raw_event_handle(data->rc_dev);
+
+       return 1;
+}
+
+static int picolcd_cir_open(struct rc_dev *dev)
+{
+       struct picolcd_data *data = dev->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&data->lock, flags);
+       data->status &= ~PICOLCD_CIR_SHUN;
+       spin_unlock_irqrestore(&data->lock, flags);
+       return 0;
+}
+
+static void picolcd_cir_close(struct rc_dev *dev)
+{
+       struct picolcd_data *data = dev->priv;
+       unsigned long flags;
+
+       spin_lock_irqsave(&data->lock, flags);
+       data->status |= PICOLCD_CIR_SHUN;
+       spin_unlock_irqrestore(&data->lock, flags);
+}
+
+/* initialize CIR input device */
+int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
+{
+       struct rc_dev *rdev;
+       int ret = 0;
+
+       rdev = rc_allocate_device();
+       if (!rdev)
+               return -ENOMEM;
+
+       rdev->priv             = data;
+       rdev->driver_type      = RC_DRIVER_IR_RAW;
+       rdev->allowed_protos   = RC_TYPE_ALL;
+       rdev->open             = picolcd_cir_open;
+       rdev->close            = picolcd_cir_close;
+       rdev->input_name       = data->hdev->name;
+       rdev->input_phys       = data->hdev->phys;
+       rdev->input_id.bustype = data->hdev->bus;
+       rdev->input_id.vendor  = data->hdev->vendor;
+       rdev->input_id.product = data->hdev->product;
+       rdev->input_id.version = data->hdev->version;
+       rdev->dev.parent       = &data->hdev->dev;
+       rdev->driver_name      = PICOLCD_NAME;
+       rdev->map_name         = RC_MAP_RC6_MCE;
+       rdev->timeout          = MS_TO_NS(100);
+       rdev->rx_resolution    = US_TO_NS(1);
+
+       ret = rc_register_device(rdev);
+       if (ret)
+               goto err;
+       data->rc_dev = rdev;
+       return 0;
+
+err:
+       rc_free_device(rdev);
+       return ret;
+}
+
+void picolcd_exit_cir(struct picolcd_data *data)
+{
+       struct rc_dev *rdev = data->rc_dev;
+
+       data->rc_dev = NULL;
+       rc_unregister_device(rdev);
+}
+
diff --git a/drivers/hid/hid-picolcd_core.c b/drivers/hid/hid-picolcd_core.c
new file mode 100644 (file)
index 0000000..86df26e
--- /dev/null
@@ -0,0 +1,689 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+/* Input device
+ *
+ * The PicoLCD has an IR receiver header, a built-in keypad with 5 keys
+ * and header for 4x4 key matrix. The built-in keys are part of the matrix.
+ */
+static const unsigned short def_keymap[PICOLCD_KEYS] = {
+       KEY_RESERVED,   /* none */
+       KEY_BACK,       /* col 4 + row 1 */
+       KEY_HOMEPAGE,   /* col 3 + row 1 */
+       KEY_RESERVED,   /* col 2 + row 1 */
+       KEY_RESERVED,   /* col 1 + row 1 */
+       KEY_SCROLLUP,   /* col 4 + row 2 */
+       KEY_OK,         /* col 3 + row 2 */
+       KEY_SCROLLDOWN, /* col 2 + row 2 */
+       KEY_RESERVED,   /* col 1 + row 2 */
+       KEY_RESERVED,   /* col 4 + row 3 */
+       KEY_RESERVED,   /* col 3 + row 3 */
+       KEY_RESERVED,   /* col 2 + row 3 */
+       KEY_RESERVED,   /* col 1 + row 3 */
+       KEY_RESERVED,   /* col 4 + row 4 */
+       KEY_RESERVED,   /* col 3 + row 4 */
+       KEY_RESERVED,   /* col 2 + row 4 */
+       KEY_RESERVED,   /* col 1 + row 4 */
+};
+
+
+/* Find a given report */
+struct hid_report *picolcd_report(int id, struct hid_device *hdev, int dir)
+{
+       struct list_head *feature_report_list = &hdev->report_enum[dir].report_list;
+       struct hid_report *report = NULL;
+
+       list_for_each_entry(report, feature_report_list, list) {
+               if (report->id == id)
+                       return report;
+       }
+       hid_warn(hdev, "No report with id 0x%x found\n", id);
+       return NULL;
+}
+
+/* Submit a report and wait for a reply from device - if device fades away
+ * or does not respond in time, return NULL */
+struct picolcd_pending *picolcd_send_and_wait(struct hid_device *hdev,
+               int report_id, const u8 *raw_data, int size)
+{
+       struct picolcd_data *data = hid_get_drvdata(hdev);
+       struct picolcd_pending *work;
+       struct hid_report *report = picolcd_out_report(report_id, hdev);
+       unsigned long flags;
+       int i, j, k;
+
+       if (!report || !data)
+               return NULL;
+       if (data->status & PICOLCD_FAILED)
+               return NULL;
+       work = kzalloc(sizeof(*work), GFP_KERNEL);
+       if (!work)
+               return NULL;
+
+       init_completion(&work->ready);
+       work->out_report = report;
+       work->in_report  = NULL;
+       work->raw_size   = 0;
+
+       mutex_lock(&data->mutex);
+       spin_lock_irqsave(&data->lock, flags);
+       for (i = k = 0; i < report->maxfield; i++)
+               for (j = 0; j < report->field[i]->report_count; j++) {
+                       hid_set_field(report->field[i], j, k < size ? raw_data[k] : 0);
+                       k++;
+               }
+       if (data->status & PICOLCD_FAILED) {
+               kfree(work);
+               work = NULL;
+       } else {
+               data->pending = work;
+               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+               spin_unlock_irqrestore(&data->lock, flags);
+               wait_for_completion_interruptible_timeout(&work->ready, HZ*2);
+               spin_lock_irqsave(&data->lock, flags);
+               data->pending = NULL;
+       }
+       spin_unlock_irqrestore(&data->lock, flags);
+       mutex_unlock(&data->mutex);
+       return work;
+}
+
+/*
+ * input class device
+ */
+static int picolcd_raw_keypad(struct picolcd_data *data,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       /*
+        * Keypad event
+        * First and second data bytes list currently pressed keys,
+        * 0x00 means no key and at most 2 keys may be pressed at same time
+        */
+       int i, j;
+
+       /* determine newly pressed keys */
+       for (i = 0; i < size; i++) {
+               unsigned int key_code;
+               if (raw_data[i] == 0)
+                       continue;
+               for (j = 0; j < sizeof(data->pressed_keys); j++)
+                       if (data->pressed_keys[j] == raw_data[i])
+                               goto key_already_down;
+               for (j = 0; j < sizeof(data->pressed_keys); j++)
+                       if (data->pressed_keys[j] == 0) {
+                               data->pressed_keys[j] = raw_data[i];
+                               break;
+                       }
+               input_event(data->input_keys, EV_MSC, MSC_SCAN, raw_data[i]);
+               if (raw_data[i] < PICOLCD_KEYS)
+                       key_code = data->keycode[raw_data[i]];
+               else
+                       key_code = KEY_UNKNOWN;
+               if (key_code != KEY_UNKNOWN) {
+                       dbg_hid(PICOLCD_NAME " got key press for %u:%d",
+                                       raw_data[i], key_code);
+                       input_report_key(data->input_keys, key_code, 1);
+               }
+               input_sync(data->input_keys);
+key_already_down:
+               continue;
+       }
+
+       /* determine newly released keys */
+       for (j = 0; j < sizeof(data->pressed_keys); j++) {
+               unsigned int key_code;
+               if (data->pressed_keys[j] == 0)
+                       continue;
+               for (i = 0; i < size; i++)
+                       if (data->pressed_keys[j] == raw_data[i])
+                               goto key_still_down;
+               input_event(data->input_keys, EV_MSC, MSC_SCAN, data->pressed_keys[j]);
+               if (data->pressed_keys[j] < PICOLCD_KEYS)
+                       key_code = data->keycode[data->pressed_keys[j]];
+               else
+                       key_code = KEY_UNKNOWN;
+               if (key_code != KEY_UNKNOWN) {
+                       dbg_hid(PICOLCD_NAME " got key release for %u:%d",
+                                       data->pressed_keys[j], key_code);
+                       input_report_key(data->input_keys, key_code, 0);
+               }
+               input_sync(data->input_keys);
+               data->pressed_keys[j] = 0;
+key_still_down:
+               continue;
+       }
+       return 1;
+}
+
+static int picolcd_check_version(struct hid_device *hdev)
+{
+       struct picolcd_data *data = hid_get_drvdata(hdev);
+       struct picolcd_pending *verinfo;
+       int ret = 0;
+
+       if (!data)
+               return -ENODEV;
+
+       verinfo = picolcd_send_and_wait(hdev, REPORT_VERSION, NULL, 0);
+       if (!verinfo) {
+               hid_err(hdev, "no version response from PicoLCD\n");
+               return -ENODEV;
+       }
+
+       if (verinfo->raw_size == 2) {
+               data->version[0] = verinfo->raw_data[1];
+               data->version[1] = verinfo->raw_data[0];
+               if (data->status & PICOLCD_BOOTLOADER) {
+                       hid_info(hdev, "PicoLCD, bootloader version %d.%d\n",
+                                verinfo->raw_data[1], verinfo->raw_data[0]);
+               } else {
+                       hid_info(hdev, "PicoLCD, firmware version %d.%d\n",
+                                verinfo->raw_data[1], verinfo->raw_data[0]);
+               }
+       } else {
+               hid_err(hdev, "confused, got unexpected version response from PicoLCD\n");
+               ret = -EINVAL;
+       }
+       kfree(verinfo);
+       return ret;
+}
+
+/*
+ * Reset our device and wait for answer to VERSION request
+ */
+int picolcd_reset(struct hid_device *hdev)
+{
+       struct picolcd_data *data = hid_get_drvdata(hdev);
+       struct hid_report *report = picolcd_out_report(REPORT_RESET, hdev);
+       unsigned long flags;
+       int error;
+
+       if (!data || !report || report->maxfield != 1)
+               return -ENODEV;
+
+       spin_lock_irqsave(&data->lock, flags);
+       if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+               data->status |= PICOLCD_BOOTLOADER;
+
+       /* perform the reset */
+       hid_set_field(report->field[0], 0, 1);
+       if (data->status & PICOLCD_FAILED) {
+               spin_unlock_irqrestore(&data->lock, flags);
+               return -ENODEV;
+       }
+       usbhid_submit_report(hdev, report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       error = picolcd_check_version(hdev);
+       if (error)
+               return error;
+
+       picolcd_resume_lcd(data);
+       picolcd_resume_backlight(data);
+       picolcd_fb_refresh(data);
+       picolcd_leds_set(data);
+       return 0;
+}
+
+/*
+ * The "operation_mode" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+
+       if (data->status & PICOLCD_BOOTLOADER)
+               return snprintf(buf, PAGE_SIZE, "[bootloader] lcd\n");
+       else
+               return snprintf(buf, PAGE_SIZE, "bootloader [lcd]\n");
+}
+
+static ssize_t picolcd_operation_mode_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+       struct hid_report *report = NULL;
+       size_t cnt = count;
+       int timeout = data->opmode_delay;
+       unsigned long flags;
+
+       if (cnt >= 3 && strncmp("lcd", buf, 3) == 0) {
+               if (data->status & PICOLCD_BOOTLOADER)
+                       report = picolcd_out_report(REPORT_EXIT_FLASHER, data->hdev);
+               buf += 3;
+               cnt -= 3;
+       } else if (cnt >= 10 && strncmp("bootloader", buf, 10) == 0) {
+               if (!(data->status & PICOLCD_BOOTLOADER))
+                       report = picolcd_out_report(REPORT_EXIT_KEYBOARD, data->hdev);
+               buf += 10;
+               cnt -= 10;
+       }
+       if (!report)
+               return -EINVAL;
+
+       while (cnt > 0 && (buf[cnt-1] == '\n' || buf[cnt-1] == '\r'))
+               cnt--;
+       if (cnt != 0)
+               return -EINVAL;
+
+       spin_lock_irqsave(&data->lock, flags);
+       hid_set_field(report->field[0], 0, timeout & 0xff);
+       hid_set_field(report->field[0], 1, (timeout >> 8) & 0xff);
+       usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+       return count;
+}
+
+static DEVICE_ATTR(operation_mode, 0644, picolcd_operation_mode_show,
+               picolcd_operation_mode_store);
+
+/*
+ * The "operation_mode_delay" sysfs attribute
+ */
+static ssize_t picolcd_operation_mode_delay_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%hu\n", data->opmode_delay);
+}
+
+static ssize_t picolcd_operation_mode_delay_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+       unsigned u;
+       if (sscanf(buf, "%u", &u) != 1)
+               return -EINVAL;
+       if (u > 30000)
+               return -EINVAL;
+       else
+               data->opmode_delay = u;
+       return count;
+}
+
+static DEVICE_ATTR(operation_mode_delay, 0644, picolcd_operation_mode_delay_show,
+               picolcd_operation_mode_delay_store);
+
+/*
+ * Handle raw report as sent by device
+ */
+static int picolcd_raw_event(struct hid_device *hdev,
+               struct hid_report *report, u8 *raw_data, int size)
+{
+       struct picolcd_data *data = hid_get_drvdata(hdev);
+       unsigned long flags;
+       int ret = 0;
+
+       if (!data)
+               return 1;
+
+       if (report->id == REPORT_KEY_STATE) {
+               if (data->input_keys)
+                       ret = picolcd_raw_keypad(data, report, raw_data+1, size-1);
+       } else if (report->id == REPORT_IR_DATA) {
+               ret = picolcd_raw_cir(data, report, raw_data+1, size-1);
+       } else {
+               spin_lock_irqsave(&data->lock, flags);
+               /*
+                * We let the caller of picolcd_send_and_wait() check if the
+                * report we got is one of the expected ones or not.
+                */
+               if (data->pending) {
+                       memcpy(data->pending->raw_data, raw_data+1, size-1);
+                       data->pending->raw_size  = size-1;
+                       data->pending->in_report = report;
+                       complete(&data->pending->ready);
+               }
+               spin_unlock_irqrestore(&data->lock, flags);
+       }
+
+       picolcd_debug_raw_event(data, hdev, report, raw_data, size);
+       return 1;
+}
+
+#ifdef CONFIG_PM
+static int picolcd_suspend(struct hid_device *hdev, pm_message_t message)
+{
+       if (PMSG_IS_AUTO(message))
+               return 0;
+
+       picolcd_suspend_backlight(hid_get_drvdata(hdev));
+       dbg_hid(PICOLCD_NAME " device ready for suspend\n");
+       return 0;
+}
+
+static int picolcd_resume(struct hid_device *hdev)
+{
+       int ret;
+       ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+       if (ret)
+               dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+       return 0;
+}
+
+static int picolcd_reset_resume(struct hid_device *hdev)
+{
+       int ret;
+       ret = picolcd_reset(hdev);
+       if (ret)
+               dbg_hid(PICOLCD_NAME " resetting our device failed: %d\n", ret);
+       ret = picolcd_fb_reset(hid_get_drvdata(hdev), 0);
+       if (ret)
+               dbg_hid(PICOLCD_NAME " restoring framebuffer content failed: %d\n", ret);
+       ret = picolcd_resume_lcd(hid_get_drvdata(hdev));
+       if (ret)
+               dbg_hid(PICOLCD_NAME " restoring lcd failed: %d\n", ret);
+       ret = picolcd_resume_backlight(hid_get_drvdata(hdev));
+       if (ret)
+               dbg_hid(PICOLCD_NAME " restoring backlight failed: %d\n", ret);
+       picolcd_leds_set(hid_get_drvdata(hdev));
+       return 0;
+}
+#endif
+
+/* initialize keypad input device */
+static int picolcd_init_keys(struct picolcd_data *data,
+               struct hid_report *report)
+{
+       struct hid_device *hdev = data->hdev;
+       struct input_dev *idev;
+       int error, i;
+
+       if (!report)
+               return -ENODEV;
+       if (report->maxfield != 1 || report->field[0]->report_count != 2 ||
+                       report->field[0]->report_size != 8) {
+               hid_err(hdev, "unsupported KEY_STATE report\n");
+               return -EINVAL;
+       }
+
+       idev = input_allocate_device();
+       if (idev == NULL) {
+               hid_err(hdev, "failed to allocate input device\n");
+               return -ENOMEM;
+       }
+       input_set_drvdata(idev, hdev);
+       memcpy(data->keycode, def_keymap, sizeof(def_keymap));
+       idev->name = hdev->name;
+       idev->phys = hdev->phys;
+       idev->uniq = hdev->uniq;
+       idev->id.bustype = hdev->bus;
+       idev->id.vendor  = hdev->vendor;
+       idev->id.product = hdev->product;
+       idev->id.version = hdev->version;
+       idev->dev.parent = &hdev->dev;
+       idev->keycode     = &data->keycode;
+       idev->keycodemax  = PICOLCD_KEYS;
+       idev->keycodesize = sizeof(data->keycode[0]);
+       input_set_capability(idev, EV_MSC, MSC_SCAN);
+       set_bit(EV_REP, idev->evbit);
+       for (i = 0; i < PICOLCD_KEYS; i++)
+               input_set_capability(idev, EV_KEY, data->keycode[i]);
+       error = input_register_device(idev);
+       if (error) {
+               hid_err(hdev, "error registering the input device\n");
+               input_free_device(idev);
+               return error;
+       }
+       data->input_keys = idev;
+       return 0;
+}
+
+static void picolcd_exit_keys(struct picolcd_data *data)
+{
+       struct input_dev *idev = data->input_keys;
+
+       data->input_keys = NULL;
+       if (idev)
+               input_unregister_device(idev);
+}
+
+static int picolcd_probe_lcd(struct hid_device *hdev, struct picolcd_data *data)
+{
+       int error;
+
+       /* Setup keypad input device */
+       error = picolcd_init_keys(data, picolcd_in_report(REPORT_KEY_STATE, hdev));
+       if (error)
+               goto err;
+
+       /* Setup CIR input device */
+       error = picolcd_init_cir(data, picolcd_in_report(REPORT_IR_DATA, hdev));
+       if (error)
+               goto err;
+
+       /* Set up the framebuffer device */
+       error = picolcd_init_framebuffer(data);
+       if (error)
+               goto err;
+
+       /* Setup lcd class device */
+       error = picolcd_init_lcd(data, picolcd_out_report(REPORT_CONTRAST, hdev));
+       if (error)
+               goto err;
+
+       /* Setup backlight class device */
+       error = picolcd_init_backlight(data, picolcd_out_report(REPORT_BRIGHTNESS, hdev));
+       if (error)
+               goto err;
+
+       /* Setup the LED class devices */
+       error = picolcd_init_leds(data, picolcd_out_report(REPORT_LED_STATE, hdev));
+       if (error)
+               goto err;
+
+       picolcd_init_devfs(data, picolcd_out_report(REPORT_EE_READ, hdev),
+                       picolcd_out_report(REPORT_EE_WRITE, hdev),
+                       picolcd_out_report(REPORT_READ_MEMORY, hdev),
+                       picolcd_out_report(REPORT_WRITE_MEMORY, hdev),
+                       picolcd_out_report(REPORT_RESET, hdev));
+       return 0;
+err:
+       picolcd_exit_leds(data);
+       picolcd_exit_backlight(data);
+       picolcd_exit_lcd(data);
+       picolcd_exit_framebuffer(data);
+       picolcd_exit_cir(data);
+       picolcd_exit_keys(data);
+       return error;
+}
+
+static int picolcd_probe_bootloader(struct hid_device *hdev, struct picolcd_data *data)
+{
+       picolcd_init_devfs(data, NULL, NULL,
+                       picolcd_out_report(REPORT_BL_READ_MEMORY, hdev),
+                       picolcd_out_report(REPORT_BL_WRITE_MEMORY, hdev), NULL);
+       return 0;
+}
+
+static int picolcd_probe(struct hid_device *hdev,
+                    const struct hid_device_id *id)
+{
+       struct picolcd_data *data;
+       int error = -ENOMEM;
+
+       dbg_hid(PICOLCD_NAME " hardware probe...\n");
+
+       /*
+        * Let's allocate the picolcd data structure, set some reasonable
+        * defaults, and associate it with the device
+        */
+       data = kzalloc(sizeof(struct picolcd_data), GFP_KERNEL);
+       if (data == NULL) {
+               hid_err(hdev, "can't allocate space for Minibox PicoLCD device data\n");
+               error = -ENOMEM;
+               goto err_no_cleanup;
+       }
+
+       spin_lock_init(&data->lock);
+       mutex_init(&data->mutex);
+       data->hdev = hdev;
+       data->opmode_delay = 5000;
+       if (hdev->product == USB_DEVICE_ID_PICOLCD_BOOTLOADER)
+               data->status |= PICOLCD_BOOTLOADER;
+       hid_set_drvdata(hdev, data);
+
+       /* Parse the device reports and start it up */
+       error = hid_parse(hdev);
+       if (error) {
+               hid_err(hdev, "device report parse failed\n");
+               goto err_cleanup_data;
+       }
+
+       error = hid_hw_start(hdev, 0);
+       if (error) {
+               hid_err(hdev, "hardware start failed\n");
+               goto err_cleanup_data;
+       }
+
+       error = hid_hw_open(hdev);
+       if (error) {
+               hid_err(hdev, "failed to open input interrupt pipe for key and IR events\n");
+               goto err_cleanup_hid_hw;
+       }
+
+       error = device_create_file(&hdev->dev, &dev_attr_operation_mode_delay);
+       if (error) {
+               hid_err(hdev, "failed to create sysfs attributes\n");
+               goto err_cleanup_hid_ll;
+       }
+
+       error = device_create_file(&hdev->dev, &dev_attr_operation_mode);
+       if (error) {
+               hid_err(hdev, "failed to create sysfs attributes\n");
+               goto err_cleanup_sysfs1;
+       }
+
+       if (data->status & PICOLCD_BOOTLOADER)
+               error = picolcd_probe_bootloader(hdev, data);
+       else
+               error = picolcd_probe_lcd(hdev, data);
+       if (error)
+               goto err_cleanup_sysfs2;
+
+       dbg_hid(PICOLCD_NAME " activated and initialized\n");
+       return 0;
+
+err_cleanup_sysfs2:
+       device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+err_cleanup_sysfs1:
+       device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+err_cleanup_hid_ll:
+       hid_hw_close(hdev);
+err_cleanup_hid_hw:
+       hid_hw_stop(hdev);
+err_cleanup_data:
+       kfree(data);
+err_no_cleanup:
+       hid_set_drvdata(hdev, NULL);
+
+       return error;
+}
+
+static void picolcd_remove(struct hid_device *hdev)
+{
+       struct picolcd_data *data = hid_get_drvdata(hdev);
+       unsigned long flags;
+
+       dbg_hid(PICOLCD_NAME " hardware remove...\n");
+       spin_lock_irqsave(&data->lock, flags);
+       data->status |= PICOLCD_FAILED;
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       picolcd_exit_devfs(data);
+       device_remove_file(&hdev->dev, &dev_attr_operation_mode);
+       device_remove_file(&hdev->dev, &dev_attr_operation_mode_delay);
+       hid_hw_close(hdev);
+       hid_hw_stop(hdev);
+
+       /* Shortcut potential pending reply that will never arrive */
+       spin_lock_irqsave(&data->lock, flags);
+       if (data->pending)
+               complete(&data->pending->ready);
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       /* Cleanup LED */
+       picolcd_exit_leds(data);
+       /* Clean up the framebuffer */
+       picolcd_exit_backlight(data);
+       picolcd_exit_lcd(data);
+       picolcd_exit_framebuffer(data);
+       /* Cleanup input */
+       picolcd_exit_cir(data);
+       picolcd_exit_keys(data);
+
+       hid_set_drvdata(hdev, NULL);
+       mutex_destroy(&data->mutex);
+       /* Finally, clean up the picolcd data itself */
+       kfree(data);
+}
+
+static const struct hid_device_id picolcd_devices[] = {
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICOLCD_BOOTLOADER) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, picolcd_devices);
+
+static struct hid_driver picolcd_driver = {
+       .name =          "hid-picolcd",
+       .id_table =      picolcd_devices,
+       .probe =         picolcd_probe,
+       .remove =        picolcd_remove,
+       .raw_event =     picolcd_raw_event,
+#ifdef CONFIG_PM
+       .suspend =       picolcd_suspend,
+       .resume =        picolcd_resume,
+       .reset_resume =  picolcd_reset_resume,
+#endif
+};
+
+static int __init picolcd_init(void)
+{
+       return hid_register_driver(&picolcd_driver);
+}
+
+static void __exit picolcd_exit(void)
+{
+       hid_unregister_driver(&picolcd_driver);
+}
+
+module_init(picolcd_init);
+module_exit(picolcd_exit);
+MODULE_DESCRIPTION("Minibox graphics PicoLCD Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-picolcd_debugfs.c b/drivers/hid/hid-picolcd_debugfs.c
new file mode 100644 (file)
index 0000000..4809aa1
--- /dev/null
@@ -0,0 +1,899 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+
+#include "hid-picolcd.h"
+
+
+static int picolcd_debug_reset_show(struct seq_file *f, void *p)
+{
+       if (picolcd_fbinfo((struct picolcd_data *)f->private))
+               seq_printf(f, "all fb\n");
+       else
+               seq_printf(f, "all\n");
+       return 0;
+}
+
+static int picolcd_debug_reset_open(struct inode *inode, struct file *f)
+{
+       return single_open(f, picolcd_debug_reset_show, inode->i_private);
+}
+
+static ssize_t picolcd_debug_reset_write(struct file *f, const char __user *user_buf,
+               size_t count, loff_t *ppos)
+{
+       struct picolcd_data *data = ((struct seq_file *)f->private_data)->private;
+       char buf[32];
+       size_t cnt = min(count, sizeof(buf)-1);
+       if (copy_from_user(buf, user_buf, cnt))
+               return -EFAULT;
+
+       while (cnt > 0 && (buf[cnt-1] == ' ' || buf[cnt-1] == '\n'))
+               cnt--;
+       buf[cnt] = '\0';
+       if (strcmp(buf, "all") == 0) {
+               picolcd_reset(data->hdev);
+               picolcd_fb_reset(data, 1);
+       } else if (strcmp(buf, "fb") == 0) {
+               picolcd_fb_reset(data, 1);
+       } else {
+               return -EINVAL;
+       }
+       return count;
+}
+
+static const struct file_operations picolcd_debug_reset_fops = {
+       .owner    = THIS_MODULE,
+       .open     = picolcd_debug_reset_open,
+       .read     = seq_read,
+       .llseek   = seq_lseek,
+       .write    = picolcd_debug_reset_write,
+       .release  = single_release,
+};
+
+/*
+ * The "eeprom" file
+ */
+static ssize_t picolcd_debug_eeprom_read(struct file *f, char __user *u,
+               size_t s, loff_t *off)
+{
+       struct picolcd_data *data = f->private_data;
+       struct picolcd_pending *resp;
+       u8 raw_data[3];
+       ssize_t ret = -EIO;
+
+       if (s == 0)
+               return -EINVAL;
+       if (*off > 0x0ff)
+               return 0;
+
+       /* prepare buffer with info about what we want to read (addr & len) */
+       raw_data[0] = *off & 0xff;
+       raw_data[1] = (*off >> 8) & 0xff;
+       raw_data[2] = s < 20 ? s : 20;
+       if (*off + raw_data[2] > 0xff)
+               raw_data[2] = 0x100 - *off;
+       resp = picolcd_send_and_wait(data->hdev, REPORT_EE_READ, raw_data,
+                       sizeof(raw_data));
+       if (!resp)
+               return -EIO;
+
+       if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+               /* successful read :) */
+               ret = resp->raw_data[2];
+               if (ret > s)
+                       ret = s;
+               if (copy_to_user(u, resp->raw_data+3, ret))
+                       ret = -EFAULT;
+               else
+                       *off += ret;
+       } /* anything else is some kind of IO error */
+
+       kfree(resp);
+       return ret;
+}
+
+static ssize_t picolcd_debug_eeprom_write(struct file *f, const char __user *u,
+               size_t s, loff_t *off)
+{
+       struct picolcd_data *data = f->private_data;
+       struct picolcd_pending *resp;
+       ssize_t ret = -EIO;
+       u8 raw_data[23];
+
+       if (s == 0)
+               return -EINVAL;
+       if (*off > 0x0ff)
+               return -ENOSPC;
+
+       memset(raw_data, 0, sizeof(raw_data));
+       raw_data[0] = *off & 0xff;
+       raw_data[1] = (*off >> 8) & 0xff;
+       raw_data[2] = min_t(size_t, 20, s);
+       if (*off + raw_data[2] > 0xff)
+               raw_data[2] = 0x100 - *off;
+
+       if (copy_from_user(raw_data+3, u, min((u8)20, raw_data[2])))
+               return -EFAULT;
+       resp = picolcd_send_and_wait(data->hdev, REPORT_EE_WRITE, raw_data,
+                       sizeof(raw_data));
+
+       if (!resp)
+               return -EIO;
+
+       if (resp->in_report && resp->in_report->id == REPORT_EE_DATA) {
+               /* check if written data matches */
+               if (memcmp(raw_data, resp->raw_data, 3+raw_data[2]) == 0) {
+                       *off += raw_data[2];
+                       ret = raw_data[2];
+               }
+       }
+       kfree(resp);
+       return ret;
+}
+
+/*
+ * Notes:
+ * - read/write happens in chunks of at most 20 bytes, it's up to userspace
+ *   to loop in order to get more data.
+ * - on write errors on otherwise correct write request the bytes
+ *   that should have been written are in undefined state.
+ */
+static const struct file_operations picolcd_debug_eeprom_fops = {
+       .owner    = THIS_MODULE,
+       .open     = simple_open,
+       .read     = picolcd_debug_eeprom_read,
+       .write    = picolcd_debug_eeprom_write,
+       .llseek   = generic_file_llseek,
+};
+
+/*
+ * The "flash" file
+ */
+/* record a flash address to buf (bounds check to be done by caller) */
+static int _picolcd_flash_setaddr(struct picolcd_data *data, u8 *buf, long off)
+{
+       buf[0] = off & 0xff;
+       buf[1] = (off >> 8) & 0xff;
+       if (data->addr_sz == 3)
+               buf[2] = (off >> 16) & 0xff;
+       return data->addr_sz == 2 ? 2 : 3;
+}
+
+/* read a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_read(struct picolcd_data *data, int report_id,
+               char __user *u, size_t s, loff_t *off)
+{
+       struct picolcd_pending *resp;
+       u8 raw_data[4];
+       ssize_t ret = 0;
+       int len_off, err = -EIO;
+
+       while (s > 0) {
+               err = -EIO;
+               len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+               raw_data[len_off] = s > 32 ? 32 : s;
+               resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off+1);
+               if (!resp || !resp->in_report)
+                       goto skip;
+               if (resp->in_report->id == REPORT_MEMORY ||
+                       resp->in_report->id == REPORT_BL_READ_MEMORY) {
+                       if (memcmp(raw_data, resp->raw_data, len_off+1) != 0)
+                               goto skip;
+                       if (copy_to_user(u+ret, resp->raw_data+len_off+1, raw_data[len_off])) {
+                               err = -EFAULT;
+                               goto skip;
+                       }
+                       *off += raw_data[len_off];
+                       s    -= raw_data[len_off];
+                       ret  += raw_data[len_off];
+                       err   = 0;
+               }
+skip:
+               kfree(resp);
+               if (err)
+                       return ret > 0 ? ret : err;
+       }
+       return ret;
+}
+
+static ssize_t picolcd_debug_flash_read(struct file *f, char __user *u,
+               size_t s, loff_t *off)
+{
+       struct picolcd_data *data = f->private_data;
+
+       if (s == 0)
+               return -EINVAL;
+       if (*off > 0x05fff)
+               return 0;
+       if (*off + s > 0x05fff)
+               s = 0x06000 - *off;
+
+       if (data->status & PICOLCD_BOOTLOADER)
+               return _picolcd_flash_read(data, REPORT_BL_READ_MEMORY, u, s, off);
+       else
+               return _picolcd_flash_read(data, REPORT_READ_MEMORY, u, s, off);
+}
+
+/* erase block aligned to 64bytes boundary */
+static ssize_t _picolcd_flash_erase64(struct picolcd_data *data, int report_id,
+               loff_t *off)
+{
+       struct picolcd_pending *resp;
+       u8 raw_data[3];
+       int len_off;
+       ssize_t ret = -EIO;
+
+       if (*off & 0x3f)
+               return -EINVAL;
+
+       len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+       resp = picolcd_send_and_wait(data->hdev, report_id, raw_data, len_off);
+       if (!resp || !resp->in_report)
+               goto skip;
+       if (resp->in_report->id == REPORT_MEMORY ||
+               resp->in_report->id == REPORT_BL_ERASE_MEMORY) {
+               if (memcmp(raw_data, resp->raw_data, len_off) != 0)
+                       goto skip;
+               ret = 0;
+       }
+skip:
+       kfree(resp);
+       return ret;
+}
+
+/* write a given size of data (bounds check to be done by caller) */
+static ssize_t _picolcd_flash_write(struct picolcd_data *data, int report_id,
+               const char __user *u, size_t s, loff_t *off)
+{
+       struct picolcd_pending *resp;
+       u8 raw_data[36];
+       ssize_t ret = 0;
+       int len_off, err = -EIO;
+
+       while (s > 0) {
+               err = -EIO;
+               len_off = _picolcd_flash_setaddr(data, raw_data, *off);
+               raw_data[len_off] = s > 32 ? 32 : s;
+               if (copy_from_user(raw_data+len_off+1, u, raw_data[len_off])) {
+                       err = -EFAULT;
+                       break;
+               }
+               resp = picolcd_send_and_wait(data->hdev, report_id, raw_data,
+                               len_off+1+raw_data[len_off]);
+               if (!resp || !resp->in_report)
+                       goto skip;
+               if (resp->in_report->id == REPORT_MEMORY ||
+                       resp->in_report->id == REPORT_BL_WRITE_MEMORY) {
+                       if (memcmp(raw_data, resp->raw_data, len_off+1+raw_data[len_off]) != 0)
+                               goto skip;
+                       *off += raw_data[len_off];
+                       s    -= raw_data[len_off];
+                       ret  += raw_data[len_off];
+                       err   = 0;
+               }
+skip:
+               kfree(resp);
+               if (err)
+                       break;
+       }
+       return ret > 0 ? ret : err;
+}
+
+static ssize_t picolcd_debug_flash_write(struct file *f, const char __user *u,
+               size_t s, loff_t *off)
+{
+       struct picolcd_data *data = f->private_data;
+       ssize_t err, ret = 0;
+       int report_erase, report_write;
+
+       if (s == 0)
+               return -EINVAL;
+       if (*off > 0x5fff)
+               return -ENOSPC;
+       if (s & 0x3f)
+               return -EINVAL;
+       if (*off & 0x3f)
+               return -EINVAL;
+
+       if (data->status & PICOLCD_BOOTLOADER) {
+               report_erase = REPORT_BL_ERASE_MEMORY;
+               report_write = REPORT_BL_WRITE_MEMORY;
+       } else {
+               report_erase = REPORT_ERASE_MEMORY;
+               report_write = REPORT_WRITE_MEMORY;
+       }
+       mutex_lock(&data->mutex_flash);
+       while (s > 0) {
+               err = _picolcd_flash_erase64(data, report_erase, off);
+               if (err)
+                       break;
+               err = _picolcd_flash_write(data, report_write, u, 64, off);
+               if (err < 0)
+                       break;
+               ret += err;
+               *off += err;
+               s -= err;
+               if (err != 64)
+                       break;
+       }
+       mutex_unlock(&data->mutex_flash);
+       return ret > 0 ? ret : err;
+}
+
+/*
+ * Notes:
+ * - concurrent writing is prevented by mutex and all writes must be
+ *   n*64 bytes and 64-byte aligned, each write being preceded by an
+ *   ERASE which erases a 64byte block.
+ *   If less than requested was written or an error is returned for an
+ *   otherwise correct write request the next 64-byte block which should
+ *   have been written is in undefined state (mostly: original, erased,
+ *   (half-)written with write error)
+ * - reading can happen without special restriction
+ */
+static const struct file_operations picolcd_debug_flash_fops = {
+       .owner    = THIS_MODULE,
+       .open     = simple_open,
+       .read     = picolcd_debug_flash_read,
+       .write    = picolcd_debug_flash_write,
+       .llseek   = generic_file_llseek,
+};
+
+
+/*
+ * Helper code for HID report level dumping/debugging
+ */
+static const char * const error_codes[] = {
+       "success", "parameter missing", "data_missing", "block readonly",
+       "block not erasable", "block too big", "section overflow",
+       "invalid command length", "invalid data length",
+};
+
+static void dump_buff_as_hex(char *dst, size_t dst_sz, const u8 *data,
+               const size_t data_len)
+{
+       int i, j;
+       for (i = j = 0; i < data_len && j + 4 < dst_sz; i++) {
+               dst[j++] = hex_asc[(data[i] >> 4) & 0x0f];
+               dst[j++] = hex_asc[data[i] & 0x0f];
+               dst[j++] = ' ';
+       }
+       dst[j]   = '\0';
+       if (j > 0)
+               dst[j-1] = '\n';
+       if (i < data_len && j > 2)
+               dst[j-2] = dst[j-3] = '.';
+}
+
+void picolcd_debug_out_report(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report)
+{
+       u8 raw_data[70];
+       int raw_size = (report->size >> 3) + 1;
+       char *buff;
+#define BUFF_SZ 256
+
+       /* Avoid unnecessary overhead if debugfs is disabled */
+       if (list_empty(&hdev->debug_list))
+               return;
+
+       buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+       if (!buff)
+               return;
+
+       snprintf(buff, BUFF_SZ, "\nout report %d (size %d) =  ",
+                       report->id, raw_size);
+       hid_debug_event(hdev, buff);
+       if (raw_size + 5 > sizeof(raw_data)) {
+               kfree(buff);
+               hid_debug_event(hdev, " TOO BIG\n");
+               return;
+       } else {
+               raw_data[0] = report->id;
+               hid_output_report(report, raw_data);
+               dump_buff_as_hex(buff, BUFF_SZ, raw_data, raw_size);
+               hid_debug_event(hdev, buff);
+       }
+
+       switch (report->id) {
+       case REPORT_LED_STATE:
+               /* 1 data byte with GPO state */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_LED_STATE", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tGPO state: 0x%02x\n", raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_BRIGHTNESS:
+               /* 1 data byte with brightness */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_BRIGHTNESS", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tBrightness: 0x%02x\n", raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_CONTRAST:
+               /* 1 data byte with contrast */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_CONTRAST", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tContrast: 0x%02x\n", raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_RESET:
+               /* 2 data bytes with reset duration in ms */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_RESET", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tDuration: 0x%02x%02x (%dms)\n",
+                               raw_data[2], raw_data[1], raw_data[2] << 8 | raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_LCD_CMD:
+               /* 63 data bytes with LCD commands */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_LCD_CMD", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               /* TODO: format decoding */
+               break;
+       case REPORT_LCD_DATA:
+               /* 63 data bytes with LCD data */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_LCD_CMD", report->id, raw_size-1);
+               /* TODO: format decoding */
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_LCD_CMD_DATA:
+               /* 63 data bytes with LCD commands and data */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_LCD_CMD", report->id, raw_size-1);
+               /* TODO: format decoding */
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_EE_READ:
+               /* 3 data bytes with read area description */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_EE_READ", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_EE_WRITE:
+               /* 3+1..20 data bytes with write area description */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_EE_WRITE", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+               hid_debug_event(hdev, buff);
+               if (raw_data[3] == 0) {
+                       snprintf(buff, BUFF_SZ, "\tNo data\n");
+               } else if (raw_data[3] + 4 <= raw_size) {
+                       snprintf(buff, BUFF_SZ, "\tData: ");
+                       hid_debug_event(hdev, buff);
+                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+               } else {
+                       snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+               }
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_ERASE_MEMORY:
+       case REPORT_BL_ERASE_MEMORY:
+               /* 3 data bytes with pointer inside erase block */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_ERASE_MEMORY", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               switch (data->addr_sz) {
+               case 2:
+                       snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x\n",
+                                       raw_data[2], raw_data[1]);
+                       break;
+               case 3:
+                       snprintf(buff, BUFF_SZ, "\tAddress inside 64 byte block: 0x%02x%02x%02x\n",
+                                       raw_data[3], raw_data[2], raw_data[1]);
+                       break;
+               default:
+                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
+               }
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_READ_MEMORY:
+       case REPORT_BL_READ_MEMORY:
+               /* 4 data bytes with read area description */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_READ_MEMORY", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               switch (data->addr_sz) {
+               case 2:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                                       raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+                       break;
+               case 3:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+                                       raw_data[3], raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+                       break;
+               default:
+                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
+               }
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_WRITE_MEMORY:
+       case REPORT_BL_WRITE_MEMORY:
+               /* 4+1..32 data bytes with write adrea description */
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_WRITE_MEMORY", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               switch (data->addr_sz) {
+               case 2:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                                       raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+                       hid_debug_event(hdev, buff);
+                       if (raw_data[3] == 0) {
+                               snprintf(buff, BUFF_SZ, "\tNo data\n");
+                       } else if (raw_data[3] + 4 <= raw_size) {
+                               snprintf(buff, BUFF_SZ, "\tData: ");
+                               hid_debug_event(hdev, buff);
+                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+                       } else {
+                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+                       }
+                       break;
+               case 3:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+                                       raw_data[3], raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+                       hid_debug_event(hdev, buff);
+                       if (raw_data[4] == 0) {
+                               snprintf(buff, BUFF_SZ, "\tNo data\n");
+                       } else if (raw_data[4] + 5 <= raw_size) {
+                               snprintf(buff, BUFF_SZ, "\tData: ");
+                               hid_debug_event(hdev, buff);
+                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+                       } else {
+                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+                       }
+                       break;
+               default:
+                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
+               }
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_SPLASH_RESTART:
+               /* TODO */
+               break;
+       case REPORT_EXIT_KEYBOARD:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_EXIT_KEYBOARD", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+                               raw_data[1] | (raw_data[2] << 8),
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_VERSION:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_VERSION", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_DEVID:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_DEVID", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_SPLASH_SIZE:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_SPLASH_SIZE", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_HOOK_VERSION:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_HOOK_VERSION", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_EXIT_FLASHER:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "REPORT_VERSION", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tRestart delay: %dms (0x%02x%02x)\n",
+                               raw_data[1] | (raw_data[2] << 8),
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       default:
+               snprintf(buff, BUFF_SZ, "out report %s (%d, size=%d)\n",
+                       "<unknown>", report->id, raw_size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       }
+       wake_up_interruptible(&hdev->debug_wait);
+       kfree(buff);
+}
+
+void picolcd_debug_raw_event(struct picolcd_data *data,
+               struct hid_device *hdev, struct hid_report *report,
+               u8 *raw_data, int size)
+{
+       char *buff;
+
+#define BUFF_SZ 256
+       /* Avoid unnecessary overhead if debugfs is disabled */
+       if (list_empty(&hdev->debug_list))
+               return;
+
+       buff = kmalloc(BUFF_SZ, GFP_ATOMIC);
+       if (!buff)
+               return;
+
+       switch (report->id) {
+       case REPORT_ERROR_CODE:
+               /* 2 data bytes with affected report and error code */
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_ERROR_CODE", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               if (raw_data[2] < ARRAY_SIZE(error_codes))
+                       snprintf(buff, BUFF_SZ, "\tError code 0x%02x (%s) in reply to report 0x%02x\n",
+                                       raw_data[2], error_codes[raw_data[2]], raw_data[1]);
+               else
+                       snprintf(buff, BUFF_SZ, "\tError code 0x%02x in reply to report 0x%02x\n",
+                                       raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_KEY_STATE:
+               /* 2 data bytes with key state */
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_KEY_STATE", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               if (raw_data[1] == 0)
+                       snprintf(buff, BUFF_SZ, "\tNo key pressed\n");
+               else if (raw_data[2] == 0)
+                       snprintf(buff, BUFF_SZ, "\tOne key pressed: 0x%02x (%d)\n",
+                                       raw_data[1], raw_data[1]);
+               else
+                       snprintf(buff, BUFF_SZ, "\tTwo keys pressed: 0x%02x (%d), 0x%02x (%d)\n",
+                                       raw_data[1], raw_data[1], raw_data[2], raw_data[2]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_IR_DATA:
+               /* Up to 20 byes of IR scancode data */
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_IR_DATA", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               if (raw_data[1] == 0) {
+                       snprintf(buff, BUFF_SZ, "\tUnexpectedly 0 data length\n");
+                       hid_debug_event(hdev, buff);
+               } else if (raw_data[1] + 1 <= size) {
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n\tIR Data: ",
+                                       raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+2, raw_data[1]);
+                       hid_debug_event(hdev, buff);
+               } else {
+                       snprintf(buff, BUFF_SZ, "\tOverflowing data length: %d\n",
+                                       raw_data[1]-1);
+                       hid_debug_event(hdev, buff);
+               }
+               break;
+       case REPORT_EE_DATA:
+               /* Data buffer in response to REPORT_EE_READ or REPORT_EE_WRITE */
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_EE_DATA", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+               hid_debug_event(hdev, buff);
+               if (raw_data[3] == 0) {
+                       snprintf(buff, BUFF_SZ, "\tNo data\n");
+                       hid_debug_event(hdev, buff);
+               } else if (raw_data[3] + 4 <= size) {
+                       snprintf(buff, BUFF_SZ, "\tData: ");
+                       hid_debug_event(hdev, buff);
+                       dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+                       hid_debug_event(hdev, buff);
+               } else {
+                       snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+                       hid_debug_event(hdev, buff);
+               }
+               break;
+       case REPORT_MEMORY:
+               /* Data buffer in response to REPORT_READ_MEMORY or REPORT_WRTIE_MEMORY */
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_MEMORY", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               switch (data->addr_sz) {
+               case 2:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x\n",
+                                       raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[3]);
+                       hid_debug_event(hdev, buff);
+                       if (raw_data[3] == 0) {
+                               snprintf(buff, BUFF_SZ, "\tNo data\n");
+                       } else if (raw_data[3] + 4 <= size) {
+                               snprintf(buff, BUFF_SZ, "\tData: ");
+                               hid_debug_event(hdev, buff);
+                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+4, raw_data[3]);
+                       } else {
+                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+                       }
+                       break;
+               case 3:
+                       snprintf(buff, BUFF_SZ, "\tData address: 0x%02x%02x%02x\n",
+                                       raw_data[3], raw_data[2], raw_data[1]);
+                       hid_debug_event(hdev, buff);
+                       snprintf(buff, BUFF_SZ, "\tData length: %d\n", raw_data[4]);
+                       hid_debug_event(hdev, buff);
+                       if (raw_data[4] == 0) {
+                               snprintf(buff, BUFF_SZ, "\tNo data\n");
+                       } else if (raw_data[4] + 5 <= size) {
+                               snprintf(buff, BUFF_SZ, "\tData: ");
+                               hid_debug_event(hdev, buff);
+                               dump_buff_as_hex(buff, BUFF_SZ, raw_data+5, raw_data[4]);
+                       } else {
+                               snprintf(buff, BUFF_SZ, "\tData overflowed\n");
+                       }
+                       break;
+               default:
+                       snprintf(buff, BUFF_SZ, "\tNot supported\n");
+               }
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_VERSION:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_VERSION", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+                               raw_data[2], raw_data[1]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_BL_ERASE_MEMORY:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_BL_ERASE_MEMORY", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               /* TODO */
+               break;
+       case REPORT_BL_READ_MEMORY:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_BL_READ_MEMORY", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               /* TODO */
+               break;
+       case REPORT_BL_WRITE_MEMORY:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_BL_WRITE_MEMORY", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               /* TODO */
+               break;
+       case REPORT_DEVID:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_DEVID", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tSerial: 0x%02x%02x%02x%02x\n",
+                               raw_data[1], raw_data[2], raw_data[3], raw_data[4]);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tType: 0x%02x\n",
+                               raw_data[5]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_SPLASH_SIZE:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_SPLASH_SIZE", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tTotal splash space: %d\n",
+                               (raw_data[2] << 8) | raw_data[1]);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tUsed splash space: %d\n",
+                               (raw_data[4] << 8) | raw_data[3]);
+               hid_debug_event(hdev, buff);
+               break;
+       case REPORT_HOOK_VERSION:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "REPORT_HOOK_VERSION", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               snprintf(buff, BUFF_SZ, "\tFirmware version: %d.%d\n",
+                               raw_data[1], raw_data[2]);
+               hid_debug_event(hdev, buff);
+               break;
+       default:
+               snprintf(buff, BUFF_SZ, "report %s (%d, size=%d)\n",
+                       "<unknown>", report->id, size-1);
+               hid_debug_event(hdev, buff);
+               break;
+       }
+       wake_up_interruptible(&hdev->debug_wait);
+       kfree(buff);
+}
+
+void picolcd_init_devfs(struct picolcd_data *data,
+               struct hid_report *eeprom_r, struct hid_report *eeprom_w,
+               struct hid_report *flash_r, struct hid_report *flash_w,
+               struct hid_report *reset)
+{
+       struct hid_device *hdev = data->hdev;
+
+       mutex_init(&data->mutex_flash);
+
+       /* reset */
+       if (reset)
+               data->debug_reset = debugfs_create_file("reset", 0600,
+                               hdev->debug_dir, data, &picolcd_debug_reset_fops);
+
+       /* eeprom */
+       if (eeprom_r || eeprom_w)
+               data->debug_eeprom = debugfs_create_file("eeprom",
+                       (eeprom_w ? S_IWUSR : 0) | (eeprom_r ? S_IRUSR : 0),
+                       hdev->debug_dir, data, &picolcd_debug_eeprom_fops);
+
+       /* flash */
+       if (flash_r && flash_r->maxfield == 1 && flash_r->field[0]->report_size == 8)
+               data->addr_sz = flash_r->field[0]->report_count - 1;
+       else
+               data->addr_sz = -1;
+       if (data->addr_sz == 2 || data->addr_sz == 3) {
+               data->debug_flash = debugfs_create_file("flash",
+                       (flash_w ? S_IWUSR : 0) | (flash_r ? S_IRUSR : 0),
+                       hdev->debug_dir, data, &picolcd_debug_flash_fops);
+       } else if (flash_r || flash_w)
+               hid_warn(hdev, "Unexpected FLASH access reports, please submit rdesc for review\n");
+}
+
+void picolcd_exit_devfs(struct picolcd_data *data)
+{
+       struct dentry *dent;
+
+       dent = data->debug_reset;
+       data->debug_reset = NULL;
+       if (dent)
+               debugfs_remove(dent);
+       dent = data->debug_eeprom;
+       data->debug_eeprom = NULL;
+       if (dent)
+               debugfs_remove(dent);
+       dent = data->debug_flash;
+       data->debug_flash = NULL;
+       if (dent)
+               debugfs_remove(dent);
+       mutex_destroy(&data->mutex_flash);
+}
+
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
new file mode 100644 (file)
index 0000000..0008a51
--- /dev/null
@@ -0,0 +1,615 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/vmalloc.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+/* Framebuffer
+ *
+ * The PicoLCD use a Topway LCD module of 256x64 pixel
+ * This display area is tiled over 4 controllers with 8 tiles
+ * each. Each tile has 8x64 pixel, each data byte representing
+ * a 1-bit wide vertical line of the tile.
+ *
+ * The display can be updated at a tile granularity.
+ *
+ *       Chip 1           Chip 2           Chip 3           Chip 4
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 1     |     Tile 1     |     Tile 1     |     Tile 1     |
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 2     |     Tile 2     |     Tile 2     |     Tile 2     |
+ * +----------------+----------------+----------------+----------------+
+ *                                  ...
+ * +----------------+----------------+----------------+----------------+
+ * |     Tile 8     |     Tile 8     |     Tile 8     |     Tile 8     |
+ * +----------------+----------------+----------------+----------------+
+ */
+#define PICOLCDFB_NAME "picolcdfb"
+#define PICOLCDFB_WIDTH (256)
+#define PICOLCDFB_HEIGHT (64)
+#define PICOLCDFB_SIZE (PICOLCDFB_WIDTH * PICOLCDFB_HEIGHT / 8)
+
+#define PICOLCDFB_UPDATE_RATE_LIMIT   10
+#define PICOLCDFB_UPDATE_RATE_DEFAULT  2
+
+/* Framebuffer visual structures */
+static const struct fb_fix_screeninfo picolcdfb_fix = {
+       .id          = PICOLCDFB_NAME,
+       .type        = FB_TYPE_PACKED_PIXELS,
+       .visual      = FB_VISUAL_MONO01,
+       .xpanstep    = 0,
+       .ypanstep    = 0,
+       .ywrapstep   = 0,
+       .line_length = PICOLCDFB_WIDTH / 8,
+       .accel       = FB_ACCEL_NONE,
+};
+
+static const struct fb_var_screeninfo picolcdfb_var = {
+       .xres           = PICOLCDFB_WIDTH,
+       .yres           = PICOLCDFB_HEIGHT,
+       .xres_virtual   = PICOLCDFB_WIDTH,
+       .yres_virtual   = PICOLCDFB_HEIGHT,
+       .width          = 103,
+       .height         = 26,
+       .bits_per_pixel = 1,
+       .grayscale      = 1,
+       .red            = {
+               .offset = 0,
+               .length = 1,
+               .msb_right = 0,
+       },
+       .green          = {
+               .offset = 0,
+               .length = 1,
+               .msb_right = 0,
+       },
+       .blue           = {
+               .offset = 0,
+               .length = 1,
+               .msb_right = 0,
+       },
+       .transp         = {
+               .offset = 0,
+               .length = 0,
+               .msb_right = 0,
+       },
+};
+
+/* Send a given tile to PicoLCD */
+static int picolcd_fb_send_tile(struct picolcd_data *data, u8 *vbitmap,
+               int chip, int tile)
+{
+       struct hid_report *report1, *report2;
+       unsigned long flags;
+       u8 *tdata;
+       int i;
+
+       report1 = picolcd_out_report(REPORT_LCD_CMD_DATA, data->hdev);
+       if (!report1 || report1->maxfield != 1)
+               return -ENODEV;
+       report2 = picolcd_out_report(REPORT_LCD_DATA, data->hdev);
+       if (!report2 || report2->maxfield != 1)
+               return -ENODEV;
+
+       spin_lock_irqsave(&data->lock, flags);
+       if ((data->status & PICOLCD_FAILED)) {
+               spin_unlock_irqrestore(&data->lock, flags);
+               return -ENODEV;
+       }
+       hid_set_field(report1->field[0],  0, chip << 2);
+       hid_set_field(report1->field[0],  1, 0x02);
+       hid_set_field(report1->field[0],  2, 0x00);
+       hid_set_field(report1->field[0],  3, 0x00);
+       hid_set_field(report1->field[0],  4, 0xb8 | tile);
+       hid_set_field(report1->field[0],  5, 0x00);
+       hid_set_field(report1->field[0],  6, 0x00);
+       hid_set_field(report1->field[0],  7, 0x40);
+       hid_set_field(report1->field[0],  8, 0x00);
+       hid_set_field(report1->field[0],  9, 0x00);
+       hid_set_field(report1->field[0], 10,   32);
+
+       hid_set_field(report2->field[0],  0, (chip << 2) | 0x01);
+       hid_set_field(report2->field[0],  1, 0x00);
+       hid_set_field(report2->field[0],  2, 0x00);
+       hid_set_field(report2->field[0],  3,   32);
+
+       tdata = vbitmap + (tile * 4 + chip) * 64;
+       for (i = 0; i < 64; i++)
+               if (i < 32)
+                       hid_set_field(report1->field[0], 11 + i, tdata[i]);
+               else
+                       hid_set_field(report2->field[0], 4 + i - 32, tdata[i]);
+
+       usbhid_submit_report(data->hdev, report1, USB_DIR_OUT);
+       usbhid_submit_report(data->hdev, report2, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+       return 0;
+}
+
+/* Translate a single tile*/
+static int picolcd_fb_update_tile(u8 *vbitmap, const u8 *bitmap, int bpp,
+               int chip, int tile)
+{
+       int i, b, changed = 0;
+       u8 tdata[64];
+       u8 *vdata = vbitmap + (tile * 4 + chip) * 64;
+
+       if (bpp == 1) {
+               for (b = 7; b >= 0; b--) {
+                       const u8 *bdata = bitmap + tile * 256 + chip * 8 + b * 32;
+                       for (i = 0; i < 64; i++) {
+                               tdata[i] <<= 1;
+                               tdata[i] |= (bdata[i/8] >> (i % 8)) & 0x01;
+                       }
+               }
+       } else if (bpp == 8) {
+               for (b = 7; b >= 0; b--) {
+                       const u8 *bdata = bitmap + (tile * 256 + chip * 8 + b * 32) * 8;
+                       for (i = 0; i < 64; i++) {
+                               tdata[i] <<= 1;
+                               tdata[i] |= (bdata[i] & 0x80) ? 0x01 : 0x00;
+                       }
+               }
+       } else {
+               /* Oops, we should never get here! */
+               WARN_ON(1);
+               return 0;
+       }
+
+       for (i = 0; i < 64; i++)
+               if (tdata[i] != vdata[i]) {
+                       changed = 1;
+                       vdata[i] = tdata[i];
+               }
+       return changed;
+}
+
+void picolcd_fb_refresh(struct picolcd_data *data)
+{
+       if (data->fb_info)
+               schedule_delayed_work(&data->fb_info->deferred_work, 0);
+}
+
+/* Reconfigure LCD display */
+int picolcd_fb_reset(struct picolcd_data *data, int clear)
+{
+       struct hid_report *report = picolcd_out_report(REPORT_LCD_CMD, data->hdev);
+       struct picolcd_fb_data *fbdata = data->fb_info->par;
+       int i, j;
+       unsigned long flags;
+       static const u8 mapcmd[8] = { 0x00, 0x02, 0x00, 0x64, 0x3f, 0x00, 0x64, 0xc0 };
+
+       if (!report || report->maxfield != 1)
+               return -ENODEV;
+
+       spin_lock_irqsave(&data->lock, flags);
+       for (i = 0; i < 4; i++) {
+               for (j = 0; j < report->field[0]->maxusage; j++)
+                       if (j == 0)
+                               hid_set_field(report->field[0], j, i << 2);
+                       else if (j < sizeof(mapcmd))
+                               hid_set_field(report->field[0], j, mapcmd[j]);
+                       else
+                               hid_set_field(report->field[0], j, 0);
+               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+       }
+       spin_unlock_irqrestore(&data->lock, flags);
+
+       if (clear) {
+               memset(fbdata->vbitmap, 0, PICOLCDFB_SIZE);
+               memset(fbdata->bitmap, 0, PICOLCDFB_SIZE*fbdata->bpp);
+       }
+       fbdata->force = 1;
+
+       /* schedule first output of framebuffer */
+       if (fbdata->ready)
+               schedule_delayed_work(&data->fb_info->deferred_work, 0);
+       else
+               fbdata->ready = 1;
+
+       return 0;
+}
+
+/* Update fb_vbitmap from the screen_base and send changed tiles to device */
+static void picolcd_fb_update(struct fb_info *info)
+{
+       int chip, tile, n;
+       unsigned long flags;
+       struct picolcd_fb_data *fbdata = info->par;
+       struct picolcd_data *data;
+
+       mutex_lock(&info->lock);
+
+       spin_lock_irqsave(&fbdata->lock, flags);
+       if (!fbdata->ready && fbdata->picolcd)
+               picolcd_fb_reset(fbdata->picolcd, 0);
+       spin_unlock_irqrestore(&fbdata->lock, flags);
+
+       /*
+        * Translate the framebuffer into the format needed by the PicoLCD.
+        * See display layout above.
+        * Do this one tile after the other and push those tiles that changed.
+        *
+        * Wait for our IO to complete as otherwise we might flood the queue!
+        */
+       n = 0;
+       for (chip = 0; chip < 4; chip++)
+               for (tile = 0; tile < 8; tile++) {
+                       if (!fbdata->force && !picolcd_fb_update_tile(
+                                       fbdata->vbitmap, fbdata->bitmap,
+                                       fbdata->bpp, chip, tile))
+                               continue;
+                       n += 2;
+                       if (n >= HID_OUTPUT_FIFO_SIZE / 2) {
+                               spin_lock_irqsave(&fbdata->lock, flags);
+                               data = fbdata->picolcd;
+                               spin_unlock_irqrestore(&fbdata->lock, flags);
+                               mutex_unlock(&info->lock);
+                               if (!data)
+                                       return;
+                               usbhid_wait_io(data->hdev);
+                               mutex_lock(&info->lock);
+                               n = 0;
+                       }
+                       spin_lock_irqsave(&fbdata->lock, flags);
+                       data = fbdata->picolcd;
+                       spin_unlock_irqrestore(&fbdata->lock, flags);
+                       if (!data || picolcd_fb_send_tile(data,
+                                       fbdata->vbitmap, chip, tile))
+                               goto out;
+               }
+       fbdata->force = false;
+       if (n) {
+               spin_lock_irqsave(&fbdata->lock, flags);
+               data = fbdata->picolcd;
+               spin_unlock_irqrestore(&fbdata->lock, flags);
+               mutex_unlock(&info->lock);
+               if (data)
+                       usbhid_wait_io(data->hdev);
+               return;
+       }
+out:
+       mutex_unlock(&info->lock);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_fillrect(struct fb_info *info,
+               const struct fb_fillrect *rect)
+{
+       if (!info->par)
+               return;
+       sys_fillrect(info, rect);
+
+       schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_copyarea(struct fb_info *info,
+               const struct fb_copyarea *area)
+{
+       if (!info->par)
+               return;
+       sys_copyarea(info, area);
+
+       schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/* Stub to call the system default and update the image on the picoLCD */
+static void picolcd_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+       if (!info->par)
+               return;
+       sys_imageblit(info, image);
+
+       schedule_delayed_work(&info->deferred_work, 0);
+}
+
+/*
+ * this is the slow path from userspace. they can seek and write to
+ * the fb. it's inefficient to do anything less than a full screen draw
+ */
+static ssize_t picolcd_fb_write(struct fb_info *info, const char __user *buf,
+               size_t count, loff_t *ppos)
+{
+       ssize_t ret;
+       if (!info->par)
+               return -ENODEV;
+       ret = fb_sys_write(info, buf, count, ppos);
+       if (ret >= 0)
+               schedule_delayed_work(&info->deferred_work, 0);
+       return ret;
+}
+
+static int picolcd_fb_blank(int blank, struct fb_info *info)
+{
+       /* We let fb notification do this for us via lcd/backlight device */
+       return 0;
+}
+
+static void picolcd_fb_destroy(struct fb_info *info)
+{
+       struct picolcd_fb_data *fbdata = info->par;
+
+       /* make sure no work is deferred */
+       fb_deferred_io_cleanup(info);
+
+       /* No thridparty should ever unregister our framebuffer! */
+       WARN_ON(fbdata->picolcd != NULL);
+
+       vfree((u8 *)info->fix.smem_start);
+       framebuffer_release(info);
+}
+
+static int picolcd_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+       __u32 bpp      = var->bits_per_pixel;
+       __u32 activate = var->activate;
+
+       /* only allow 1/8 bit depth (8-bit is grayscale) */
+       *var = picolcdfb_var;
+       var->activate = activate;
+       if (bpp >= 8) {
+               var->bits_per_pixel = 8;
+               var->red.length     = 8;
+               var->green.length   = 8;
+               var->blue.length    = 8;
+       } else {
+               var->bits_per_pixel = 1;
+               var->red.length     = 1;
+               var->green.length   = 1;
+               var->blue.length    = 1;
+       }
+       return 0;
+}
+
+static int picolcd_set_par(struct fb_info *info)
+{
+       struct picolcd_fb_data *fbdata = info->par;
+       u8 *tmp_fb, *o_fb;
+       if (info->var.bits_per_pixel == fbdata->bpp)
+               return 0;
+       /* switch between 1/8 bit depths */
+       if (info->var.bits_per_pixel != 1 && info->var.bits_per_pixel != 8)
+               return -EINVAL;
+
+       o_fb   = fbdata->bitmap;
+       tmp_fb = kmalloc(PICOLCDFB_SIZE*info->var.bits_per_pixel, GFP_KERNEL);
+       if (!tmp_fb)
+               return -ENOMEM;
+
+       /* translate FB content to new bits-per-pixel */
+       if (info->var.bits_per_pixel == 1) {
+               int i, b;
+               for (i = 0; i < PICOLCDFB_SIZE; i++) {
+                       u8 p = 0;
+                       for (b = 0; b < 8; b++) {
+                               p <<= 1;
+                               p |= o_fb[i*8+b] ? 0x01 : 0x00;
+                       }
+                       tmp_fb[i] = p;
+               }
+               memcpy(o_fb, tmp_fb, PICOLCDFB_SIZE);
+               info->fix.visual = FB_VISUAL_MONO01;
+               info->fix.line_length = PICOLCDFB_WIDTH / 8;
+       } else {
+               int i;
+               memcpy(tmp_fb, o_fb, PICOLCDFB_SIZE);
+               for (i = 0; i < PICOLCDFB_SIZE * 8; i++)
+                       o_fb[i] = tmp_fb[i/8] & (0x01 << (7 - i % 8)) ? 0xff : 0x00;
+               info->fix.visual = FB_VISUAL_DIRECTCOLOR;
+               info->fix.line_length = PICOLCDFB_WIDTH;
+       }
+
+       kfree(tmp_fb);
+       fbdata->bpp = info->var.bits_per_pixel;
+       return 0;
+}
+
+/* Note this can't be const because of struct fb_info definition */
+static struct fb_ops picolcdfb_ops = {
+       .owner        = THIS_MODULE,
+       .fb_destroy   = picolcd_fb_destroy,
+       .fb_read      = fb_sys_read,
+       .fb_write     = picolcd_fb_write,
+       .fb_blank     = picolcd_fb_blank,
+       .fb_fillrect  = picolcd_fb_fillrect,
+       .fb_copyarea  = picolcd_fb_copyarea,
+       .fb_imageblit = picolcd_fb_imageblit,
+       .fb_check_var = picolcd_fb_check_var,
+       .fb_set_par   = picolcd_set_par,
+};
+
+
+/* Callback from deferred IO workqueue */
+static void picolcd_fb_deferred_io(struct fb_info *info, struct list_head *pagelist)
+{
+       picolcd_fb_update(info);
+}
+
+static const struct fb_deferred_io picolcd_fb_defio = {
+       .delay = HZ / PICOLCDFB_UPDATE_RATE_DEFAULT,
+       .deferred_io = picolcd_fb_deferred_io,
+};
+
+
+/*
+ * The "fb_update_rate" sysfs attribute
+ */
+static ssize_t picolcd_fb_update_rate_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+       struct picolcd_fb_data *fbdata = data->fb_info->par;
+       unsigned i, fb_update_rate = fbdata->update_rate;
+       size_t ret = 0;
+
+       for (i = 1; i <= PICOLCDFB_UPDATE_RATE_LIMIT; i++)
+               if (ret >= PAGE_SIZE)
+                       break;
+               else if (i == fb_update_rate)
+                       ret += snprintf(buf+ret, PAGE_SIZE-ret, "[%u] ", i);
+               else
+                       ret += snprintf(buf+ret, PAGE_SIZE-ret, "%u ", i);
+       if (ret > 0)
+               buf[min(ret, (size_t)PAGE_SIZE)-1] = '\n';
+       return ret;
+}
+
+static ssize_t picolcd_fb_update_rate_store(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t count)
+{
+       struct picolcd_data *data = dev_get_drvdata(dev);
+       struct picolcd_fb_data *fbdata = data->fb_info->par;
+       int i;
+       unsigned u;
+
+       if (count < 1 || count > 10)
+               return -EINVAL;
+
+       i = sscanf(buf, "%u", &u);
+       if (i != 1)
+               return -EINVAL;
+
+       if (u > PICOLCDFB_UPDATE_RATE_LIMIT)
+               return -ERANGE;
+       else if (u == 0)
+               u = PICOLCDFB_UPDATE_RATE_DEFAULT;
+
+       fbdata->update_rate = u;
+       data->fb_info->fbdefio->delay = HZ / fbdata->update_rate;
+       return count;
+}
+
+static DEVICE_ATTR(fb_update_rate, 0666, picolcd_fb_update_rate_show,
+               picolcd_fb_update_rate_store);
+
+/* initialize Framebuffer device */
+int picolcd_init_framebuffer(struct picolcd_data *data)
+{
+       struct device *dev = &data->hdev->dev;
+       struct fb_info *info = NULL;
+       struct picolcd_fb_data *fbdata = NULL;
+       int i, error = -ENOMEM;
+       u32 *palette;
+
+       /* The extra memory is:
+        * - 256*u32 for pseudo_palette
+        * - struct fb_deferred_io
+        */
+       info = framebuffer_alloc(256 * sizeof(u32) +
+                       sizeof(struct fb_deferred_io) +
+                       sizeof(struct picolcd_fb_data) +
+                       PICOLCDFB_SIZE, dev);
+       if (info == NULL) {
+               dev_err(dev, "failed to allocate a framebuffer\n");
+               goto err_nomem;
+       }
+
+       info->fbdefio = info->par;
+       *info->fbdefio = picolcd_fb_defio;
+       info->par += sizeof(struct fb_deferred_io);
+       palette = info->par;
+       info->par += 256 * sizeof(u32);
+       for (i = 0; i < 256; i++)
+               palette[i] = i > 0 && i < 16 ? 0xff : 0;
+       info->pseudo_palette = palette;
+       info->fbops = &picolcdfb_ops;
+       info->var = picolcdfb_var;
+       info->fix = picolcdfb_fix;
+       info->fix.smem_len   = PICOLCDFB_SIZE*8;
+       info->flags = FBINFO_FLAG_DEFAULT;
+
+       fbdata = info->par;
+       spin_lock_init(&fbdata->lock);
+       fbdata->picolcd = data;
+       fbdata->update_rate = PICOLCDFB_UPDATE_RATE_DEFAULT;
+       fbdata->bpp     = picolcdfb_var.bits_per_pixel;
+       fbdata->force   = 1;
+       fbdata->vbitmap = info->par + sizeof(struct picolcd_fb_data);
+       fbdata->bitmap  = vmalloc(PICOLCDFB_SIZE*8);
+       if (fbdata->bitmap == NULL) {
+               dev_err(dev, "can't get a free page for framebuffer\n");
+               goto err_nomem;
+       }
+       info->screen_base = (char __force __iomem *)fbdata->bitmap;
+       info->fix.smem_start = (unsigned long)fbdata->bitmap;
+       memset(fbdata->vbitmap, 0xff, PICOLCDFB_SIZE);
+       data->fb_info = info;
+
+       error = picolcd_fb_reset(data, 1);
+       if (error) {
+               dev_err(dev, "failed to configure display\n");
+               goto err_cleanup;
+       }
+
+       error = device_create_file(dev, &dev_attr_fb_update_rate);
+       if (error) {
+               dev_err(dev, "failed to create sysfs attributes\n");
+               goto err_cleanup;
+       }
+
+       fb_deferred_io_init(info);
+       error = register_framebuffer(info);
+       if (error) {
+               dev_err(dev, "failed to register framebuffer\n");
+               goto err_sysfs;
+       }
+       return 0;
+
+err_sysfs:
+       device_remove_file(dev, &dev_attr_fb_update_rate);
+       fb_deferred_io_cleanup(info);
+err_cleanup:
+       data->fb_info    = NULL;
+
+err_nomem:
+       if (fbdata)
+               vfree(fbdata->bitmap);
+       framebuffer_release(info);
+       return error;
+}
+
+void picolcd_exit_framebuffer(struct picolcd_data *data)
+{
+       struct fb_info *info = data->fb_info;
+       struct picolcd_fb_data *fbdata = info->par;
+       unsigned long flags;
+
+       device_remove_file(&data->hdev->dev, &dev_attr_fb_update_rate);
+
+       /* disconnect framebuffer from HID dev */
+       spin_lock_irqsave(&fbdata->lock, flags);
+       fbdata->picolcd = NULL;
+       spin_unlock_irqrestore(&fbdata->lock, flags);
+
+       /* make sure there is no running update - thus that fbdata->picolcd
+        * once obtained under lock is guaranteed not to get free() under
+        * the feet of the deferred work */
+       flush_delayed_work_sync(&info->deferred_work);
+
+       data->fb_info = NULL;
+       unregister_framebuffer(info);
+}
diff --git a/drivers/hid/hid-picolcd_lcd.c b/drivers/hid/hid-picolcd_lcd.c
new file mode 100644 (file)
index 0000000..2d0ddc5
--- /dev/null
@@ -0,0 +1,107 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/lcd.h>
+
+#include "hid-picolcd.h"
+
+/*
+ * lcd class device
+ */
+static int picolcd_get_contrast(struct lcd_device *ldev)
+{
+       struct picolcd_data *data = lcd_get_data(ldev);
+       return data->lcd_contrast;
+}
+
+static int picolcd_set_contrast(struct lcd_device *ldev, int contrast)
+{
+       struct picolcd_data *data = lcd_get_data(ldev);
+       struct hid_report *report = picolcd_out_report(REPORT_CONTRAST, data->hdev);
+       unsigned long flags;
+
+       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+               return -ENODEV;
+
+       data->lcd_contrast = contrast & 0x0ff;
+       spin_lock_irqsave(&data->lock, flags);
+       hid_set_field(report->field[0], 0, data->lcd_contrast);
+       if (!(data->status & PICOLCD_FAILED))
+               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+       return 0;
+}
+
+static int picolcd_check_lcd_fb(struct lcd_device *ldev, struct fb_info *fb)
+{
+       return fb && fb == picolcd_fbinfo((struct picolcd_data *)lcd_get_data(ldev));
+}
+
+static struct lcd_ops picolcd_lcdops = {
+       .get_contrast   = picolcd_get_contrast,
+       .set_contrast   = picolcd_set_contrast,
+       .check_fb       = picolcd_check_lcd_fb,
+};
+
+int picolcd_init_lcd(struct picolcd_data *data, struct hid_report *report)
+{
+       struct device *dev = &data->hdev->dev;
+       struct lcd_device *ldev;
+
+       if (!report)
+               return -ENODEV;
+       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+                       report->field[0]->report_size != 8) {
+               dev_err(dev, "unsupported CONTRAST report");
+               return -EINVAL;
+       }
+
+       ldev = lcd_device_register(dev_name(dev), dev, data, &picolcd_lcdops);
+       if (IS_ERR(ldev)) {
+               dev_err(dev, "failed to register LCD\n");
+               return PTR_ERR(ldev);
+       }
+       ldev->props.max_contrast = 0x0ff;
+       data->lcd_contrast = 0xe5;
+       data->lcd = ldev;
+       picolcd_set_contrast(ldev, 0xe5);
+       return 0;
+}
+
+void picolcd_exit_lcd(struct picolcd_data *data)
+{
+       struct lcd_device *ldev = data->lcd;
+
+       data->lcd = NULL;
+       if (ldev)
+               lcd_device_unregister(ldev);
+}
+
+int picolcd_resume_lcd(struct picolcd_data *data)
+{
+       if (!data->lcd)
+               return 0;
+       return picolcd_set_contrast(data->lcd, data->lcd_contrast);
+}
+
diff --git a/drivers/hid/hid-picolcd_leds.c b/drivers/hid/hid-picolcd_leds.c
new file mode 100644 (file)
index 0000000..28cb6a4
--- /dev/null
@@ -0,0 +1,175 @@
+/***************************************************************************
+ *   Copyright (C) 2010-2012 by Bruno Prémont <bonbons@linux-vserver.org>  *
+ *                                                                         *
+ *   Based on Logitech G13 driver (v0.4)                                   *
+ *     Copyright (C) 2009 by Rick L. Vinyard, Jr. <rvinyard@cs.nmsu.edu>   *
+ *                                                                         *
+ *   This program is free software: you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation, version 2 of the License.               *
+ *                                                                         *
+ *   This driver 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 software. If not see <http://www.gnu.org/licenses/>.  *
+ ***************************************************************************/
+
+#include <linux/hid.h>
+#include <linux/hid-debug.h>
+#include <linux/input.h>
+#include "hid-ids.h"
+#include "usbhid/usbhid.h"
+#include <linux/usb.h>
+
+#include <linux/fb.h>
+#include <linux/vmalloc.h>
+#include <linux/backlight.h>
+#include <linux/lcd.h>
+
+#include <linux/leds.h>
+
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#include <linux/completion.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include "hid-picolcd.h"
+
+
+void picolcd_leds_set(struct picolcd_data *data)
+{
+       struct hid_report *report;
+       unsigned long flags;
+
+       if (!data->led[0])
+               return;
+       report = picolcd_out_report(REPORT_LED_STATE, data->hdev);
+       if (!report || report->maxfield != 1 || report->field[0]->report_count != 1)
+               return;
+
+       spin_lock_irqsave(&data->lock, flags);
+       hid_set_field(report->field[0], 0, data->led_state);
+       if (!(data->status & PICOLCD_FAILED))
+               usbhid_submit_report(data->hdev, report, USB_DIR_OUT);
+       spin_unlock_irqrestore(&data->lock, flags);
+}
+
+static void picolcd_led_set_brightness(struct led_classdev *led_cdev,
+                       enum led_brightness value)
+{
+       struct device *dev;
+       struct hid_device *hdev;
+       struct picolcd_data *data;
+       int i, state = 0;
+
+       dev  = led_cdev->dev->parent;
+       hdev = container_of(dev, struct hid_device, dev);
+       data = hid_get_drvdata(hdev);
+       if (!data)
+               return;
+       for (i = 0; i < 8; i++) {
+               if (led_cdev != data->led[i])
+                       continue;
+               state = (data->led_state >> i) & 1;
+               if (value == LED_OFF && state) {
+                       data->led_state &= ~(1 << i);
+                       picolcd_leds_set(data);
+               } else if (value != LED_OFF && !state) {
+                       data->led_state |= 1 << i;
+                       picolcd_leds_set(data);
+               }
+               break;
+       }
+}
+
+static enum led_brightness picolcd_led_get_brightness(struct led_classdev *led_cdev)
+{
+       struct device *dev;
+       struct hid_device *hdev;
+       struct picolcd_data *data;
+       int i, value = 0;
+
+       dev  = led_cdev->dev->parent;
+       hdev = container_of(dev, struct hid_device, dev);
+       data = hid_get_drvdata(hdev);
+       for (i = 0; i < 8; i++)
+               if (led_cdev == data->led[i]) {
+                       value = (data->led_state >> i) & 1;
+                       break;
+               }
+       return value ? LED_FULL : LED_OFF;
+}
+
+int picolcd_init_leds(struct picolcd_data *data, struct hid_report *report)
+{
+       struct device *dev = &data->hdev->dev;
+       struct led_classdev *led;
+       size_t name_sz = strlen(dev_name(dev)) + 8;
+       char *name;
+       int i, ret = 0;
+
+       if (!report)
+               return -ENODEV;
+       if (report->maxfield != 1 || report->field[0]->report_count != 1 ||
+                       report->field[0]->report_size != 8) {
+               dev_err(dev, "unsupported LED_STATE report");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < 8; i++) {
+               led = kzalloc(sizeof(struct led_classdev)+name_sz, GFP_KERNEL);
+               if (!led) {
+                       dev_err(dev, "can't allocate memory for LED %d\n", i);
+                       ret = -ENOMEM;
+                       goto err;
+               }
+               name = (void *)(&led[1]);
+               snprintf(name, name_sz, "%s::GPO%d", dev_name(dev), i);
+               led->name = name;
+               led->brightness = 0;
+               led->max_brightness = 1;
+               led->brightness_get = picolcd_led_get_brightness;
+               led->brightness_set = picolcd_led_set_brightness;
+
+               data->led[i] = led;
+               ret = led_classdev_register(dev, data->led[i]);
+               if (ret) {
+                       data->led[i] = NULL;
+                       kfree(led);
+                       dev_err(dev, "can't register LED %d\n", i);
+                       goto err;
+               }
+       }
+       return 0;
+err:
+       for (i = 0; i < 8; i++)
+               if (data->led[i]) {
+                       led = data->led[i];
+                       data->led[i] = NULL;
+                       led_classdev_unregister(led);
+                       kfree(led);
+               }
+       return ret;
+}
+
+void picolcd_exit_leds(struct picolcd_data *data)
+{
+       struct led_classdev *led;
+       int i;
+
+       for (i = 0; i < 8; i++) {
+               led = data->led[i];
+               data->led[i] = NULL;
+               if (!led)
+                       continue;
+               led_classdev_unregister(led);
+               kfree(led);
+       }
+}
+
+
index 4d3c60d..c15adb0 100644 (file)
@@ -64,29 +64,6 @@ static int px_raw_event(struct hid_device *hid, struct hid_report *report,
        return 0;
 }
 
-static int px_probe(struct hid_device *hid, const struct hid_device_id *id)
-{
-       int ret;
-
-       ret = hid_parse(hid);
-       if (ret) {
-               hid_err(hid, "parse failed\n");
-               goto fail;
-       }
-
-       ret = hid_hw_start(hid, HID_CONNECT_DEFAULT);
-       if (ret)
-               hid_err(hid, "hw start failed\n");
-
-fail:
-       return ret;
-}
-
-static void px_remove(struct hid_device *hid)
-{
-       hid_hw_stop(hid);
-}
-
 static const struct hid_device_id px_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_PRIMAX, USB_DEVICE_ID_PRIMAX_KEYBOARD) },
        { }
@@ -97,8 +74,6 @@ static struct hid_driver px_driver = {
        .name = "primax",
        .id_table = px_devices,
        .raw_event = px_raw_event,
-       .probe = px_probe,
-       .remove = px_remove,
 };
 
 static int __init px_init(void)
index b71b77a..ec8ca33 100644 (file)
@@ -105,7 +105,7 @@ static ssize_t show_channel(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        dbg_hid("pcmidi sysfs read channel=%u\n", pk->pm->midi_channel);
 
@@ -118,7 +118,7 @@ static ssize_t store_channel(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t count)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        unsigned channel = 0;
 
@@ -142,7 +142,7 @@ static ssize_t show_sustain(struct device *dev,
  struct device_attribute *attr, char *buf)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        dbg_hid("pcmidi sysfs read sustain=%u\n", pk->pm->midi_sustain);
 
@@ -155,7 +155,7 @@ static ssize_t store_sustain(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t count)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        unsigned sustain = 0;
 
@@ -181,7 +181,7 @@ static ssize_t show_octave(struct device *dev,
        struct device_attribute *attr, char *buf)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        dbg_hid("pcmidi sysfs read octave=%d\n", pk->pm->midi_octave);
 
@@ -194,7 +194,7 @@ static ssize_t store_octave(struct device *dev,
        struct device_attribute *attr, const char *buf, size_t count)
 {
        struct hid_device *hdev = container_of(dev, struct hid_device, dev);
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
 
        int octave = 0;
 
@@ -759,7 +759,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
                struct hid_field *field, struct hid_usage *usage,
                unsigned long **bit, int *max)
 {
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
        struct pcmidi_snd *pm;
 
        pm = pk->pm;
@@ -777,7 +777,7 @@ static int pk_input_mapping(struct hid_device *hdev, struct hid_input *hi,
 static int pk_raw_event(struct hid_device *hdev, struct hid_report *report,
        u8 *data, int size)
 {
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
        int ret = 0;
 
        if (1 == pk->pm->ifnum) {
@@ -858,7 +858,7 @@ err_free_pk:
 
 static void pk_remove(struct hid_device *hdev)
 {
-       struct pk_device *pk = (struct pk_device *)hid_get_drvdata(hdev);
+       struct pk_device *pk = hid_get_drvdata(hdev);
        struct pcmidi_snd *pm;
 
        pm = pk->pm;
diff --git a/drivers/hid/hid-ps3remote.c b/drivers/hid/hid-ps3remote.c
new file mode 100644 (file)
index 0000000..03811e5
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * HID driver for Sony PS3 BD Remote Control
+ *
+ * Copyright (c) 2012 David Dillow <dave@thedillows.org>
+ * Based on a blend of the bluez fakehid user-space code by Marcel Holtmann
+ * and other kernel HID drivers.
+ */
+
+/*
+ * 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.
+ */
+
+/* NOTE: in order for the Sony PS3 BD Remote Control to be found by
+ * a Bluetooth host, the key combination Start+Enter has to be kept pressed
+ * for about 7 seconds with the Bluetooth Host Controller in discovering mode.
+ *
+ * There will be no PIN request from the device.
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+static __u8 ps3remote_rdesc[] = {
+       0x05, 0x01,          /* GUsagePage Generic Desktop */
+       0x09, 0x05,          /* LUsage 0x05 [Game Pad] */
+       0xA1, 0x01,          /* MCollection Application (mouse, keyboard) */
+
+        /* Use collection 1 for joypad buttons */
+        0xA1, 0x02,         /* MCollection Logical (interrelated data) */
+
+         /* Ignore the 1st byte, maybe it is used for a controller
+          * number but it's not needed for correct operation */
+         0x75, 0x08,        /* GReportSize 0x08 [8] */
+         0x95, 0x01,        /* GReportCount 0x01 [1] */
+         0x81, 0x01,        /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+         /* Bytes from 2nd to 4th are a bitmap for joypad buttons, for these
+          * buttons multiple keypresses are allowed */
+         0x05, 0x09,        /* GUsagePage Button */
+         0x19, 0x01,        /* LUsageMinimum 0x01 [Button 1 (primary/trigger)] */
+         0x29, 0x18,        /* LUsageMaximum 0x18 [Button 24] */
+         0x14,              /* GLogicalMinimum [0] */
+         0x25, 0x01,        /* GLogicalMaximum 0x01 [1] */
+         0x75, 0x01,        /* GReportSize 0x01 [1] */
+         0x95, 0x18,        /* GReportCount 0x18 [24] */
+         0x81, 0x02,        /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+         0xC0,              /* MEndCollection */
+
+        /* Use collection 2 for remote control buttons */
+        0xA1, 0x02,         /* MCollection Logical (interrelated data) */
+
+         /* 5th byte is used for remote control buttons */
+         0x05, 0x09,        /* GUsagePage Button */
+         0x18,              /* LUsageMinimum [No button pressed] */
+         0x29, 0xFE,        /* LUsageMaximum 0xFE [Button 254] */
+         0x14,              /* GLogicalMinimum [0] */
+         0x26, 0xFE, 0x00,  /* GLogicalMaximum 0x00FE [254] */
+         0x75, 0x08,        /* GReportSize 0x08 [8] */
+         0x95, 0x01,        /* GReportCount 0x01 [1] */
+         0x80,              /* MInput  */
+
+         /* Ignore bytes from 6th to 11th, 6th to 10th are always constant at
+          * 0xff and 11th is for press indication */
+         0x75, 0x08,        /* GReportSize 0x08 [8] */
+         0x95, 0x06,        /* GReportCount 0x06 [6] */
+         0x81, 0x01,        /* MInput 0x01 (Const[0] Arr[1] Abs[2]) */
+
+         /* 12th byte is for battery strength */
+         0x05, 0x06,        /* GUsagePage Generic Device Controls */
+         0x09, 0x20,        /* LUsage 0x20 [Battery Strength] */
+         0x14,              /* GLogicalMinimum [0] */
+         0x25, 0x05,        /* GLogicalMaximum 0x05 [5] */
+         0x75, 0x08,        /* GReportSize 0x08 [8] */
+         0x95, 0x01,        /* GReportCount 0x01 [1] */
+         0x81, 0x02,        /* MInput 0x02 (Data[0] Var[1] Abs[2]) */
+
+         0xC0,              /* MEndCollection */
+
+        0xC0                /* MEndCollection [Game Pad] */
+};
+
+static const unsigned int ps3remote_keymap_joypad_buttons[] = {
+       [0x01] = KEY_SELECT,
+       [0x02] = BTN_THUMBL,            /* L3 */
+       [0x03] = BTN_THUMBR,            /* R3 */
+       [0x04] = BTN_START,
+       [0x05] = KEY_UP,
+       [0x06] = KEY_RIGHT,
+       [0x07] = KEY_DOWN,
+       [0x08] = KEY_LEFT,
+       [0x09] = BTN_TL2,               /* L2 */
+       [0x0a] = BTN_TR2,               /* R2 */
+       [0x0b] = BTN_TL,                /* L1 */
+       [0x0c] = BTN_TR,                /* R1 */
+       [0x0d] = KEY_OPTION,            /* options/triangle */
+       [0x0e] = KEY_BACK,              /* back/circle */
+       [0x0f] = BTN_0,                 /* cross */
+       [0x10] = KEY_SCREEN,            /* view/square */
+       [0x11] = KEY_HOMEPAGE,          /* PS button */
+       [0x14] = KEY_ENTER,
+};
+static const unsigned int ps3remote_keymap_remote_buttons[] = {
+       [0x00] = KEY_1,
+       [0x01] = KEY_2,
+       [0x02] = KEY_3,
+       [0x03] = KEY_4,
+       [0x04] = KEY_5,
+       [0x05] = KEY_6,
+       [0x06] = KEY_7,
+       [0x07] = KEY_8,
+       [0x08] = KEY_9,
+       [0x09] = KEY_0,
+       [0x0e] = KEY_ESC,               /* return */
+       [0x0f] = KEY_CLEAR,
+       [0x16] = KEY_EJECTCD,
+       [0x1a] = KEY_MENU,              /* top menu */
+       [0x28] = KEY_TIME,
+       [0x30] = KEY_PREVIOUS,
+       [0x31] = KEY_NEXT,
+       [0x32] = KEY_PLAY,
+       [0x33] = KEY_REWIND,            /* scan back */
+       [0x34] = KEY_FORWARD,           /* scan forward */
+       [0x38] = KEY_STOP,
+       [0x39] = KEY_PAUSE,
+       [0x40] = KEY_CONTEXT_MENU,      /* pop up/menu */
+       [0x60] = KEY_FRAMEBACK,         /* slow/step back */
+       [0x61] = KEY_FRAMEFORWARD,      /* slow/step forward */
+       [0x63] = KEY_SUBTITLE,
+       [0x64] = KEY_AUDIO,
+       [0x65] = KEY_ANGLE,
+       [0x70] = KEY_INFO,              /* display */
+       [0x80] = KEY_BLUE,
+       [0x81] = KEY_RED,
+       [0x82] = KEY_GREEN,
+       [0x83] = KEY_YELLOW,
+};
+
+static __u8 *ps3remote_fixup(struct hid_device *hdev, __u8 *rdesc,
+                            unsigned int *rsize)
+{
+       *rsize = sizeof(ps3remote_rdesc);
+       return ps3remote_rdesc;
+}
+
+static int ps3remote_mapping(struct hid_device *hdev, struct hid_input *hi,
+                            struct hid_field *field, struct hid_usage *usage,
+                            unsigned long **bit, int *max)
+{
+       unsigned int key = usage->hid & HID_USAGE;
+
+       if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
+               return -1;
+
+       switch (usage->collection_index) {
+       case 1:
+               if (key >= ARRAY_SIZE(ps3remote_keymap_joypad_buttons))
+                       return -1;
+
+               key = ps3remote_keymap_joypad_buttons[key];
+               if (!key)
+                       return -1;
+               break;
+       case 2:
+               if (key >= ARRAY_SIZE(ps3remote_keymap_remote_buttons))
+                       return -1;
+
+               key = ps3remote_keymap_remote_buttons[key];
+               if (!key)
+                       return -1;
+               break;
+       default:
+               return -1;
+       }
+
+       hid_map_usage_clear(hi, usage, bit, max, EV_KEY, key);
+       return 1;
+}
+
+static const struct hid_device_id ps3remote_devices[] = {
+       /* PS3 BD Remote Control */
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS3_BDREMOTE) },
+       /* Logitech Harmony Adapter for PS3 */
+       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_LOGITECH_HARMONY_PS3) },
+       { }
+};
+MODULE_DEVICE_TABLE(hid, ps3remote_devices);
+
+static struct hid_driver ps3remote_driver = {
+       .name          = "ps3_remote",
+       .id_table      = ps3remote_devices,
+       .report_fixup  = ps3remote_fixup,
+       .input_mapping = ps3remote_mapping,
+};
+
+static int __init ps3remote_init(void)
+{
+       return hid_register_driver(&ps3remote_driver);
+}
+
+static void __exit ps3remote_exit(void)
+{
+       hid_unregister_driver(&ps3remote_driver);
+}
+
+module_init(ps3remote_init);
+module_exit(ps3remote_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Dillow <dave@thedillows.org>, Antonio Ospite <ospite@studenti.unina.it>");
index 3c1fd8a..a5821d3 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2010 Don Prince <dhprince.devel@yahoo.co.uk>
  *
index 5cd25bd..7f33ebf 100644 (file)
@@ -4,7 +4,6 @@
  *  Copyright (c) 1999 Andreas Gal
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  *  Copyright (c) 2006-2008 Jiri Kosina
  */
index d484a00..45b4b06 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby
  */
 
index 3aba02b..2e56a1f 100644 (file)
@@ -466,6 +466,86 @@ static __u8 twhl850_rdesc_fixed2[] = {
        0xC0                /*  End Collection                      */
 };
 
+/*
+ * See TWHA60 description, device and HID report descriptors at
+ * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
+ */
+
+/* Size of the original descriptors of TWHA60 tablet */
+#define TWHA60_RDESC_ORIG_SIZE0 254
+#define TWHA60_RDESC_ORIG_SIZE1 139
+
+/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
+static __u8 twha60_rdesc_fixed0[] = {
+       0x05, 0x0D,         /*  Usage Page (Digitizer),             */
+       0x09, 0x02,         /*  Usage (Pen),                        */
+       0xA1, 0x01,         /*  Collection (Application),           */
+       0x85, 0x09,         /*      Report ID (9),                  */
+       0x09, 0x20,         /*      Usage (Stylus),                 */
+       0xA0,               /*      Collection (Physical),          */
+       0x75, 0x01,         /*          Report Size (1),            */
+       0x09, 0x42,         /*          Usage (Tip Switch),         */
+       0x09, 0x44,         /*          Usage (Barrel Switch),      */
+       0x09, 0x46,         /*          Usage (Tablet Pick),        */
+       0x14,               /*          Logical Minimum (0),        */
+       0x25, 0x01,         /*          Logical Maximum (1),        */
+       0x95, 0x03,         /*          Report Count (3),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x95, 0x04,         /*          Report Count (4),           */
+       0x81, 0x01,         /*          Input (Constant),           */
+       0x09, 0x32,         /*          Usage (In Range),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x75, 0x10,         /*          Report Size (16),           */
+       0x95, 0x01,         /*          Report Count (1),           */
+       0x14,               /*          Logical Minimum (0),        */
+       0xA4,               /*          Push,                       */
+       0x05, 0x01,         /*          Usage Page (Desktop),       */
+       0x55, 0xFD,         /*          Unit Exponent (-3),         */
+       0x65, 0x13,         /*          Unit (Inch),                */
+       0x34,               /*          Physical Minimum (0),       */
+       0x09, 0x30,         /*          Usage (X),                  */
+       0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
+       0x27, 0x3F, 0x9C,
+               0x00, 0x00, /*          Logical Maximum (39999),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0x09, 0x31,         /*          Usage (Y),                  */
+       0x46, 0x6A, 0x18,   /*          Physical Maximum (6250),    */
+       0x26, 0xA7, 0x61,   /*          Logical Maximum (24999),    */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xB4,               /*          Pop,                        */
+       0x09, 0x30,         /*          Usage (Tip Pressure),       */
+       0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
+       0x81, 0x02,         /*          Input (Variable),           */
+       0xC0,               /*      End Collection,                 */
+       0xC0                /*  End Collection                      */
+};
+
+/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
+static __u8 twha60_rdesc_fixed1[] = {
+       0x05, 0x01, /*  Usage Page (Desktop),       */
+       0x09, 0x06, /*  Usage (Keyboard),           */
+       0xA1, 0x01, /*  Collection (Application),   */
+       0x85, 0x05, /*      Report ID (5),          */
+       0x05, 0x07, /*      Usage Page (Keyboard),  */
+       0x14,       /*      Logical Minimum (0),    */
+       0x25, 0x01, /*      Logical Maximum (1),    */
+       0x75, 0x01, /*      Report Size (1),        */
+       0x95, 0x08, /*      Report Count (8),       */
+       0x81, 0x01, /*      Input (Constant),       */
+       0x95, 0x0C, /*      Report Count (12),      */
+       0x19, 0x3A, /*      Usage Minimum (KB F1),  */
+       0x29, 0x45, /*      Usage Maximum (KB F12), */
+       0x81, 0x02, /*      Input (Variable),       */
+       0x95, 0x0C, /*      Report Count (12),      */
+       0x19, 0x68, /*      Usage Minimum (KB F13), */
+       0x29, 0x73, /*      Usage Maximum (KB F24), */
+       0x81, 0x02, /*      Input (Variable),       */
+       0x95, 0x08, /*      Report Count (8),       */
+       0x81, 0x01, /*      Input (Constant),       */
+       0xC0        /*  End Collection              */
+};
+
 static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                                        unsigned int *rsize)
 {
@@ -525,6 +605,22 @@ static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                        break;
                }
                break;
+       case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
+               switch (iface_num) {
+               case 0:
+                       if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
+                               rdesc = twha60_rdesc_fixed0;
+                               *rsize = sizeof(twha60_rdesc_fixed0);
+                       }
+                       break;
+               case 1:
+                       if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
+                               rdesc = twha60_rdesc_fixed1;
+                               *rsize = sizeof(twha60_rdesc_fixed1);
+                       }
+                       break;
+               }
+               break;
        }
 
        return rdesc;
@@ -543,6 +639,8 @@ static const struct hid_device_id uclogic_devices[] = {
                                USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
        { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
                                USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
+                               USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
        { }
 };
 MODULE_DEVICE_TABLE(hid, uclogic_devices);
index fe23a1e..2f60da9 100644 (file)
@@ -5,7 +5,6 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
  *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
  *  Copyright (c) 2006-2007 Jiri Kosina
- *  Copyright (c) 2007 Paul Walmsley
  *  Copyright (c) 2008 Jiri Slaby <jirislaby@gmail.com>
  *  Copyright (c) 2006 Andrew Zabolotny <zap@homelink.ru>
  *  Copyright (c) 2009 Bastien Nocera <hadess@hadess.net>
@@ -33,6 +32,8 @@
 #define PAD_DEVICE_ID  0x0F
 
 #define WAC_CMD_LED_CONTROL     0x20
+#define WAC_CMD_ICON_START_STOP     0x21
+#define WAC_CMD_ICON_TRANSFER       0x26
 
 struct wacom_data {
        __u16 tool;
@@ -69,6 +70,91 @@ static enum power_supply_property wacom_ac_props[] = {
        POWER_SUPPLY_PROP_SCOPE,
 };
 
+static void wacom_scramble(__u8 *image)
+{
+       __u16 mask;
+       __u16 s1;
+       __u16 s2;
+       __u16 r1 ;
+       __u16 r2 ;
+       __u16 r;
+       __u8 buf[256];
+       int i, w, x, y, z;
+
+       for (x = 0; x < 32; x++) {
+               for (y = 0; y < 8; y++)
+                       buf[(8 * x) + (7 - y)] = image[(8 * x) + y];
+       }
+
+       /* Change 76543210 into GECA6420 as required by Intuos4 WL
+        *        HGFEDCBA      HFDB7531
+        */
+       for (x = 0; x < 4; x++) {
+               for (y = 0; y < 4; y++) {
+                       for (z = 0; z < 8; z++) {
+                               mask = 0x0001;
+                               r1 = 0;
+                               r2 = 0;
+                               i = (x << 6) + (y << 4) + z;
+                               s1 = buf[i];
+                               s2 = buf[i+8];
+                               for (w = 0; w < 8; w++) {
+                                       r1 |= (s1 & mask);
+                                       r2 |= (s2 & mask);
+                                       s1 <<= 1;
+                                       s2 <<= 1;
+                                       mask <<= 2;
+                               }
+                               r = r1 | (r2 << 1);
+                               i = (x << 6) + (y << 4) + (z << 1);
+                               image[i] = 0xFF & r;
+                               image[i+1] = (0xFF00 & r) >> 8;
+                       }
+               }
+       }
+}
+
+static void wacom_set_image(struct hid_device *hdev, const char *image,
+                                               __u8 icon_no)
+{
+       __u8 rep_data[68];
+       __u8 p[256];
+       int ret, i, j;
+
+       for (i = 0; i < 256; i++)
+               p[i] = image[i];
+
+       rep_data[0] = WAC_CMD_ICON_START_STOP;
+       rep_data[1] = 0;
+       ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+                               HID_FEATURE_REPORT);
+       if (ret < 0)
+               goto err;
+
+       rep_data[0] = WAC_CMD_ICON_TRANSFER;
+       rep_data[1] = icon_no & 0x07;
+
+       wacom_scramble(p);
+
+       for (i = 0; i < 4; i++) {
+               for (j = 0; j < 64; j++)
+                       rep_data[j + 3] = p[(i << 6) + j];
+
+               rep_data[2] = i;
+               ret = hdev->hid_output_raw_report(hdev, rep_data, 67,
+                                       HID_FEATURE_REPORT);
+       }
+
+       rep_data[0] = WAC_CMD_ICON_START_STOP;
+       rep_data[1] = 0;
+
+       ret = hdev->hid_output_raw_report(hdev, rep_data, 2,
+                               HID_FEATURE_REPORT);
+
+err:
+       return;
+}
+
 static void wacom_leds_set_brightness(struct led_classdev *led_dev,
                                                enum led_brightness value)
 {
@@ -91,7 +177,10 @@ static void wacom_leds_set_brightness(struct led_classdev *led_dev,
        if (buf) {
                buf[0] = WAC_CMD_LED_CONTROL;
                buf[1] = led;
-               buf[2] = value;
+               buf[2] = value >> 2;
+               buf[3] = value;
+               /* use fixed brightness for OLEDs */
+               buf[4] = 0x08;
                hdev->hid_output_raw_report(hdev, buf, 9, HID_FEATURE_REPORT);
                kfree(buf);
        }
@@ -317,6 +406,34 @@ static ssize_t wacom_store_speed(struct device *dev,
 static DEVICE_ATTR(speed, S_IRUGO | S_IWUSR | S_IWGRP,
                wacom_show_speed, wacom_store_speed);
 
+#define WACOM_STORE(OLED_ID)                                           \
+static ssize_t wacom_oled##OLED_ID##_store(struct device *dev,         \
+                               struct device_attribute *attr,          \
+                               const char *buf, size_t count)          \
+{                                                                      \
+       struct hid_device *hdev = container_of(dev, struct hid_device,  \
+                               dev);                                   \
+                                                                       \
+       if (count != 256)                                               \
+               return -EINVAL;                                         \
+                                                                       \
+       wacom_set_image(hdev, buf, OLED_ID);                            \
+                                                                       \
+       return count;                                                   \
+}                                                                      \
+                                                                       \
+static DEVICE_ATTR(oled##OLED_ID##_img, S_IWUSR | S_IWGRP, NULL,       \
+                               wacom_oled##OLED_ID##_store)
+
+WACOM_STORE(0);
+WACOM_STORE(1);
+WACOM_STORE(2);
+WACOM_STORE(3);
+WACOM_STORE(4);
+WACOM_STORE(5);
+WACOM_STORE(6);
+WACOM_STORE(7);
+
 static int wacom_gr_parse_report(struct hid_device *hdev,
                        struct wacom_data *wdata,
                        struct input_dev *input, unsigned char *data)
@@ -717,17 +834,33 @@ static int wacom_probe(struct hid_device *hdev,
                hid_warn(hdev,
                         "can't create sysfs speed attribute err: %d\n", ret);
 
+#define OLED_INIT(OLED_ID)                                             \
+       do {                                                            \
+               ret = device_create_file(&hdev->dev,                    \
+                               &dev_attr_oled##OLED_ID##_img);         \
+               if (ret)                                                \
+                       hid_warn(hdev,                                  \
+                        "can't create sysfs oled attribute, err: %d\n", ret);\
+       } while (0)
+
+OLED_INIT(0);
+OLED_INIT(1);
+OLED_INIT(2);
+OLED_INIT(3);
+OLED_INIT(4);
+OLED_INIT(5);
+OLED_INIT(6);
+OLED_INIT(7);
+
        wdata->features = 0;
        wacom_set_features(hdev, 1);
 
        if (hdev->product == USB_DEVICE_ID_WACOM_INTUOS4_BLUETOOTH) {
                sprintf(hdev->name, "%s", "Wacom Intuos4 WL");
                ret = wacom_initialize_leds(hdev);
-               if (ret) {
+               if (ret)
                        hid_warn(hdev,
                                 "can't create led attribute, err: %d\n", ret);
-                       goto destroy_leds;
-               }
        }
 
        wdata->battery.properties = wacom_battery_props;
@@ -740,8 +873,8 @@ static int wacom_probe(struct hid_device *hdev,
 
        ret = power_supply_register(&hdev->dev, &wdata->battery);
        if (ret) {
-               hid_warn(hdev, "can't create sysfs battery attribute, err: %d\n",
-                        ret);
+               hid_err(hdev, "can't create sysfs battery attribute, err: %d\n",
+                       ret);
                goto err_battery;
        }
 
@@ -756,8 +889,8 @@ static int wacom_probe(struct hid_device *hdev,
 
        ret = power_supply_register(&hdev->dev, &wdata->ac);
        if (ret) {
-               hid_warn(hdev,
-                        "can't create ac battery attribute, err: %d\n", ret);
+               hid_err(hdev,
+                       "can't create ac battery attribute, err: %d\n", ret);
                goto err_ac;
        }
 
@@ -767,10 +900,17 @@ static int wacom_probe(struct hid_device *hdev,
 err_ac:
        power_supply_unregister(&wdata->battery);
 err_battery:
+       wacom_destroy_leds(hdev);
+       device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled7_img);
        device_remove_file(&hdev->dev, &dev_attr_speed);
        hid_hw_stop(hdev);
-destroy_leds:
-       wacom_destroy_leds(hdev);
 err_free:
        kfree(wdata);
        return ret;
@@ -781,6 +921,14 @@ static void wacom_remove(struct hid_device *hdev)
        struct wacom_data *wdata = hid_get_drvdata(hdev);
 
        wacom_destroy_leds(hdev);
+       device_remove_file(&hdev->dev, &dev_attr_oled0_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled1_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled2_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled3_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled4_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled5_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled6_img);
+       device_remove_file(&hdev->dev, &dev_attr_oled7_img);
        device_remove_file(&hdev->dev, &dev_attr_speed);
        hid_hw_stop(hdev);
 
index 745e4e9..bb536ab 100644 (file)
@@ -638,28 +638,6 @@ static __u8 sirius_battery_free_tablet_rdesc_fixed[] = {
        0xC0                /*  End Collection                      */
 };
 
-static int waltop_probe(struct hid_device *hdev,
-                       const struct hid_device_id *id)
-{
-       int ret;
-
-       ret = hid_parse(hdev);
-       if (ret) {
-               hid_err(hdev, "parse failed\n");
-               goto err;
-       }
-
-       ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
-       if (ret) {
-               hid_err(hdev, "hw start failed\n");
-               goto err;
-       }
-
-       return 0;
-err:
-       return ret;
-}
-
 static __u8 *waltop_report_fixup(struct hid_device *hdev, __u8 *rdesc,
                unsigned int *rsize)
 {
@@ -776,11 +754,6 @@ static int waltop_raw_event(struct hid_device *hdev, struct hid_report *report,
        return 0;
 }
 
-static void waltop_remove(struct hid_device *hdev)
-{
-       hid_hw_stop(hdev);
-}
-
 static const struct hid_device_id waltop_devices[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WALTOP,
                                USB_DEVICE_ID_WALTOP_SLIM_TABLET_5_8_INCH) },
@@ -803,10 +776,8 @@ MODULE_DEVICE_TABLE(hid, waltop_devices);
 static struct hid_driver waltop_driver = {
        .name = "waltop",
        .id_table = waltop_devices,
-       .probe = waltop_probe,
        .report_fixup = waltop_report_fixup,
        .raw_event = waltop_raw_event,
-       .remove = waltop_remove,
 };
 
 static int __init waltop_init(void)
index 0a1805c..bc85bf2 100644 (file)
@@ -28,12 +28,14 @@ struct wiimote_ext {
        bool mp_plugged;
        bool motionp;
        __u8 ext_type;
+       __u16 calib[4][3];
 };
 
 enum wiiext_type {
        WIIEXT_NONE,            /* placeholder */
        WIIEXT_CLASSIC,         /* Nintendo classic controller */
        WIIEXT_NUNCHUCK,        /* Nintendo nunchuck controller */
+       WIIEXT_BALANCE_BOARD,   /* Nintendo balance board controller */
 };
 
 enum wiiext_keys {
@@ -126,6 +128,7 @@ error:
 static __u8 ext_read(struct wiimote_ext *ext)
 {
        ssize_t ret;
+       __u8 buf[24], i, j, offs = 0;
        __u8 rmem[2], wmem;
        __u8 type = WIIEXT_NONE;
 
@@ -151,6 +154,28 @@ static __u8 ext_read(struct wiimote_ext *ext)
                        type = WIIEXT_NUNCHUCK;
                else if (rmem[0] == 0x01 && rmem[1] == 0x01)
                        type = WIIEXT_CLASSIC;
+               else if (rmem[0] == 0x04 && rmem[1] == 0x02)
+                       type = WIIEXT_BALANCE_BOARD;
+       }
+
+       /* get balance board calibration data */
+       if (type == WIIEXT_BALANCE_BOARD) {
+               ret = wiimote_cmd_read(ext->wdata, 0xa40024, buf, 12);
+               ret += wiimote_cmd_read(ext->wdata, 0xa40024 + 12,
+                                       buf + 12, 12);
+
+               if (ret != 24) {
+                       type = WIIEXT_NONE;
+               } else {
+                       for (i = 0; i < 3; i++) {
+                               for (j = 0; j < 4; j++) {
+                                       ext->calib[j][i] = buf[offs];
+                                       ext->calib[j][i] <<= 8;
+                                       ext->calib[j][i] |= buf[offs + 1];
+                                       offs += 2;
+                               }
+                       }
+               }
        }
 
        wiimote_cmd_release(ext->wdata);
@@ -509,6 +534,71 @@ static void handler_classic(struct wiimote_ext *ext, const __u8 *payload)
        input_sync(ext->input);
 }
 
+static void handler_balance_board(struct wiimote_ext *ext, const __u8 *payload)
+{
+       __s32 val[4], tmp;
+       unsigned int i;
+
+       /*   Byte |  8  7  6  5  4  3  2  1  |
+        *   -----+--------------------------+
+        *    1   |    Top Right <15:8>      |
+        *    2   |    Top Right  <7:0>      |
+        *   -----+--------------------------+
+        *    3   | Bottom Right <15:8>      |
+        *    4   | Bottom Right  <7:0>      |
+        *   -----+--------------------------+
+        *    5   |     Top Left <15:8>      |
+        *    6   |     Top Left  <7:0>      |
+        *   -----+--------------------------+
+        *    7   |  Bottom Left <15:8>      |
+        *    8   |  Bottom Left  <7:0>      |
+        *   -----+--------------------------+
+        *
+        * These values represent the weight-measurements of the Wii-balance
+        * board with 16bit precision.
+        *
+        * The balance-board is never reported interleaved with motionp.
+        */
+
+       val[0] = payload[0];
+       val[0] <<= 8;
+       val[0] |= payload[1];
+
+       val[1] = payload[2];
+       val[1] <<= 8;
+       val[1] |= payload[3];
+
+       val[2] = payload[4];
+       val[2] <<= 8;
+       val[2] |= payload[5];
+
+       val[3] = payload[6];
+       val[3] <<= 8;
+       val[3] |= payload[7];
+
+       /* apply calibration data */
+       for (i = 0; i < 4; i++) {
+               if (val[i] < ext->calib[i][1]) {
+                       tmp = val[i] - ext->calib[i][0];
+                       tmp *= 1700;
+                       tmp /= ext->calib[i][1] - ext->calib[i][0];
+               } else {
+                       tmp = val[i] - ext->calib[i][1];
+                       tmp *= 1700;
+                       tmp /= ext->calib[i][2] - ext->calib[i][1];
+                       tmp += 1700;
+               }
+               val[i] = tmp;
+       }
+
+       input_report_abs(ext->input, ABS_HAT0X, val[0]);
+       input_report_abs(ext->input, ABS_HAT0Y, val[1]);
+       input_report_abs(ext->input, ABS_HAT1X, val[2]);
+       input_report_abs(ext->input, ABS_HAT1Y, val[3]);
+
+       input_sync(ext->input);
+}
+
 /* call this with state.lock spinlock held */
 void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
 {
@@ -523,6 +613,8 @@ void wiiext_handle(struct wiimote_data *wdata, const __u8 *payload)
                handler_nunchuck(ext, payload);
        } else if (ext->ext_type == WIIEXT_CLASSIC) {
                handler_classic(ext, payload);
+       } else if (ext->ext_type == WIIEXT_BALANCE_BOARD) {
+               handler_balance_board(ext, payload);
        }
 }
 
@@ -551,6 +643,11 @@ static ssize_t wiiext_show(struct device *dev, struct device_attribute *attr,
                        return sprintf(buf, "motionp+classic\n");
                else
                        return sprintf(buf, "classic\n");
+       } else if (type == WIIEXT_BALANCE_BOARD) {
+               if (motionp)
+                       return sprintf(buf, "motionp+balanceboard\n");
+               else
+                       return sprintf(buf, "balanceboard\n");
        } else {
                if (motionp)
                        return sprintf(buf, "motionp\n");
index 3b6f7bf..17d15bb 100644 (file)
@@ -42,6 +42,7 @@ static struct cdev hidraw_cdev;
 static struct class *hidraw_class;
 static struct hidraw *hidraw_table[HIDRAW_MAX_DEVICES];
 static DEFINE_MUTEX(minors_lock);
+static void drop_ref(struct hidraw *hid, int exists_bit);
 
 static ssize_t hidraw_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
 {
@@ -113,7 +114,7 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
        __u8 *buf;
        int ret = 0;
 
-       if (!hidraw_table[minor]) {
+       if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
                ret = -ENODEV;
                goto out;
        }
@@ -261,7 +262,7 @@ static int hidraw_open(struct inode *inode, struct file *file)
        }
 
        mutex_lock(&minors_lock);
-       if (!hidraw_table[minor]) {
+       if (!hidraw_table[minor] || !hidraw_table[minor]->exist) {
                err = -ENODEV;
                goto out_unlock;
        }
@@ -298,36 +299,12 @@ out:
 static int hidraw_release(struct inode * inode, struct file * file)
 {
        unsigned int minor = iminor(inode);
-       struct hidraw *dev;
        struct hidraw_list *list = file->private_data;
-       int ret;
-       int i;
-
-       mutex_lock(&minors_lock);
-       if (!hidraw_table[minor]) {
-               ret = -ENODEV;
-               goto unlock;
-       }
 
+       drop_ref(hidraw_table[minor], 0);
        list_del(&list->node);
-       dev = hidraw_table[minor];
-       if (!--dev->open) {
-               if (list->hidraw->exist) {
-                       hid_hw_power(dev->hid, PM_HINT_NORMAL);
-                       hid_hw_close(dev->hid);
-               } else {
-                       kfree(list->hidraw);
-               }
-       }
-
-       for (i = 0; i < HIDRAW_BUFFER_SIZE; ++i)
-               kfree(list->buffer[i].value);
        kfree(list);
-       ret = 0;
-unlock:
-       mutex_unlock(&minors_lock);
-
-       return ret;
+       return 0;
 }
 
 static long hidraw_ioctl(struct file *file, unsigned int cmd,
@@ -529,21 +506,7 @@ EXPORT_SYMBOL_GPL(hidraw_connect);
 void hidraw_disconnect(struct hid_device *hid)
 {
        struct hidraw *hidraw = hid->hidraw;
-
-       mutex_lock(&minors_lock);
-       hidraw->exist = 0;
-
-       device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
-
-       hidraw_table[hidraw->minor] = NULL;
-
-       if (hidraw->open) {
-               hid_hw_close(hid);
-               wake_up_interruptible(&hidraw->wait);
-       } else {
-               kfree(hidraw);
-       }
-       mutex_unlock(&minors_lock);
+       drop_ref(hidraw, 1);
 }
 EXPORT_SYMBOL_GPL(hidraw_disconnect);
 
@@ -559,21 +522,28 @@ int __init hidraw_init(void)
 
        if (result < 0) {
                pr_warn("can't get major number\n");
-               result = 0;
                goto out;
        }
 
        hidraw_class = class_create(THIS_MODULE, "hidraw");
        if (IS_ERR(hidraw_class)) {
                result = PTR_ERR(hidraw_class);
-               unregister_chrdev(hidraw_major, "hidraw");
-               goto out;
+               goto error_cdev;
        }
 
         cdev_init(&hidraw_cdev, &hidraw_ops);
-        cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+       result = cdev_add(&hidraw_cdev, dev_id, HIDRAW_MAX_DEVICES);
+       if (result < 0)
+               goto error_class;
+
 out:
        return result;
+
+error_class:
+       class_destroy(hidraw_class);
+error_cdev:
+       unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
+       goto out;
 }
 
 void hidraw_exit(void)
@@ -585,3 +555,23 @@ void hidraw_exit(void)
        unregister_chrdev_region(dev_id, HIDRAW_MAX_DEVICES);
 
 }
+
+static void drop_ref(struct hidraw *hidraw, int exists_bit)
+{
+       mutex_lock(&minors_lock);
+       if (exists_bit) {
+               hid_hw_close(hidraw->hid);
+               hidraw->exist = 0;
+               if (hidraw->open)
+                       wake_up_interruptible(&hidraw->wait);
+       } else {
+               --hidraw->open;
+       }
+
+       if (!hidraw->open && !hidraw->exist) {
+               device_destroy(hidraw_class, MKDEV(hidraw_major, hidraw->minor));
+               hidraw_table[hidraw->minor] = NULL;
+               kfree(hidraw);
+       }
+       mutex_unlock(&minors_lock);
+}
index dedd8e4..8e0c4bf 100644 (file)
@@ -1415,20 +1415,20 @@ static int hid_post_reset(struct usb_interface *intf)
         * configuration descriptors passed, we already know that
         * the size of the HID report descriptor has not changed.
         */
-       rdesc = kmalloc(hid->rsize, GFP_KERNEL);
+       rdesc = kmalloc(hid->dev_rsize, GFP_KERNEL);
        if (!rdesc) {
                dbg_hid("couldn't allocate rdesc memory (post_reset)\n");
                return 1;
        }
        status = hid_get_class_descriptor(dev,
                                interface->desc.bInterfaceNumber,
-                               HID_DT_REPORT, rdesc, hid->rsize);
+                               HID_DT_REPORT, rdesc, hid->dev_rsize);
        if (status < 0) {
                dbg_hid("reading report descriptor failed (post_reset)\n");
                kfree(rdesc);
                return 1;
        }
-       status = memcmp(rdesc, hid->rdesc, hid->rsize);
+       status = memcmp(rdesc, hid->dev_rdesc, hid->dev_rsize);
        kfree(rdesc);
        if (status != 0) {
                dbg_hid("report descriptor changed\n");
index 991e85c..11c7932 100644 (file)
@@ -70,12 +70,13 @@ static const struct hid_blacklist {
        { USB_VENDOR_ID_CH, USB_DEVICE_ID_CH_AXIS_295, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_DMI, USB_DEVICE_ID_DMI_ENC, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_ELO, USB_DEVICE_ID_ELO_TS2700, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_FREESCALE, USB_DEVICE_ID_FREESCALE_MX28, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN1, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PIXART, USB_DEVICE_ID_PIXART_OPTICAL_TOUCH_SCREEN2, HID_QUIRK_NO_INIT_REPORTS },
        { USB_VENDOR_ID_PRODIGE, USB_DEVICE_ID_PRODIGE_CORDLESS, HID_QUIRK_NOGET },
-       { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN, HID_QUIRK_NOGET },
+       { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3001, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH_3008, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SENNHEISER, USB_DEVICE_ID_SENNHEISER_BTD500USB, HID_QUIRK_NOGET },
        { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
index 6c58bff..118d030 100644 (file)
@@ -54,16 +54,9 @@ struct evdev_client {
 static struct evdev *evdev_table[EVDEV_MINORS];
 static DEFINE_MUTEX(evdev_table_mutex);
 
-static void evdev_pass_event(struct evdev_client *client,
-                            struct input_event *event,
-                            ktime_t mono, ktime_t real)
+static void __pass_event(struct evdev_client *client,
+                        const struct input_event *event)
 {
-       event->time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
-                                       mono : real);
-
-       /* Interrupts are disabled, just acquire the lock. */
-       spin_lock(&client->buffer_lock);
-
        client->buffer[client->head++] = *event;
        client->head &= client->bufsize - 1;
 
@@ -86,42 +79,74 @@ static void evdev_pass_event(struct evdev_client *client,
                client->packet_head = client->head;
                kill_fasync(&client->fasync, SIGIO, POLL_IN);
        }
+}
+
+static void evdev_pass_values(struct evdev_client *client,
+                       const struct input_value *vals, unsigned int count,
+                       ktime_t mono, ktime_t real)
+{
+       struct evdev *evdev = client->evdev;
+       const struct input_value *v;
+       struct input_event event;
+       bool wakeup = false;
+
+       event.time = ktime_to_timeval(client->clkid == CLOCK_MONOTONIC ?
+                                     mono : real);
+
+       /* Interrupts are disabled, just acquire the lock. */
+       spin_lock(&client->buffer_lock);
+
+       for (v = vals; v != vals + count; v++) {
+               event.type = v->type;
+               event.code = v->code;
+               event.value = v->value;
+               __pass_event(client, &event);
+               if (v->type == EV_SYN && v->code == SYN_REPORT)
+                       wakeup = true;
+       }
 
        spin_unlock(&client->buffer_lock);
+
+       if (wakeup)
+               wake_up_interruptible(&evdev->wait);
 }
 
 /*
- * Pass incoming event to all connected clients.
+ * Pass incoming events to all connected clients.
  */
-static void evdev_event(struct input_handle *handle,
-                       unsigned int type, unsigned int code, int value)
+static void evdev_events(struct input_handle *handle,
+                        const struct input_value *vals, unsigned int count)
 {
        struct evdev *evdev = handle->private;
        struct evdev_client *client;
-       struct input_event event;
        ktime_t time_mono, time_real;
 
        time_mono = ktime_get();
        time_real = ktime_sub(time_mono, ktime_get_monotonic_offset());
 
-       event.type = type;
-       event.code = code;
-       event.value = value;
-
        rcu_read_lock();
 
        client = rcu_dereference(evdev->grab);
 
        if (client)
-               evdev_pass_event(client, &event, time_mono, time_real);
+               evdev_pass_values(client, vals, count, time_mono, time_real);
        else
                list_for_each_entry_rcu(client, &evdev->client_list, node)
-                       evdev_pass_event(client, &event, time_mono, time_real);
+                       evdev_pass_values(client, vals, count,
+                                         time_mono, time_real);
 
        rcu_read_unlock();
+}
 
-       if (type == EV_SYN && code == SYN_REPORT)
-               wake_up_interruptible(&evdev->wait);
+/*
+ * Pass incoming event to all connected clients.
+ */
+static void evdev_event(struct input_handle *handle,
+                       unsigned int type, unsigned int code, int value)
+{
+       struct input_value vals[] = { { type, code, value } };
+
+       evdev_events(handle, vals, 1);
 }
 
 static int evdev_fasync(int fd, struct file *file, int on)
@@ -653,20 +678,22 @@ static int evdev_handle_mt_request(struct input_dev *dev,
                                   unsigned int size,
                                   int __user *ip)
 {
-       const struct input_mt_slot *mt = dev->mt;
+       const struct input_mt *mt = dev->mt;
        unsigned int code;
        int max_slots;
        int i;
 
        if (get_user(code, &ip[0]))
                return -EFAULT;
-       if (!input_is_mt_value(code))
+       if (!mt || !input_is_mt_value(code))
                return -EINVAL;
 
        max_slots = (size - sizeof(__u32)) / sizeof(__s32);
-       for (i = 0; i < dev->mtsize && i < max_slots; i++)
-               if (put_user(input_mt_get_value(&mt[i], code), &ip[1 + i]))
+       for (i = 0; i < mt->num_slots && i < max_slots; i++) {
+               int value = input_mt_get_value(&mt->slots[i], code);
+               if (put_user(value, &ip[1 + i]))
                        return -EFAULT;
+       }
 
        return 0;
 }
@@ -1048,6 +1075,7 @@ MODULE_DEVICE_TABLE(input, evdev_ids);
 
 static struct input_handler evdev_handler = {
        .event          = evdev_event,
+       .events         = evdev_events,
        .connect        = evdev_connect,
        .disconnect     = evdev_disconnect,
        .fops           = &evdev_fops,
index 70a16c7..c0ec7d4 100644 (file)
 
 #define TRKID_SGN      ((TRKID_MAX + 1) >> 1)
 
+static void copy_abs(struct input_dev *dev, unsigned int dst, unsigned int src)
+{
+       if (dev->absinfo && test_bit(src, dev->absbit)) {
+               dev->absinfo[dst] = dev->absinfo[src];
+               dev->absbit[BIT_WORD(dst)] |= BIT_MASK(dst);
+       }
+}
+
 /**
  * input_mt_init_slots() - initialize MT input slots
  * @dev: input device supporting MT events and finger tracking
  * May be called repeatedly. Returns -EINVAL if attempting to
  * reinitialize with a different number of slots.
  */
-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots)
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+                       unsigned int flags)
 {
+       struct input_mt *mt = dev->mt;
        int i;
 
        if (!num_slots)
                return 0;
-       if (dev->mt)
-               return dev->mtsize != num_slots ? -EINVAL : 0;
+       if (mt)
+               return mt->num_slots != num_slots ? -EINVAL : 0;
 
-       dev->mt = kcalloc(num_slots, sizeof(struct input_mt_slot), GFP_KERNEL);
-       if (!dev->mt)
-               return -ENOMEM;
+       mt = kzalloc(sizeof(*mt) + num_slots * sizeof(*mt->slots), GFP_KERNEL);
+       if (!mt)
+               goto err_mem;
 
-       dev->mtsize = num_slots;
+       mt->num_slots = num_slots;
+       mt->flags = flags;
        input_set_abs_params(dev, ABS_MT_SLOT, 0, num_slots - 1, 0, 0);
        input_set_abs_params(dev, ABS_MT_TRACKING_ID, 0, TRKID_MAX, 0, 0);
-       input_set_events_per_packet(dev, 6 * num_slots);
+
+       if (flags & (INPUT_MT_POINTER | INPUT_MT_DIRECT)) {
+               __set_bit(EV_KEY, dev->evbit);
+               __set_bit(BTN_TOUCH, dev->keybit);
+
+               copy_abs(dev, ABS_X, ABS_MT_POSITION_X);
+               copy_abs(dev, ABS_Y, ABS_MT_POSITION_Y);
+               copy_abs(dev, ABS_PRESSURE, ABS_MT_PRESSURE);
+       }
+       if (flags & INPUT_MT_POINTER) {
+               __set_bit(BTN_TOOL_FINGER, dev->keybit);
+               __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+               if (num_slots >= 3)
+                       __set_bit(BTN_TOOL_TRIPLETAP, dev->keybit);
+               if (num_slots >= 4)
+                       __set_bit(BTN_TOOL_QUADTAP, dev->keybit);
+               if (num_slots >= 5)
+                       __set_bit(BTN_TOOL_QUINTTAP, dev->keybit);
+               __set_bit(INPUT_PROP_POINTER, dev->propbit);
+       }
+       if (flags & INPUT_MT_DIRECT)
+               __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+       if (flags & INPUT_MT_TRACK) {
+               unsigned int n2 = num_slots * num_slots;
+               mt->red = kcalloc(n2, sizeof(*mt->red), GFP_KERNEL);
+               if (!mt->red)
+                       goto err_mem;
+       }
 
        /* Mark slots as 'unused' */
        for (i = 0; i < num_slots; i++)
-               input_mt_set_value(&dev->mt[i], ABS_MT_TRACKING_ID, -1);
+               input_mt_set_value(&mt->slots[i], ABS_MT_TRACKING_ID, -1);
 
+       dev->mt = mt;
        return 0;
+err_mem:
+       kfree(mt);
+       return -ENOMEM;
 }
 EXPORT_SYMBOL(input_mt_init_slots);
 
@@ -60,11 +102,11 @@ EXPORT_SYMBOL(input_mt_init_slots);
  */
 void input_mt_destroy_slots(struct input_dev *dev)
 {
-       kfree(dev->mt);
+       if (dev->mt) {
+               kfree(dev->mt->red);
+               kfree(dev->mt);
+       }
        dev->mt = NULL;
-       dev->mtsize = 0;
-       dev->slot = 0;
-       dev->trkid = 0;
 }
 EXPORT_SYMBOL(input_mt_destroy_slots);
 
@@ -83,18 +125,24 @@ EXPORT_SYMBOL(input_mt_destroy_slots);
 void input_mt_report_slot_state(struct input_dev *dev,
                                unsigned int tool_type, bool active)
 {
-       struct input_mt_slot *mt;
+       struct input_mt *mt = dev->mt;
+       struct input_mt_slot *slot;
        int id;
 
-       if (!dev->mt || !active) {
+       if (!mt)
+               return;
+
+       slot = &mt->slots[mt->slot];
+       slot->frame = mt->frame;
+
+       if (!active) {
                input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
                return;
        }
 
-       mt = &dev->mt[dev->slot];
-       id = input_mt_get_value(mt, ABS_MT_TRACKING_ID);
-       if (id < 0 || input_mt_get_value(mt, ABS_MT_TOOL_TYPE) != tool_type)
-               id = input_mt_new_trkid(dev);
+       id = input_mt_get_value(slot, ABS_MT_TRACKING_ID);
+       if (id < 0 || input_mt_get_value(slot, ABS_MT_TOOL_TYPE) != tool_type)
+               id = input_mt_new_trkid(mt);
 
        input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, id);
        input_event(dev, EV_ABS, ABS_MT_TOOL_TYPE, tool_type);
@@ -135,13 +183,19 @@ EXPORT_SYMBOL(input_mt_report_finger_count);
  */
 void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
 {
-       struct input_mt_slot *oldest = NULL;
-       int oldid = dev->trkid;
-       int count = 0;
-       int i;
+       struct input_mt *mt = dev->mt;
+       struct input_mt_slot *oldest;
+       int oldid, count, i;
+
+       if (!mt)
+               return;
+
+       oldest = 0;
+       oldid = mt->trkid;
+       count = 0;
 
-       for (i = 0; i < dev->mtsize; ++i) {
-               struct input_mt_slot *ps = &dev->mt[i];
+       for (i = 0; i < mt->num_slots; ++i) {
+               struct input_mt_slot *ps = &mt->slots[i];
                int id = input_mt_get_value(ps, ABS_MT_TRACKING_ID);
 
                if (id < 0)
@@ -160,13 +214,208 @@ void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
        if (oldest) {
                int x = input_mt_get_value(oldest, ABS_MT_POSITION_X);
                int y = input_mt_get_value(oldest, ABS_MT_POSITION_Y);
-               int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
 
                input_event(dev, EV_ABS, ABS_X, x);
                input_event(dev, EV_ABS, ABS_Y, y);
-               input_event(dev, EV_ABS, ABS_PRESSURE, p);
+
+               if (test_bit(ABS_MT_PRESSURE, dev->absbit)) {
+                       int p = input_mt_get_value(oldest, ABS_MT_PRESSURE);
+                       input_event(dev, EV_ABS, ABS_PRESSURE, p);
+               }
        } else {
-               input_event(dev, EV_ABS, ABS_PRESSURE, 0);
+               if (test_bit(ABS_MT_PRESSURE, dev->absbit))
+                       input_event(dev, EV_ABS, ABS_PRESSURE, 0);
        }
 }
 EXPORT_SYMBOL(input_mt_report_pointer_emulation);
+
+/**
+ * input_mt_sync_frame() - synchronize mt frame
+ * @dev: input device with allocated MT slots
+ *
+ * Close the frame and prepare the internal state for a new one.
+ * Depending on the flags, marks unused slots as inactive and performs
+ * pointer emulation.
+ */
+void input_mt_sync_frame(struct input_dev *dev)
+{
+       struct input_mt *mt = dev->mt;
+       struct input_mt_slot *s;
+
+       if (!mt)
+               return;
+
+       if (mt->flags & INPUT_MT_DROP_UNUSED) {
+               for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+                       if (s->frame == mt->frame)
+                               continue;
+                       input_mt_slot(dev, s - mt->slots);
+                       input_event(dev, EV_ABS, ABS_MT_TRACKING_ID, -1);
+               }
+       }
+
+       input_mt_report_pointer_emulation(dev, (mt->flags & INPUT_MT_POINTER));
+
+       mt->frame++;
+}
+EXPORT_SYMBOL(input_mt_sync_frame);
+
+static int adjust_dual(int *begin, int step, int *end, int eq)
+{
+       int f, *p, s, c;
+
+       if (begin == end)
+               return 0;
+
+       f = *begin;
+       p = begin + step;
+       s = p == end ? f + 1 : *p;
+
+       for (; p != end; p += step)
+               if (*p < f)
+                       s = f, f = *p;
+               else if (*p < s)
+                       s = *p;
+
+       c = (f + s + 1) / 2;
+       if (c == 0 || (c > 0 && !eq))
+               return 0;
+       if (s < 0)
+               c *= 2;
+
+       for (p = begin; p != end; p += step)
+               *p -= c;
+
+       return (c < s && s <= 0) || (f >= 0 && f < c);
+}
+
+static void find_reduced_matrix(int *w, int nr, int nc, int nrc)
+{
+       int i, k, sum;
+
+       for (k = 0; k < nrc; k++) {
+               for (i = 0; i < nr; i++)
+                       adjust_dual(w + i, nr, w + i + nrc, nr <= nc);
+               sum = 0;
+               for (i = 0; i < nrc; i += nr)
+                       sum += adjust_dual(w + i, 1, w + i + nr, nc <= nr);
+               if (!sum)
+                       break;
+       }
+}
+
+static int input_mt_set_matrix(struct input_mt *mt,
+                              const struct input_mt_pos *pos, int num_pos)
+{
+       const struct input_mt_pos *p;
+       struct input_mt_slot *s;
+       int *w = mt->red;
+       int x, y;
+
+       for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+               if (!input_mt_is_active(s))
+                       continue;
+               x = input_mt_get_value(s, ABS_MT_POSITION_X);
+               y = input_mt_get_value(s, ABS_MT_POSITION_Y);
+               for (p = pos; p != pos + num_pos; p++) {
+                       int dx = x - p->x, dy = y - p->y;
+                       *w++ = dx * dx + dy * dy;
+               }
+       }
+
+       return w - mt->red;
+}
+
+static void input_mt_set_slots(struct input_mt *mt,
+                              int *slots, int num_pos)
+{
+       struct input_mt_slot *s;
+       int *w = mt->red, *p;
+
+       for (p = slots; p != slots + num_pos; p++)
+               *p = -1;
+
+       for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+               if (!input_mt_is_active(s))
+                       continue;
+               for (p = slots; p != slots + num_pos; p++)
+                       if (*w++ < 0)
+                               *p = s - mt->slots;
+       }
+
+       for (s = mt->slots; s != mt->slots + mt->num_slots; s++) {
+               if (input_mt_is_active(s))
+                       continue;
+               for (p = slots; p != slots + num_pos; p++)
+                       if (*p < 0) {
+                               *p = s - mt->slots;
+                               break;
+                       }
+       }
+}
+
+/**
+ * input_mt_assign_slots() - perform a best-match assignment
+ * @dev: input device with allocated MT slots
+ * @slots: the slot assignment to be filled
+ * @pos: the position array to match
+ * @num_pos: number of positions
+ *
+ * Performs a best match against the current contacts and returns
+ * the slot assignment list. New contacts are assigned to unused
+ * slots.
+ *
+ * Returns zero on success, or negative error in case of failure.
+ */
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+                         const struct input_mt_pos *pos, int num_pos)
+{
+       struct input_mt *mt = dev->mt;
+       int nrc;
+
+       if (!mt || !mt->red)
+               return -ENXIO;
+       if (num_pos > mt->num_slots)
+               return -EINVAL;
+       if (num_pos < 1)
+               return 0;
+
+       nrc = input_mt_set_matrix(mt, pos, num_pos);
+       find_reduced_matrix(mt->red, num_pos, nrc / num_pos, nrc);
+       input_mt_set_slots(mt, slots, num_pos);
+
+       return 0;
+}
+EXPORT_SYMBOL(input_mt_assign_slots);
+
+/**
+ * input_mt_get_slot_by_key() - return slot matching key
+ * @dev: input device with allocated MT slots
+ * @key: the key of the sought slot
+ *
+ * Returns the slot of the given key, if it exists, otherwise
+ * set the key on the first unused slot and return.
+ *
+ * If no available slot can be found, -1 is returned.
+ */
+int input_mt_get_slot_by_key(struct input_dev *dev, int key)
+{
+       struct input_mt *mt = dev->mt;
+       struct input_mt_slot *s;
+
+       if (!mt)
+               return -1;
+
+       for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+               if (input_mt_is_active(s) && s->key == key)
+                       return s - mt->slots;
+
+       for (s = mt->slots; s != mt->slots + mt->num_slots; s++)
+               if (!input_mt_is_active(s)) {
+                       s->key = key;
+                       return s - mt->slots;
+               }
+
+       return -1;
+}
+EXPORT_SYMBOL(input_mt_get_slot_by_key);
index 8921c61..5244f3d 100644 (file)
@@ -47,6 +47,8 @@ static DEFINE_MUTEX(input_mutex);
 
 static struct input_handler *input_table[8];
 
+static const struct input_value input_value_sync = { EV_SYN, SYN_REPORT, 1 };
+
 static inline int is_event_supported(unsigned int code,
                                     unsigned long *bm, unsigned int max)
 {
@@ -69,42 +71,102 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
        return value;
 }
 
+static void input_start_autorepeat(struct input_dev *dev, int code)
+{
+       if (test_bit(EV_REP, dev->evbit) &&
+           dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
+           dev->timer.data) {
+               dev->repeat_key = code;
+               mod_timer(&dev->timer,
+                         jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
+       }
+}
+
+static void input_stop_autorepeat(struct input_dev *dev)
+{
+       del_timer(&dev->timer);
+}
+
 /*
  * Pass event first through all filters and then, if event has not been
  * filtered out, through all open handles. This function is called with
  * dev->event_lock held and interrupts disabled.
  */
-static void input_pass_event(struct input_dev *dev,
-                            unsigned int type, unsigned int code, int value)
+static unsigned int input_to_handler(struct input_handle *handle,
+                       struct input_value *vals, unsigned int count)
+{
+       struct input_handler *handler = handle->handler;
+       struct input_value *end = vals;
+       struct input_value *v;
+
+       for (v = vals; v != vals + count; v++) {
+               if (handler->filter &&
+                   handler->filter(handle, v->type, v->code, v->value))
+                       continue;
+               if (end != v)
+                       *end = *v;
+               end++;
+       }
+
+       count = end - vals;
+       if (!count)
+               return 0;
+
+       if (handler->events)
+               handler->events(handle, vals, count);
+       else if (handler->event)
+               for (v = vals; v != end; v++)
+                       handler->event(handle, v->type, v->code, v->value);
+
+       return count;
+}
+
+/*
+ * Pass values first through all filters and then, if event has not been
+ * filtered out, through all open handles. This function is called with
+ * dev->event_lock held and interrupts disabled.
+ */
+static void input_pass_values(struct input_dev *dev,
+                             struct input_value *vals, unsigned int count)
 {
-       struct input_handler *handler;
        struct input_handle *handle;
+       struct input_value *v;
+
+       if (!count)
+               return;
 
        rcu_read_lock();
 
        handle = rcu_dereference(dev->grab);
-       if (handle)
-               handle->handler->event(handle, type, code, value);
-       else {
-               bool filtered = false;
-
-               list_for_each_entry_rcu(handle, &dev->h_list, d_node) {
-                       if (!handle->open)
-                               continue;
+       if (handle) {
+               count = input_to_handler(handle, vals, count);
+       } else {
+               list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+                       if (handle->open)
+                               count = input_to_handler(handle, vals, count);
+       }
 
-                       handler = handle->handler;
-                       if (!handler->filter) {
-                               if (filtered)
-                                       break;
+       rcu_read_unlock();
 
-                               handler->event(handle, type, code, value);
+       add_input_randomness(vals->type, vals->code, vals->value);
 
-                       } else if (handler->filter(handle, type, code, value))
-                               filtered = true;
+       /* trigger auto repeat for key events */
+       for (v = vals; v != vals + count; v++) {
+               if (v->type == EV_KEY && v->value != 2) {
+                       if (v->value)
+                               input_start_autorepeat(dev, v->code);
+                       else
+                               input_stop_autorepeat(dev);
                }
        }
+}
 
-       rcu_read_unlock();
+static void input_pass_event(struct input_dev *dev,
+                            unsigned int type, unsigned int code, int value)
+{
+       struct input_value vals[] = { { type, code, value } };
+
+       input_pass_values(dev, vals, ARRAY_SIZE(vals));
 }
 
 /*
@@ -121,18 +183,12 @@ static void input_repeat_key(unsigned long data)
 
        if (test_bit(dev->repeat_key, dev->key) &&
            is_event_supported(dev->repeat_key, dev->keybit, KEY_MAX)) {
+               struct input_value vals[] =  {
+                       { EV_KEY, dev->repeat_key, 2 },
+                       input_value_sync
+               };
 
-               input_pass_event(dev, EV_KEY, dev->repeat_key, 2);
-
-               if (dev->sync) {
-                       /*
-                        * Only send SYN_REPORT if we are not in a middle
-                        * of driver parsing a new hardware packet.
-                        * Otherwise assume that the driver will send
-                        * SYN_REPORT once it's done.
-                        */
-                       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
-               }
+               input_pass_values(dev, vals, ARRAY_SIZE(vals));
 
                if (dev->rep[REP_PERIOD])
                        mod_timer(&dev->timer, jiffies +
@@ -142,30 +198,17 @@ static void input_repeat_key(unsigned long data)
        spin_unlock_irqrestore(&dev->event_lock, flags);
 }
 
-static void input_start_autorepeat(struct input_dev *dev, int code)
-{
-       if (test_bit(EV_REP, dev->evbit) &&
-           dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] &&
-           dev->timer.data) {
-               dev->repeat_key = code;
-               mod_timer(&dev->timer,
-                         jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
-       }
-}
-
-static void input_stop_autorepeat(struct input_dev *dev)
-{
-       del_timer(&dev->timer);
-}
-
 #define INPUT_IGNORE_EVENT     0
 #define INPUT_PASS_TO_HANDLERS 1
 #define INPUT_PASS_TO_DEVICE   2
+#define INPUT_SLOT             4
+#define INPUT_FLUSH            8
 #define INPUT_PASS_TO_ALL      (INPUT_PASS_TO_HANDLERS | INPUT_PASS_TO_DEVICE)
 
 static int input_handle_abs_event(struct input_dev *dev,
                                  unsigned int code, int *pval)
 {
+       struct input_mt *mt = dev->mt;
        bool is_mt_event;
        int *pold;
 
@@ -174,8 +217,8 @@ static int input_handle_abs_event(struct input_dev *dev,
                 * "Stage" the event; we'll flush it later, when we
                 * get actual touch data.
                 */
-               if (*pval >= 0 && *pval < dev->mtsize)
-                       dev->slot = *pval;
+               if (mt && *pval >= 0 && *pval < mt->num_slots)
+                       mt->slot = *pval;
 
                return INPUT_IGNORE_EVENT;
        }
@@ -184,9 +227,8 @@ static int input_handle_abs_event(struct input_dev *dev,
 
        if (!is_mt_event) {
                pold = &dev->absinfo[code].value;
-       } else if (dev->mt) {
-               struct input_mt_slot *mtslot = &dev->mt[dev->slot];
-               pold = &mtslot->abs[code - ABS_MT_FIRST];
+       } else if (mt) {
+               pold = &mt->slots[mt->slot].abs[code - ABS_MT_FIRST];
        } else {
                /*
                 * Bypass filtering for multi-touch events when
@@ -205,16 +247,16 @@ static int input_handle_abs_event(struct input_dev *dev,
        }
 
        /* Flush pending "slot" event */
-       if (is_mt_event && dev->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
-               input_abs_set_val(dev, ABS_MT_SLOT, dev->slot);
-               input_pass_event(dev, EV_ABS, ABS_MT_SLOT, dev->slot);
+       if (is_mt_event && mt && mt->slot != input_abs_get_val(dev, ABS_MT_SLOT)) {
+               input_abs_set_val(dev, ABS_MT_SLOT, mt->slot);
+               return INPUT_PASS_TO_HANDLERS | INPUT_SLOT;
        }
 
        return INPUT_PASS_TO_HANDLERS;
 }
 
-static void input_handle_event(struct input_dev *dev,
-                              unsigned int type, unsigned int code, int value)
+static int input_get_disposition(struct input_dev *dev,
+                         unsigned int type, unsigned int code, int value)
 {
        int disposition = INPUT_IGNORE_EVENT;
 
@@ -227,37 +269,34 @@ static void input_handle_event(struct input_dev *dev,
                        break;
 
                case SYN_REPORT:
-                       if (!dev->sync) {
-                               dev->sync = true;
-                               disposition = INPUT_PASS_TO_HANDLERS;
-                       }
+                       disposition = INPUT_PASS_TO_HANDLERS | INPUT_FLUSH;
                        break;
                case SYN_MT_REPORT:
-                       dev->sync = false;
                        disposition = INPUT_PASS_TO_HANDLERS;
                        break;
                }
                break;
 
        case EV_KEY:
-               if (is_event_supported(code, dev->keybit, KEY_MAX) &&
-                   !!test_bit(code, dev->key) != value) {
+               if (is_event_supported(code, dev->keybit, KEY_MAX)) {
 
-                       if (value != 2) {
-                               __change_bit(code, dev->key);
-                               if (value)
-                                       input_start_autorepeat(dev, code);
-                               else
-                                       input_stop_autorepeat(dev);
+                       /* auto-repeat bypasses state updates */
+                       if (value == 2) {
+                               disposition = INPUT_PASS_TO_HANDLERS;
+                               break;
                        }
 
-                       disposition = INPUT_PASS_TO_HANDLERS;
+                       if (!!test_bit(code, dev->key) != !!value) {
+
+                               __change_bit(code, dev->key);
+                               disposition = INPUT_PASS_TO_HANDLERS;
+                       }
                }
                break;
 
        case EV_SW:
                if (is_event_supported(code, dev->swbit, SW_MAX) &&
-                   !!test_bit(code, dev->sw) != value) {
+                   !!test_bit(code, dev->sw) != !!value) {
 
                        __change_bit(code, dev->sw);
                        disposition = INPUT_PASS_TO_HANDLERS;
@@ -284,7 +323,7 @@ static void input_handle_event(struct input_dev *dev,
 
        case EV_LED:
                if (is_event_supported(code, dev->ledbit, LED_MAX) &&
-                   !!test_bit(code, dev->led) != value) {
+                   !!test_bit(code, dev->led) != !!value) {
 
                        __change_bit(code, dev->led);
                        disposition = INPUT_PASS_TO_ALL;
@@ -317,14 +356,48 @@ static void input_handle_event(struct input_dev *dev,
                break;
        }
 
-       if (disposition != INPUT_IGNORE_EVENT && type != EV_SYN)
-               dev->sync = false;
+       return disposition;
+}
+
+static void input_handle_event(struct input_dev *dev,
+                              unsigned int type, unsigned int code, int value)
+{
+       int disposition;
+
+       disposition = input_get_disposition(dev, type, code, value);
 
        if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
                dev->event(dev, type, code, value);
 
-       if (disposition & INPUT_PASS_TO_HANDLERS)
-               input_pass_event(dev, type, code, value);
+       if (!dev->vals)
+               return;
+
+       if (disposition & INPUT_PASS_TO_HANDLERS) {
+               struct input_value *v;
+
+               if (disposition & INPUT_SLOT) {
+                       v = &dev->vals[dev->num_vals++];
+                       v->type = EV_ABS;
+                       v->code = ABS_MT_SLOT;
+                       v->value = dev->mt->slot;
+               }
+
+               v = &dev->vals[dev->num_vals++];
+               v->type = type;
+               v->code = code;
+               v->value = value;
+       }
+
+       if (disposition & INPUT_FLUSH) {
+               if (dev->num_vals >= 2)
+                       input_pass_values(dev, dev->vals, dev->num_vals);
+               dev->num_vals = 0;
+       } else if (dev->num_vals >= dev->max_vals - 2) {
+               dev->vals[dev->num_vals++] = input_value_sync;
+               input_pass_values(dev, dev->vals, dev->num_vals);
+               dev->num_vals = 0;
+       }
+
 }
 
 /**
@@ -352,7 +425,6 @@ void input_event(struct input_dev *dev,
        if (is_event_supported(type, dev->evbit, EV_MAX)) {
 
                spin_lock_irqsave(&dev->event_lock, flags);
-               add_input_randomness(type, code, value);
                input_handle_event(dev, type, code, value);
                spin_unlock_irqrestore(&dev->event_lock, flags);
        }
@@ -831,10 +903,12 @@ int input_set_keycode(struct input_dev *dev,
        if (test_bit(EV_KEY, dev->evbit) &&
            !is_event_supported(old_keycode, dev->keybit, KEY_MAX) &&
            __test_and_clear_bit(old_keycode, dev->key)) {
+               struct input_value vals[] =  {
+                       { EV_KEY, old_keycode, 0 },
+                       input_value_sync
+               };
 
-               input_pass_event(dev, EV_KEY, old_keycode, 0);
-               if (dev->sync)
-                       input_pass_event(dev, EV_SYN, SYN_REPORT, 1);
+               input_pass_values(dev, vals, ARRAY_SIZE(vals));
        }
 
  out:
@@ -1416,6 +1490,7 @@ static void input_dev_release(struct device *device)
        input_ff_destroy(dev);
        input_mt_destroy_slots(dev);
        kfree(dev->absinfo);
+       kfree(dev->vals);
        kfree(dev);
 
        module_put(THIS_MODULE);
@@ -1751,8 +1826,8 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
        int i;
        unsigned int events;
 
-       if (dev->mtsize) {
-               mt_slots = dev->mtsize;
+       if (dev->mt) {
+               mt_slots = dev->mt->num_slots;
        } else if (test_bit(ABS_MT_TRACKING_ID, dev->absbit)) {
                mt_slots = dev->absinfo[ABS_MT_TRACKING_ID].maximum -
                           dev->absinfo[ABS_MT_TRACKING_ID].minimum + 1,
@@ -1778,6 +1853,9 @@ static unsigned int input_estimate_events_per_packet(struct input_dev *dev)
                if (test_bit(i, dev->relbit))
                        events++;
 
+       /* Make room for KEY and MSC events */
+       events += 7;
+
        return events;
 }
 
@@ -1816,6 +1894,7 @@ int input_register_device(struct input_dev *dev)
 {
        static atomic_t input_no = ATOMIC_INIT(0);
        struct input_handler *handler;
+       unsigned int packet_size;
        const char *path;
        int error;
 
@@ -1828,9 +1907,14 @@ int input_register_device(struct input_dev *dev)
        /* Make sure that bitmasks not mentioned in dev->evbit are clean. */
        input_cleanse_bitmasks(dev);
 
-       if (!dev->hint_events_per_packet)
-               dev->hint_events_per_packet =
-                               input_estimate_events_per_packet(dev);
+       packet_size = input_estimate_events_per_packet(dev);
+       if (dev->hint_events_per_packet < packet_size)
+               dev->hint_events_per_packet = packet_size;
+
+       dev->max_vals = max(dev->hint_events_per_packet, packet_size) + 2;
+       dev->vals = kcalloc(dev->max_vals, sizeof(*dev->vals), GFP_KERNEL);
+       if (!dev->vals)
+               return -ENOMEM;
 
        /*
         * If delay and period are pre-set by the driver, then autorepeating
index 7360568..6b17975 100644 (file)
@@ -405,7 +405,7 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
                        goto exit;
                if (test_bit(ABS_MT_SLOT, dev->absbit)) {
                        int nslot = input_abs_get_max(dev, ABS_MT_SLOT) + 1;
-                       input_mt_init_slots(dev, nslot);
+                       input_mt_init_slots(dev, nslot, 0);
                } else if (test_bit(ABS_MT_POSITION_X, dev->absbit)) {
                        input_set_events_per_packet(dev, 60);
                }
index 4a1347e..cf5af1f 100644 (file)
@@ -1620,7 +1620,7 @@ int alps_init(struct psmouse *psmouse)
        case ALPS_PROTO_V3:
        case ALPS_PROTO_V4:
                set_bit(INPUT_PROP_SEMI_MT, dev1->propbit);
-               input_mt_init_slots(dev1, 2);
+               input_mt_init_slots(dev1, 2, 0);
                input_set_abs_params(dev1, ABS_MT_POSITION_X, 0, ALPS_V3_X_MAX, 0, 0);
                input_set_abs_params(dev1, ABS_MT_POSITION_Y, 0, ALPS_V3_Y_MAX, 0, 0);
 
index d528c23..3a78f23 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/usb/input.h>
 #include <linux/hid.h>
 #include <linux/mutex.h>
+#include <linux/input/mt.h>
 
 #define USB_VENDOR_ID_APPLE            0x05ac
 
@@ -183,26 +184,26 @@ struct tp_finger {
        __le16 abs_y;           /* absolute y coodinate */
        __le16 rel_x;           /* relative x coodinate */
        __le16 rel_y;           /* relative y coodinate */
-       __le16 size_major;      /* finger size, major axis? */
-       __le16 size_minor;      /* finger size, minor axis? */
+       __le16 tool_major;      /* tool area, major axis */
+       __le16 tool_minor;      /* tool area, minor axis */
        __le16 orientation;     /* 16384 when point, else 15 bit angle */
-       __le16 force_major;     /* trackpad force, major axis? */
-       __le16 force_minor;     /* trackpad force, minor axis? */
+       __le16 touch_major;     /* touch area, major axis */
+       __le16 touch_minor;     /* touch area, minor axis */
        __le16 unused[3];       /* zeros */
        __le16 multi;           /* one finger: varies, more fingers: constant */
 } __attribute__((packed,aligned(2)));
 
 /* trackpad finger data size, empirically at least ten fingers */
+#define MAX_FINGERS            16
 #define SIZEOF_FINGER          sizeof(struct tp_finger)
-#define SIZEOF_ALL_FINGERS     (16 * SIZEOF_FINGER)
+#define SIZEOF_ALL_FINGERS     (MAX_FINGERS * SIZEOF_FINGER)
 #define MAX_FINGER_ORIENTATION 16384
 
 /* device-specific parameters */
 struct bcm5974_param {
-       int dim;                /* logical dimension */
-       int fuzz;               /* logical noise value */
-       int devmin;             /* device minimum reading */
-       int devmax;             /* device maximum reading */
+       int snratio;            /* signal-to-noise ratio */
+       int min;                /* device minimum reading */
+       int max;                /* device maximum reading */
 };
 
 /* device-specific configuration */
@@ -219,6 +220,7 @@ struct bcm5974_config {
        struct bcm5974_param w; /* finger width limits */
        struct bcm5974_param x; /* horizontal limits */
        struct bcm5974_param y; /* vertical limits */
+       struct bcm5974_param o; /* orientation limits */
 };
 
 /* logical device structure */
@@ -234,23 +236,16 @@ struct bcm5974 {
        struct bt_data *bt_data;        /* button transferred data */
        struct urb *tp_urb;             /* trackpad usb request block */
        u8 *tp_data;                    /* trackpad transferred data */
-       int fingers;                    /* number of fingers on trackpad */
+       const struct tp_finger *index[MAX_FINGERS];     /* finger index data */
+       struct input_mt_pos pos[MAX_FINGERS];           /* position array */
+       int slots[MAX_FINGERS];                         /* slot assignments */
 };
 
-/* logical dimensions */
-#define DIM_PRESSURE   256             /* maximum finger pressure */
-#define DIM_WIDTH      16              /* maximum finger width */
-#define DIM_X          1280            /* maximum trackpad x value */
-#define DIM_Y          800             /* maximum trackpad y value */
-
 /* logical signal quality */
 #define SN_PRESSURE    45              /* pressure signal-to-noise ratio */
-#define SN_WIDTH       100             /* width signal-to-noise ratio */
+#define SN_WIDTH       25              /* width signal-to-noise ratio */
 #define SN_COORD       250             /* coordinate signal-to-noise ratio */
-
-/* pressure thresholds */
-#define PRESSURE_LOW   (2 * DIM_PRESSURE / SN_PRESSURE)
-#define PRESSURE_HIGH  (3 * PRESSURE_LOW)
+#define SN_ORIENT      10              /* orientation signal-to-noise ratio */
 
 /* device constants */
 static const struct bcm5974_config bcm5974_config_table[] = {
@@ -261,10 +256,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                0,
                0x84, sizeof(struct bt_data),
                0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4824, 5342 },
-               { DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
+               { SN_PRESSURE, 0, 256 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4824, 5342 },
+               { SN_COORD, -172, 5820 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
@@ -273,10 +269,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                0,
                0x84, sizeof(struct bt_data),
                0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4824, 4824 },
-               { DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
+               { SN_PRESSURE, 0, 256 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4824, 4824 },
+               { SN_COORD, -172, 4290 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
@@ -285,10 +282,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4460, 5166 },
-               { DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4460, 5166 },
+               { SN_COORD, -75, 6700 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
@@ -297,10 +295,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
-               { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4620, 5140 },
+               { SN_COORD, -150, 6600 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
@@ -309,10 +308,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4616, 5112 },
-               { DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4616, 5112 },
+               { SN_COORD, -142, 5234 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
@@ -321,10 +321,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4415, 5050 },
-               { DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4415, 5050 },
+               { SN_COORD, -55, 6680 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
@@ -333,10 +334,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
-               { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4620, 5140 },
+               { SN_COORD, -150, 6600 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
@@ -345,10 +347,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
-               { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4750, 5280 },
+               { SN_COORD, -150, 6730 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
@@ -357,10 +360,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4620, 5140 },
-               { DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4620, 5140 },
+               { SN_COORD, -150, 6600 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {
                USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
@@ -369,10 +373,11 @@ static const struct bcm5974_config bcm5974_config_table[] = {
                HAS_INTEGRATED_BUTTON,
                0x84, sizeof(struct bt_data),
                0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
-               { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
-               { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
-               { DIM_X, DIM_X / SN_COORD, -4750, 5280 },
-               { DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
+               { SN_PRESSURE, 0, 300 },
+               { SN_WIDTH, 0, 2048 },
+               { SN_COORD, -4750, 5280 },
+               { SN_COORD, -150, 6730 },
+               { SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
        },
        {}
 };
@@ -396,18 +401,11 @@ static inline int raw2int(__le16 x)
        return (signed short)le16_to_cpu(x);
 }
 
-/* scale device data to logical dimensions (asserts devmin < devmax) */
-static inline int int2scale(const struct bcm5974_param *p, int x)
-{
-       return x * p->dim / (p->devmax - p->devmin);
-}
-
-/* all logical value ranges are [0,dim). */
-static inline int int2bound(const struct bcm5974_param *p, int x)
+static void set_abs(struct input_dev *input, unsigned int code,
+                   const struct bcm5974_param *p)
 {
-       int s = int2scale(p, x);
-
-       return clamp_val(s, 0, p->dim - 1);
+       int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
+       input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
 }
 
 /* setup which logical events to report */
@@ -416,48 +414,30 @@ static void setup_events_to_report(struct input_dev *input_dev,
 {
        __set_bit(EV_ABS, input_dev->evbit);
 
-       input_set_abs_params(input_dev, ABS_PRESSURE,
-                               0, cfg->p.dim, cfg->p.fuzz, 0);
-       input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
-                               0, cfg->w.dim, cfg->w.fuzz, 0);
-       input_set_abs_params(input_dev, ABS_X,
-                               0, cfg->x.dim, cfg->x.fuzz, 0);
-       input_set_abs_params(input_dev, ABS_Y,
-                               0, cfg->y.dim, cfg->y.fuzz, 0);
+       /* for synaptics only */
+       input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
+       input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
 
        /* finger touch area */
-       input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
-                            cfg->w.devmin, cfg->w.devmax, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
-                            cfg->w.devmin, cfg->w.devmax, 0, 0);
+       set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
+       set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
        /* finger approach area */
-       input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR,
-                            cfg->w.devmin, cfg->w.devmax, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR,
-                            cfg->w.devmin, cfg->w.devmax, 0, 0);
+       set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
+       set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
        /* finger orientation */
-       input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
-                            -MAX_FINGER_ORIENTATION,
-                            MAX_FINGER_ORIENTATION, 0, 0);
+       set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
        /* finger position */
-       input_set_abs_params(input_dev, ABS_MT_POSITION_X,
-                            cfg->x.devmin, cfg->x.devmax, 0, 0);
-       input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
-                            cfg->y.devmin, cfg->y.devmax, 0, 0);
+       set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
+       set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
 
        __set_bit(EV_KEY, input_dev->evbit);
-       __set_bit(BTN_TOUCH, input_dev->keybit);
-       __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
-       __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
-       __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
-       __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
        __set_bit(BTN_LEFT, input_dev->keybit);
 
-       __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
        if (cfg->caps & HAS_INTEGRATED_BUTTON)
                __set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
 
-       input_set_events_per_packet(input_dev, 60);
+       input_mt_init_slots(input_dev, MAX_FINGERS,
+               INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
 }
 
 /* report button data as logical button state */
@@ -477,24 +457,44 @@ static int report_bt_state(struct bcm5974 *dev, int size)
        return 0;
 }
 
-static void report_finger_data(struct input_dev *input,
-                              const struct bcm5974_config *cfg,
+static void report_finger_data(struct input_dev *input, int slot,
+                              const struct input_mt_pos *pos,
                               const struct tp_finger *f)
 {
+       input_mt_slot(input, slot);
+       input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
        input_report_abs(input, ABS_MT_TOUCH_MAJOR,
-                        raw2int(f->force_major) << 1);
+                        raw2int(f->touch_major) << 1);
        input_report_abs(input, ABS_MT_TOUCH_MINOR,
-                        raw2int(f->force_minor) << 1);
+                        raw2int(f->touch_minor) << 1);
        input_report_abs(input, ABS_MT_WIDTH_MAJOR,
-                        raw2int(f->size_major) << 1);
+                        raw2int(f->tool_major) << 1);
        input_report_abs(input, ABS_MT_WIDTH_MINOR,
-                        raw2int(f->size_minor) << 1);
+                        raw2int(f->tool_minor) << 1);
        input_report_abs(input, ABS_MT_ORIENTATION,
                         MAX_FINGER_ORIENTATION - raw2int(f->orientation));
-       input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x));
-       input_report_abs(input, ABS_MT_POSITION_Y,
-                        cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y));
-       input_mt_sync(input);
+       input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+       input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_synaptics_data(struct input_dev *input,
+                                 const struct bcm5974_config *cfg,
+                                 const struct tp_finger *f, int raw_n)
+{
+       int abs_p = 0, abs_w = 0;
+
+       if (raw_n) {
+               int p = raw2int(f->touch_major);
+               int w = raw2int(f->tool_major);
+               if (p > 0 && raw2int(f->origin)) {
+                       abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
+                       abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
+               }
+       }
+
+       input_report_abs(input, ABS_PRESSURE, abs_p);
+       input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
 }
 
 /* report trackpad data as logical trackpad state */
@@ -503,9 +503,7 @@ static int report_tp_state(struct bcm5974 *dev, int size)
        const struct bcm5974_config *c = &dev->cfg;
        const struct tp_finger *f;
        struct input_dev *input = dev->input;
-       int raw_p, raw_w, raw_x, raw_y, raw_n, i;
-       int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
-       int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
+       int raw_n, i, n = 0;
 
        if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
                return -EIO;
@@ -514,76 +512,29 @@ static int report_tp_state(struct bcm5974 *dev, int size)
        f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
        raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
 
-       /* always track the first finger; when detached, start over */
-       if (raw_n) {
-
-               /* report raw trackpad data */
-               for (i = 0; i < raw_n; i++)
-                       report_finger_data(input, c, &f[i]);
-
-               raw_p = raw2int(f->force_major);
-               raw_w = raw2int(f->size_major);
-               raw_x = raw2int(f->abs_x);
-               raw_y = raw2int(f->abs_y);
-
-               dprintk(9,
-                       "bcm5974: "
-                       "raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
-                       raw_p, raw_w, raw_x, raw_y, raw_n);
-
-               ptest = int2bound(&c->p, raw_p);
-               origin = raw2int(f->origin);
-
-               /* while tracking finger still valid, count all fingers */
-               if (ptest > PRESSURE_LOW && origin) {
-                       abs_p = ptest;
-                       abs_w = int2bound(&c->w, raw_w);
-                       abs_x = int2bound(&c->x, raw_x - c->x.devmin);
-                       abs_y = int2bound(&c->y, c->y.devmax - raw_y);
-                       while (raw_n--) {
-                               ptest = int2bound(&c->p,
-                                                 raw2int(f->force_major));
-                               if (ptest > PRESSURE_LOW)
-                                       nmax++;
-                               if (ptest > PRESSURE_HIGH)
-                                       nmin++;
-                               f++;
-                       }
-               }
+       for (i = 0; i < raw_n; i++) {
+               if (raw2int(f[i].touch_major) == 0)
+                       continue;
+               dev->pos[n].x = raw2int(f[i].abs_x);
+               dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
+               dev->index[n++] = &f[i];
        }
 
-       /* set the integrated button if applicable */
-       if (c->tp_type == TYPE2)
-               ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
-
-       if (dev->fingers < nmin)
-               dev->fingers = nmin;
-       if (dev->fingers > nmax)
-               dev->fingers = nmax;
-
-       input_report_key(input, BTN_TOUCH, dev->fingers > 0);
-       input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
-       input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
-       input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
-       input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
-
-       input_report_abs(input, ABS_PRESSURE, abs_p);
-       input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
+       input_mt_assign_slots(input, dev->slots, dev->pos, n);
 
-       if (abs_p) {
-               input_report_abs(input, ABS_X, abs_x);
-               input_report_abs(input, ABS_Y, abs_y);
+       for (i = 0; i < n; i++)
+               report_finger_data(input, dev->slots[i],
+                                  &dev->pos[i], dev->index[i]);
 
-               dprintk(8,
-                       "bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
-                       "nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
-                       abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
+       input_mt_sync_frame(input);
 
-       }
+       report_synaptics_data(input, c, f, raw_n);
 
        /* type 2 reports button events via ibt only */
-       if (c->tp_type == TYPE2)
+       if (c->tp_type == TYPE2) {
+               int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
                input_report_key(input, BTN_LEFT, ibt);
+       }
 
        input_sync(input);
 
@@ -742,9 +693,11 @@ static int bcm5974_start_traffic(struct bcm5974 *dev)
                goto err_out;
        }
 
-       error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
-       if (error)
-               goto err_reset_mode;
+       if (dev->bt_urb) {
+               error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
+               if (error)
+                       goto err_reset_mode;
+       }
 
        error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
        if (error)
@@ -868,19 +821,23 @@ static int bcm5974_probe(struct usb_interface *iface,
        mutex_init(&dev->pm_mutex);
 
        /* setup urbs */
-       dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (!dev->bt_urb)
-               goto err_free_devs;
+       if (cfg->tp_type == TYPE1) {
+               dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!dev->bt_urb)
+                       goto err_free_devs;
+       }
 
        dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!dev->tp_urb)
                goto err_free_bt_urb;
 
-       dev->bt_data = usb_alloc_coherent(dev->udev,
+       if (dev->bt_urb) {
+               dev->bt_data = usb_alloc_coherent(dev->udev,
                                          dev->cfg.bt_datalen, GFP_KERNEL,
                                          &dev->bt_urb->transfer_dma);
-       if (!dev->bt_data)
-               goto err_free_urb;
+               if (!dev->bt_data)
+                       goto err_free_urb;
+       }
 
        dev->tp_data = usb_alloc_coherent(dev->udev,
                                          dev->cfg.tp_datalen, GFP_KERNEL,
@@ -888,10 +845,11 @@ static int bcm5974_probe(struct usb_interface *iface,
        if (!dev->tp_data)
                goto err_free_bt_buffer;
 
-       usb_fill_int_urb(dev->bt_urb, udev,
-                        usb_rcvintpipe(udev, cfg->bt_ep),
-                        dev->bt_data, dev->cfg.bt_datalen,
-                        bcm5974_irq_button, dev, 1);
+       if (dev->bt_urb)
+               usb_fill_int_urb(dev->bt_urb, udev,
+                                usb_rcvintpipe(udev, cfg->bt_ep),
+                                dev->bt_data, dev->cfg.bt_datalen,
+                                bcm5974_irq_button, dev, 1);
 
        usb_fill_int_urb(dev->tp_urb, udev,
                         usb_rcvintpipe(udev, cfg->tp_ep),
@@ -929,8 +887,9 @@ err_free_buffer:
        usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
                dev->tp_data, dev->tp_urb->transfer_dma);
 err_free_bt_buffer:
-       usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
-               dev->bt_data, dev->bt_urb->transfer_dma);
+       if (dev->bt_urb)
+               usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+                                 dev->bt_data, dev->bt_urb->transfer_dma);
 err_free_urb:
        usb_free_urb(dev->tp_urb);
 err_free_bt_urb:
@@ -951,8 +910,9 @@ static void bcm5974_disconnect(struct usb_interface *iface)
        input_unregister_device(dev->input);
        usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
                          dev->tp_data, dev->tp_urb->transfer_dma);
-       usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
-                         dev->bt_data, dev->bt_urb->transfer_dma);
+       if (dev->bt_urb)
+               usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
+                                 dev->bt_data, dev->bt_urb->transfer_dma);
        usb_free_urb(dev->tp_urb);
        usb_free_urb(dev->bt_urb);
        kfree(dev);
index 4790110..1e8e42f 100644 (file)
@@ -1004,7 +1004,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                        input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
                                             ETP_WMAX_V2, 0, 0);
                }
-               input_mt_init_slots(dev, 2);
+               input_mt_init_slots(dev, 2, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
                break;
@@ -1035,7 +1035,7 @@ static int elantech_set_input_params(struct psmouse *psmouse)
                input_set_abs_params(dev, ABS_TOOL_WIDTH, ETP_WMIN_V2,
                                     ETP_WMAX_V2, 0, 0);
                /* Multitouch capable pad, up to 5 fingers. */
-               input_mt_init_slots(dev, ETP_MAX_FINGERS);
+               input_mt_init_slots(dev, ETP_MAX_FINGERS, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_X, x_min, x_max, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, y_min, y_max, 0, 0);
                input_abs_set_res(dev, ABS_MT_POSITION_X, x_res);
index a261d85..e582922 100644 (file)
@@ -971,7 +971,7 @@ static int fsp_set_input_params(struct psmouse *psmouse)
 
                input_set_abs_params(dev, ABS_X, 0, abs_x, 0, 0);
                input_set_abs_params(dev, ABS_Y, 0, abs_y, 0, 0);
-               input_mt_init_slots(dev, 2);
+               input_mt_init_slots(dev, 2, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_X, 0, abs_x, 0, 0);
                input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, abs_y, 0, 0);
        }
index 14eaece..37033ad 100644 (file)
@@ -1232,7 +1232,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
 
        if (SYN_CAP_IMAGE_SENSOR(priv->ext_cap_0c)) {
-               input_mt_init_slots(dev, 2);
+               input_mt_init_slots(dev, 2, 0);
                set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
                                        ABS_MT_POSITION_Y);
                /* Image sensors can report per-contact pressure */
@@ -1244,7 +1244,7 @@ static void set_input_params(struct input_dev *dev, struct synaptics_data *priv)
        } else if (SYN_CAP_ADV_GESTURE(priv->ext_cap_0c)) {
                /* Non-image sensors with AGM use semi-mt */
                __set_bit(INPUT_PROP_SEMI_MT, dev->propbit);
-               input_mt_init_slots(dev, 2);
+               input_mt_init_slots(dev, 2, 0);
                set_abs_position_params(dev, priv, ABS_MT_POSITION_X,
                                        ABS_MT_POSITION_Y);
        }
index 532d067..2a81ce3 100644 (file)
@@ -1530,7 +1530,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
                        __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
                        __set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
 
-                       input_mt_init_slots(input_dev, features->touch_max);
+                       input_mt_init_slots(input_dev, features->touch_max, 0);
 
                        input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
                                             0, 255, 0, 0);
@@ -1575,7 +1575,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
        case TABLETPC2FG:
                if (features->device_type == BTN_TOOL_FINGER) {
-                       input_mt_init_slots(input_dev, features->touch_max);
+                       input_mt_init_slots(input_dev, features->touch_max, 0);
                        input_set_abs_params(input_dev, ABS_MT_TOOL_TYPE,
                                        0, MT_TOOL_MAX, 0, 0);
                        input_set_abs_params(input_dev, ABS_MT_POSITION_X,
@@ -1631,7 +1631,7 @@ int wacom_setup_input_capabilities(struct input_dev *input_dev,
 
                        __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
                        __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
-                       input_mt_init_slots(input_dev, features->touch_max);
+                       input_mt_init_slots(input_dev, features->touch_max, 0);
 
                        if (features->pktlen == WACOM_PKGLEN_BBTOUCH3) {
                                __set_bit(BTN_TOOL_TRIPLETAP,
index 4623cc6..e92615d 100644 (file)
@@ -1152,7 +1152,7 @@ static int __devinit mxt_probe(struct i2c_client *client,
 
        /* For multi touch */
        num_mt_slots = data->T9_reportid_max - data->T9_reportid_min + 1;
-       error = input_mt_init_slots(input_dev, num_mt_slots);
+       error = input_mt_init_slots(input_dev, num_mt_slots, 0);
        if (error)
                goto err_free_object;
        input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
index f030d9e..8e60437 100644 (file)
@@ -571,7 +571,7 @@ struct cyttsp *cyttsp_probe(const struct cyttsp_bus_ops *bus_ops,
        input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
                             0, CY_MAXZ, 0, 0);
 
-       input_mt_init_slots(input_dev, CY_MAX_ID);
+       input_mt_init_slots(input_dev, CY_MAX_ID, 0);
 
        error = request_threaded_irq(ts->irq, NULL, cyttsp_irq,
                                     IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
index 6495777..099d144 100644 (file)
@@ -782,7 +782,7 @@ static int __devinit edt_ft5x06_ts_probe(struct i2c_client *client,
                             0, tsdata->num_x * 64 - 1, 0, 0);
        input_set_abs_params(input, ABS_MT_POSITION_Y,
                             0, tsdata->num_y * 64 - 1, 0, 0);
-       error = input_mt_init_slots(input, MAX_SUPPORT_POINTS);
+       error = input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0);
        if (error) {
                dev_err(&client->dev, "Unable to init MT slots.\n");
                goto err_free_mem;
index 70524dd..c1e3460 100644 (file)
@@ -204,7 +204,7 @@ static int __devinit egalax_ts_probe(struct i2c_client *client,
                             ABS_MT_POSITION_X, 0, EGALAX_MAX_X, 0, 0);
        input_set_abs_params(input_dev,
                             ABS_MT_POSITION_X, 0, EGALAX_MAX_Y, 0, 0);
-       input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS);
+       input_mt_init_slots(input_dev, MAX_SUPPORT_POINTS, 0);
 
        input_set_drvdata(input_dev, ts);
 
index c004417..4ac6976 100644 (file)
@@ -252,7 +252,7 @@ static int __devinit ili210x_i2c_probe(struct i2c_client *client,
        input_set_abs_params(input, ABS_Y, 0, ymax, 0, 0);
 
        /* Multi touch */
-       input_mt_init_slots(input, MAX_TOUCHES);
+       input_mt_init_slots(input, MAX_TOUCHES, 0);
        input_set_abs_params(input, ABS_MT_POSITION_X, 0, xmax, 0, 0);
        input_set_abs_params(input, ABS_MT_POSITION_Y, 0, ymax, 0, 0);
 
index 49c44bb..560cf09 100644 (file)
@@ -404,7 +404,7 @@ static int __devinit mms114_probe(struct i2c_client *client,
        input_set_abs_params(input_dev, ABS_Y, 0, data->pdata->y_size, 0, 0);
 
        /* For multi touch */
-       input_mt_init_slots(input_dev, MMS114_MAX_TOUCH);
+       input_mt_init_slots(input_dev, MMS114_MAX_TOUCH, 0);
        input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
                             0, MMS114_MAX_AREA, 0, 0);
        input_set_abs_params(input_dev, ABS_MT_POSITION_X,
index 4ccde45..b49f0b8 100644 (file)
@@ -264,7 +264,7 @@ static int pm_connect(struct serio *serio, struct serio_driver *drv)
        input_set_abs_params(pm->dev, ABS_Y, 0, max_y, 0, 0);
 
        if (pm->maxcontacts > 1) {
-               input_mt_init_slots(pm->dev, pm->maxcontacts);
+               input_mt_init_slots(pm->dev, pm->maxcontacts, 0);
                input_set_abs_params(pm->dev,
                                     ABS_MT_POSITION_X, 0, max_x, 0, 0);
                input_set_abs_params(pm->dev,
index 8f9ad2f..9a83be6 100644 (file)
@@ -471,7 +471,7 @@ static int w8001_setup(struct w8001 *w8001)
                case 5:
                        w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
 
-                       input_mt_init_slots(dev, 2);
+                       input_mt_init_slots(dev, 2, 0);
                        input_set_abs_params(dev, ABS_MT_POSITION_X,
                                                0, touch.x, 0, 0);
                        input_set_abs_params(dev, ABS_MT_POSITION_Y,
index b64502d..e89daf1 100644 (file)
@@ -266,7 +266,7 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to)
 
 static int iommu_init_device(struct device *dev)
 {
-       struct pci_dev *dma_pdev, *pdev = to_pci_dev(dev);
+       struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev);
        struct iommu_dev_data *dev_data;
        struct iommu_group *group;
        u16 alias;
@@ -293,7 +293,9 @@ static int iommu_init_device(struct device *dev)
                dev_data->alias_data = alias_data;
 
                dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff);
-       } else
+       }
+
+       if (dma_pdev == NULL)
                dma_pdev = pci_dev_get(pdev);
 
        /* Account for quirked devices */
index d8abb90..034233e 100644 (file)
@@ -1555,6 +1555,7 @@ static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
                           unsigned long arg)
 {
        struct multipath *m = ti->private;
+       struct pgpath *pgpath;
        struct block_device *bdev;
        fmode_t mode;
        unsigned long flags;
@@ -1570,12 +1571,14 @@ again:
        if (!m->current_pgpath)
                __choose_pgpath(m, 0);
 
-       if (m->current_pgpath) {
-               bdev = m->current_pgpath->path.dev->bdev;
-               mode = m->current_pgpath->path.dev->mode;
+       pgpath = m->current_pgpath;
+
+       if (pgpath) {
+               bdev = pgpath->path.dev->bdev;
+               mode = pgpath->path.dev->mode;
        }
 
-       if (m->queue_io)
+       if ((pgpath && m->queue_io) || (!pgpath && m->queue_if_no_path))
                r = -EAGAIN;
        else if (!bdev)
                r = -EIO;
index f900690..100368e 100644 (file)
@@ -1212,6 +1212,41 @@ struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector)
        return &t->targets[(KEYS_PER_NODE * n) + k];
 }
 
+static int count_device(struct dm_target *ti, struct dm_dev *dev,
+                       sector_t start, sector_t len, void *data)
+{
+       unsigned *num_devices = data;
+
+       (*num_devices)++;
+
+       return 0;
+}
+
+/*
+ * Check whether a table has no data devices attached using each
+ * target's iterate_devices method.
+ * Returns false if the result is unknown because a target doesn't
+ * support iterate_devices.
+ */
+bool dm_table_has_no_data_devices(struct dm_table *table)
+{
+       struct dm_target *uninitialized_var(ti);
+       unsigned i = 0, num_devices = 0;
+
+       while (i < dm_table_get_num_targets(table)) {
+               ti = dm_table_get_target(table, i++);
+
+               if (!ti->type->iterate_devices)
+                       return false;
+
+               ti->type->iterate_devices(ti, count_device, &num_devices);
+               if (num_devices)
+                       return false;
+       }
+
+       return true;
+}
+
 /*
  * Establish the new table's queue_limits and validate them.
  */
@@ -1354,17 +1389,25 @@ static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
        return q && blk_queue_nonrot(q);
 }
 
-static bool dm_table_is_nonrot(struct dm_table *t)
+static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
+                            sector_t start, sector_t len, void *data)
+{
+       struct request_queue *q = bdev_get_queue(dev->bdev);
+
+       return q && !blk_queue_add_random(q);
+}
+
+static bool dm_table_all_devices_attribute(struct dm_table *t,
+                                          iterate_devices_callout_fn func)
 {
        struct dm_target *ti;
        unsigned i = 0;
 
-       /* Ensure that all underlying device are non-rotational. */
        while (i < dm_table_get_num_targets(t)) {
                ti = dm_table_get_target(t, i++);
 
                if (!ti->type->iterate_devices ||
-                   !ti->type->iterate_devices(ti, device_is_nonrot, NULL))
+                   !ti->type->iterate_devices(ti, func, NULL))
                        return 0;
        }
 
@@ -1396,7 +1439,8 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        if (!dm_table_discard_zeroes_data(t))
                q->limits.discard_zeroes_data = 0;
 
-       if (dm_table_is_nonrot(t))
+       /* Ensure that all underlying devices are non-rotational. */
+       if (dm_table_all_devices_attribute(t, device_is_nonrot))
                queue_flag_set_unlocked(QUEUE_FLAG_NONROT, q);
        else
                queue_flag_clear_unlocked(QUEUE_FLAG_NONROT, q);
@@ -1404,6 +1448,15 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        dm_table_set_integrity(t);
 
        /*
+        * Determine whether or not this queue's I/O timings contribute
+        * to the entropy pool, Only request-based targets use this.
+        * Clear QUEUE_FLAG_ADD_RANDOM if any underlying device does not
+        * have it set.
+        */
+       if (blk_queue_add_random(q) && dm_table_all_devices_attribute(t, device_is_not_random))
+               queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, q);
+
+       /*
         * QUEUE_FLAG_STACKABLE must be set after all queue settings are
         * visible to other CPUs because, once the flag is set, incoming bios
         * are processed by request-based dm, which refers to the queue
index af1fc3b..c29410a 100644 (file)
@@ -509,9 +509,9 @@ enum pool_mode {
 struct pool_features {
        enum pool_mode mode;
 
-       unsigned zero_new_blocks:1;
-       unsigned discard_enabled:1;
-       unsigned discard_passdown:1;
+       bool zero_new_blocks:1;
+       bool discard_enabled:1;
+       bool discard_passdown:1;
 };
 
 struct thin_c;
@@ -580,7 +580,8 @@ struct pool_c {
        struct dm_target_callbacks callbacks;
 
        dm_block_t low_water_blocks;
-       struct pool_features pf;
+       struct pool_features requested_pf; /* Features requested during table load */
+       struct pool_features adjusted_pf;  /* Features used after adjusting for constituent devices */
 };
 
 /*
@@ -1839,6 +1840,47 @@ static void __requeue_bios(struct pool *pool)
 /*----------------------------------------------------------------
  * Binding of control targets to a pool object
  *--------------------------------------------------------------*/
+static bool data_dev_supports_discard(struct pool_c *pt)
+{
+       struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
+
+       return q && blk_queue_discard(q);
+}
+
+/*
+ * If discard_passdown was enabled verify that the data device
+ * supports discards.  Disable discard_passdown if not.
+ */
+static void disable_passdown_if_not_supported(struct pool_c *pt)
+{
+       struct pool *pool = pt->pool;
+       struct block_device *data_bdev = pt->data_dev->bdev;
+       struct queue_limits *data_limits = &bdev_get_queue(data_bdev)->limits;
+       sector_t block_size = pool->sectors_per_block << SECTOR_SHIFT;
+       const char *reason = NULL;
+       char buf[BDEVNAME_SIZE];
+
+       if (!pt->adjusted_pf.discard_passdown)
+               return;
+
+       if (!data_dev_supports_discard(pt))
+               reason = "discard unsupported";
+
+       else if (data_limits->max_discard_sectors < pool->sectors_per_block)
+               reason = "max discard sectors smaller than a block";
+
+       else if (data_limits->discard_granularity > block_size)
+               reason = "discard granularity larger than a block";
+
+       else if (block_size & (data_limits->discard_granularity - 1))
+               reason = "discard granularity not a factor of block size";
+
+       if (reason) {
+               DMWARN("Data device (%s) %s: Disabling discard passdown.", bdevname(data_bdev, buf), reason);
+               pt->adjusted_pf.discard_passdown = false;
+       }
+}
+
 static int bind_control_target(struct pool *pool, struct dm_target *ti)
 {
        struct pool_c *pt = ti->private;
@@ -1847,31 +1889,16 @@ static int bind_control_target(struct pool *pool, struct dm_target *ti)
         * We want to make sure that degraded pools are never upgraded.
         */
        enum pool_mode old_mode = pool->pf.mode;
-       enum pool_mode new_mode = pt->pf.mode;
+       enum pool_mode new_mode = pt->adjusted_pf.mode;
 
        if (old_mode > new_mode)
                new_mode = old_mode;
 
        pool->ti = ti;
        pool->low_water_blocks = pt->low_water_blocks;
-       pool->pf = pt->pf;
-       set_pool_mode(pool, new_mode);
+       pool->pf = pt->adjusted_pf;
 
-       /*
-        * If discard_passdown was enabled verify that the data device
-        * supports discards.  Disable discard_passdown if not; otherwise
-        * -EOPNOTSUPP will be returned.
-        */
-       /* FIXME: pull this out into a sep fn. */
-       if (pt->pf.discard_passdown) {
-               struct request_queue *q = bdev_get_queue(pt->data_dev->bdev);
-               if (!q || !blk_queue_discard(q)) {
-                       char buf[BDEVNAME_SIZE];
-                       DMWARN("Discard unsupported by data device (%s): Disabling discard passdown.",
-                              bdevname(pt->data_dev->bdev, buf));
-                       pool->pf.discard_passdown = 0;
-               }
-       }
+       set_pool_mode(pool, new_mode);
 
        return 0;
 }
@@ -1889,9 +1916,9 @@ static void unbind_control_target(struct pool *pool, struct dm_target *ti)
 static void pool_features_init(struct pool_features *pf)
 {
        pf->mode = PM_WRITE;
-       pf->zero_new_blocks = 1;
-       pf->discard_enabled = 1;
-       pf->discard_passdown = 1;
+       pf->zero_new_blocks = true;
+       pf->discard_enabled = true;
+       pf->discard_passdown = true;
 }
 
 static void __pool_destroy(struct pool *pool)
@@ -2119,13 +2146,13 @@ static int parse_pool_features(struct dm_arg_set *as, struct pool_features *pf,
                argc--;
 
                if (!strcasecmp(arg_name, "skip_block_zeroing"))
-                       pf->zero_new_blocks = 0;
+                       pf->zero_new_blocks = false;
 
                else if (!strcasecmp(arg_name, "ignore_discard"))
-                       pf->discard_enabled = 0;
+                       pf->discard_enabled = false;
 
                else if (!strcasecmp(arg_name, "no_discard_passdown"))
-                       pf->discard_passdown = 0;
+                       pf->discard_passdown = false;
 
                else if (!strcasecmp(arg_name, "read_only"))
                        pf->mode = PM_READ_ONLY;
@@ -2259,8 +2286,9 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
        pt->metadata_dev = metadata_dev;
        pt->data_dev = data_dev;
        pt->low_water_blocks = low_water_blocks;
-       pt->pf = pf;
+       pt->adjusted_pf = pt->requested_pf = pf;
        ti->num_flush_requests = 1;
+
        /*
         * Only need to enable discards if the pool should pass
         * them down to the data device.  The thin device's discard
@@ -2268,12 +2296,14 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
         */
        if (pf.discard_enabled && pf.discard_passdown) {
                ti->num_discard_requests = 1;
+
                /*
                 * Setting 'discards_supported' circumvents the normal
                 * stacking of discard limits (this keeps the pool and
                 * thin devices' discard limits consistent).
                 */
                ti->discards_supported = true;
+               ti->discard_zeroes_data_unsupported = true;
        }
        ti->private = pt;
 
@@ -2703,7 +2733,7 @@ static int pool_status(struct dm_target *ti, status_type_t type,
                       format_dev_t(buf2, pt->data_dev->bdev->bd_dev),
                       (unsigned long)pool->sectors_per_block,
                       (unsigned long long)pt->low_water_blocks);
-               emit_flags(&pt->pf, result, sz, maxlen);
+               emit_flags(&pt->requested_pf, result, sz, maxlen);
                break;
        }
 
@@ -2732,20 +2762,21 @@ static int pool_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
        return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
 }
 
-static void set_discard_limits(struct pool *pool, struct queue_limits *limits)
+static void set_discard_limits(struct pool_c *pt, struct queue_limits *limits)
 {
-       /*
-        * FIXME: these limits may be incompatible with the pool's data device
-        */
+       struct pool *pool = pt->pool;
+       struct queue_limits *data_limits;
+
        limits->max_discard_sectors = pool->sectors_per_block;
 
        /*
-        * This is just a hint, and not enforced.  We have to cope with
-        * bios that cover a block partially.  A discard that spans a block
-        * boundary is not sent to this target.
+        * discard_granularity is just a hint, and not enforced.
         */
-       limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
-       limits->discard_zeroes_data = pool->pf.zero_new_blocks;
+       if (pt->adjusted_pf.discard_passdown) {
+               data_limits = &bdev_get_queue(pt->data_dev->bdev)->limits;
+               limits->discard_granularity = data_limits->discard_granularity;
+       } else
+               limits->discard_granularity = pool->sectors_per_block << SECTOR_SHIFT;
 }
 
 static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
@@ -2755,15 +2786,25 @@ static void pool_io_hints(struct dm_target *ti, struct queue_limits *limits)
 
        blk_limits_io_min(limits, 0);
        blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
-       if (pool->pf.discard_enabled)
-               set_discard_limits(pool, limits);
+
+       /*
+        * pt->adjusted_pf is a staging area for the actual features to use.
+        * They get transferred to the live pool in bind_control_target()
+        * called from pool_preresume().
+        */
+       if (!pt->adjusted_pf.discard_enabled)
+               return;
+
+       disable_passdown_if_not_supported(pt);
+
+       set_discard_limits(pt, limits);
 }
 
 static struct target_type pool_target = {
        .name = "thin-pool",
        .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
                    DM_TARGET_IMMUTABLE,
-       .version = {1, 3, 0},
+       .version = {1, 4, 0},
        .module = THIS_MODULE,
        .ctr = pool_ctr,
        .dtr = pool_dtr,
@@ -3042,19 +3083,19 @@ static int thin_iterate_devices(struct dm_target *ti,
        return 0;
 }
 
+/*
+ * A thin device always inherits its queue limits from its pool.
+ */
 static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 {
        struct thin_c *tc = ti->private;
-       struct pool *pool = tc->pool;
 
-       blk_limits_io_min(limits, 0);
-       blk_limits_io_opt(limits, pool->sectors_per_block << SECTOR_SHIFT);
-       set_discard_limits(pool, limits);
+       *limits = bdev_get_queue(tc->pool_dev->bdev)->limits;
 }
 
 static struct target_type thin_target = {
        .name = "thin",
-       .version = {1, 3, 0},
+       .version = {1, 4, 0},
        .module = THIS_MODULE,
        .ctr = thin_ctr,
        .dtr = thin_dtr,
index 254d192..892ae27 100644 (file)
@@ -718,8 +718,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
        v->hash_dev_block_bits = ffs(num) - 1;
 
        if (sscanf(argv[5], "%llu%c", &num_ll, &dummy) != 1 ||
-           num_ll << (v->data_dev_block_bits - SECTOR_SHIFT) !=
-           (sector_t)num_ll << (v->data_dev_block_bits - SECTOR_SHIFT)) {
+           (sector_t)(num_ll << (v->data_dev_block_bits - SECTOR_SHIFT))
+           >> (v->data_dev_block_bits - SECTOR_SHIFT) != num_ll) {
                ti->error = "Invalid data blocks";
                r = -EINVAL;
                goto bad;
@@ -733,8 +733,8 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
        }
 
        if (sscanf(argv[6], "%llu%c", &num_ll, &dummy) != 1 ||
-           num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT) !=
-           (sector_t)num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT)) {
+           (sector_t)(num_ll << (v->hash_dev_block_bits - SECTOR_SHIFT))
+           >> (v->hash_dev_block_bits - SECTOR_SHIFT) != num_ll) {
                ti->error = "Invalid hash start";
                r = -EINVAL;
                goto bad;
index 4e09b6f..67ffa39 100644 (file)
@@ -865,10 +865,14 @@ static void dm_done(struct request *clone, int error, bool mapped)
 {
        int r = error;
        struct dm_rq_target_io *tio = clone->end_io_data;
-       dm_request_endio_fn rq_end_io = tio->ti->type->rq_end_io;
+       dm_request_endio_fn rq_end_io = NULL;
 
-       if (mapped && rq_end_io)
-               r = rq_end_io(tio->ti, clone, error, &tio->info);
+       if (tio->ti) {
+               rq_end_io = tio->ti->type->rq_end_io;
+
+               if (mapped && rq_end_io)
+                       r = rq_end_io(tio->ti, clone, error, &tio->info);
+       }
 
        if (r <= 0)
                /* The target wants to complete the I/O */
@@ -1588,15 +1592,6 @@ static int map_request(struct dm_target *ti, struct request *clone,
        int r, requeued = 0;
        struct dm_rq_target_io *tio = clone->end_io_data;
 
-       /*
-        * Hold the md reference here for the in-flight I/O.
-        * We can't rely on the reference count by device opener,
-        * because the device may be closed during the request completion
-        * when all bios are completed.
-        * See the comment in rq_completed() too.
-        */
-       dm_get(md);
-
        tio->ti = ti;
        r = ti->type->map_rq(ti, clone, &tio->info);
        switch (r) {
@@ -1628,6 +1623,26 @@ static int map_request(struct dm_target *ti, struct request *clone,
        return requeued;
 }
 
+static struct request *dm_start_request(struct mapped_device *md, struct request *orig)
+{
+       struct request *clone;
+
+       blk_start_request(orig);
+       clone = orig->special;
+       atomic_inc(&md->pending[rq_data_dir(clone)]);
+
+       /*
+        * Hold the md reference here for the in-flight I/O.
+        * We can't rely on the reference count by device opener,
+        * because the device may be closed during the request completion
+        * when all bios are completed.
+        * See the comment in rq_completed() too.
+        */
+       dm_get(md);
+
+       return clone;
+}
+
 /*
  * q->request_fn for request-based dm.
  * Called with the queue lock held.
@@ -1657,14 +1672,21 @@ static void dm_request_fn(struct request_queue *q)
                        pos = blk_rq_pos(rq);
 
                ti = dm_table_find_target(map, pos);
-               BUG_ON(!dm_target_is_valid(ti));
+               if (!dm_target_is_valid(ti)) {
+                       /*
+                        * Must perform setup, that dm_done() requires,
+                        * before calling dm_kill_unmapped_request
+                        */
+                       DMERR_LIMIT("request attempted access beyond the end of device");
+                       clone = dm_start_request(md, rq);
+                       dm_kill_unmapped_request(clone, -EIO);
+                       continue;
+               }
 
                if (ti->type->busy && ti->type->busy(ti))
                        goto delay_and_out;
 
-               blk_start_request(rq);
-               clone = rq->special;
-               atomic_inc(&md->pending[rq_data_dir(clone)]);
+               clone = dm_start_request(md, rq);
 
                spin_unlock(q->queue_lock);
                if (map_request(ti, clone, md))
@@ -1684,8 +1706,6 @@ delay_and_out:
        blk_delay_queue(q, HZ / 10);
 out:
        dm_table_put(map);
-
-       return;
 }
 
 int dm_underlying_device_busy(struct request_queue *q)
@@ -2409,7 +2429,7 @@ static void dm_queue_flush(struct mapped_device *md)
  */
 struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
 {
-       struct dm_table *map = ERR_PTR(-EINVAL);
+       struct dm_table *live_map, *map = ERR_PTR(-EINVAL);
        struct queue_limits limits;
        int r;
 
@@ -2419,6 +2439,19 @@ struct dm_table *dm_swap_table(struct mapped_device *md, struct dm_table *table)
        if (!dm_suspended_md(md))
                goto out;
 
+       /*
+        * If the new table has no data devices, retain the existing limits.
+        * This helps multipath with queue_if_no_path if all paths disappear,
+        * then new I/O is queued based on these limits, and then some paths
+        * reappear.
+        */
+       if (dm_table_has_no_data_devices(table)) {
+               live_map = dm_get_live_table(md);
+               if (live_map)
+                       limits = md->queue->limits;
+               dm_table_put(live_map);
+       }
+
        r = dm_calculate_queue_limits(table, &limits);
        if (r) {
                map = ERR_PTR(r);
index 52eef49..6a99fef 100644 (file)
@@ -54,6 +54,7 @@ void dm_table_event_callback(struct dm_table *t,
                             void (*fn)(void *), void *context);
 struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
 struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
+bool dm_table_has_no_data_devices(struct dm_table *table);
 int dm_calculate_queue_limits(struct dm_table *table,
                              struct queue_limits *limits);
 void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
index 1c2eb38..0138a72 100644 (file)
@@ -1512,14 +1512,16 @@ static int _enough(struct r10conf *conf, struct geom *geo, int ignore)
        do {
                int n = conf->copies;
                int cnt = 0;
+               int this = first;
                while (n--) {
-                       if (conf->mirrors[first].rdev &&
-                           first != ignore)
+                       if (conf->mirrors[this].rdev &&
+                           this != ignore)
                                cnt++;
-                       first = (first+1) % geo->raid_disks;
+                       this = (this+1) % geo->raid_disks;
                }
                if (cnt == 0)
                        return 0;
+               first = (first + geo->near_copies) % geo->raid_disks;
        } while (first != 0);
        return 1;
 }
index 7031b86..0689173 100644 (file)
@@ -1591,6 +1591,7 @@ static int resize_stripes(struct r5conf *conf, int newsize)
                #ifdef CONFIG_MULTICORE_RAID456
                init_waitqueue_head(&nsh->ops.wait_for_ops);
                #endif
+               spin_lock_init(&nsh->stripe_lock);
 
                list_add(&nsh->lru, &newstripes);
        }
index 866f959..29d72a2 100644 (file)
@@ -342,7 +342,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
 
                 /*
                  * Delay might be needed for ABB8500 cut 3.0, if not, remove
-                 * when hardware will be availible
+                 * when hardware will be available
                  */
                        msleep(1);
                        break;
index 3a8fa88..ff61efc 100644 (file)
@@ -281,7 +281,7 @@ static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c,
 
        if (i2c->irq) {
                ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base);
-               /* Still continue with waring if irq init fails */
+               /* Still continue with warning, if irq init fails */
                if (ret)
                        dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret);
                else
index 0f70dce..fbabc3c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * RDC321x MFD southbrige driver
+ * RDC321x MFD southbridge driver
  *
  * Copyright (C) 2007-2010 Florian Fainelli <florian@openwrt.org>
  * Copyright (C) 2010 Bernhard Loos <bernhardloos@googlemail.com>
index 5f58370..345960c 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/regulator/of_regulator.h>
+#include <linux/regulator/machine.h>
 
 #include <linux/mfd/core.h>
 #include <linux/mfd/tps6586x.h>
@@ -346,6 +347,7 @@ failed:
 
 #ifdef CONFIG_OF
 static struct of_regulator_match tps6586x_matches[] = {
+       { .name = "sys",     .driver_data = (void *)TPS6586X_ID_SYS     },
        { .name = "sm0",     .driver_data = (void *)TPS6586X_ID_SM_0    },
        { .name = "sm1",     .driver_data = (void *)TPS6586X_ID_SM_1    },
        { .name = "sm2",     .driver_data = (void *)TPS6586X_ID_SM_2    },
@@ -369,6 +371,7 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
        struct tps6586x_platform_data *pdata;
        struct tps6586x_subdev_info *devs;
        struct device_node *regs;
+       const char *sys_rail_name = NULL;
        unsigned int count;
        unsigned int i, j;
        int err;
@@ -391,12 +394,22 @@ static struct tps6586x_platform_data *tps6586x_parse_dt(struct i2c_client *clien
                return NULL;
 
        for (i = 0, j = 0; i < num && j < count; i++) {
+               struct regulator_init_data *reg_idata;
+
                if (!tps6586x_matches[i].init_data)
                        continue;
 
+               reg_idata  = tps6586x_matches[i].init_data;
                devs[j].name = "tps6586x-regulator";
                devs[j].platform_data = tps6586x_matches[i].init_data;
                devs[j].id = (int)tps6586x_matches[i].driver_data;
+               if (devs[j].id == TPS6586X_ID_SYS)
+                       sys_rail_name = reg_idata->constraints.name;
+
+               if ((devs[j].id == TPS6586X_ID_LDO_5) ||
+                       (devs[j].id == TPS6586X_ID_LDO_RTC))
+                       reg_idata->supply_regulator = sys_rail_name;
+
                devs[j].of_node = tps6586x_matches[i].of_node;
                j++;
        }
index 5a62e6b..0b6e361 100644 (file)
@@ -136,7 +136,7 @@ static __devinit int tps65911_comparator_probe(struct platform_device *pdev)
 
        ret = comp_threshold_set(tps65910, COMP2, pdata->vmbch2_threshold);
        if (ret < 0) {
-               dev_err(&pdev->dev, "cannot set COMP2 theshold\n");
+               dev_err(&pdev->dev, "cannot set COMP2 threshold\n");
                return ret;
        }
 
index 0aac4af..a050e56 100644 (file)
@@ -135,6 +135,7 @@ static struct regmap_irq_chip wm8994_irq_chip = {
        .status_base = WM8994_INTERRUPT_STATUS_1,
        .mask_base = WM8994_INTERRUPT_STATUS_1_MASK,
        .ack_base = WM8994_INTERRUPT_STATUS_1,
+       .runtime_pm = true,
 };
 
 int wm8994_irq_init(struct wm8994 *wm8994)
index d4619e2..2273ce6 100644 (file)
@@ -641,7 +641,7 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
        /*
         * If the host and card support UHS-I mode request the card
         * to switch to 1.8V signaling level.  No 1.8v signalling if
-        * UHS mode is not enabled to maintain compatibilty and some
+        * UHS mode is not enabled to maintain compatibility and some
         * systems that claim 1.8v signalling in fact do not support
         * it.
         */
index efdb81d..74bed0f 100644 (file)
@@ -356,7 +356,7 @@ static void at91_mci_handle_transmitted(struct at91mci_host *host)
 }
 
 /*
- * Update bytes tranfered count during a write operation
+ * Update bytes transfered count during a write operation
  */
 static void at91_mci_update_bytes_xfered(struct at91mci_host *host)
 {
index a53c7c4..852d5fb 100644 (file)
@@ -1022,7 +1022,7 @@ static void atmci_stop_transfer(struct atmel_mci *host)
 }
 
 /*
- * Stop data transfer because error(s) occured.
+ * Stop data transfer because error(s) occurred.
  */
 static void atmci_stop_transfer_pdc(struct atmel_mci *host)
 {
index 3a09f93..686e256 100644 (file)
@@ -447,7 +447,7 @@ static void omap_hsmmc_stop_clock(struct omap_hsmmc_host *host)
        OMAP_HSMMC_WRITE(host->base, SYSCTL,
                OMAP_HSMMC_READ(host->base, SYSCTL) & ~CEN);
        if ((OMAP_HSMMC_READ(host->base, SYSCTL) & CEN) != 0x0)
-               dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stoped\n");
+               dev_dbg(mmc_dev(host->mmc), "MMC Clock is not stopped\n");
 }
 
 static void omap_hsmmc_enable_irq(struct omap_hsmmc_host *host,
index e23f813..32f4a07 100644 (file)
@@ -315,7 +315,7 @@ static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)
                new_val = val & (SDHCI_CTRL_LED | \
                                SDHCI_CTRL_4BITBUS | \
                                SDHCI_CTRL_D3CD);
-               /* ensure the endianess */
+               /* ensure the endianness */
                new_val |= ESDHC_HOST_CONTROL_LE;
                /* bits 8&9 are reserved on mx25 */
                if (!is_imx25_esdhc(imx_data)) {
index 3135a1a..58eab9a 100644 (file)
@@ -806,7 +806,7 @@ static void command_res_completed(struct urb *urb)
                 * we suspect a buggy USB host controller
                 */
        } else if (!vub300->data) {
-               /* this means that the command (typically CMD52) suceeded */
+               /* this means that the command (typically CMD52) succeeded */
        } else if (vub300->resp.common.header_type != 0x02) {
                /*
                 * this is an error response from the VUB300 chip
index f2f482b..a6e7451 100644 (file)
@@ -1123,6 +1123,33 @@ static unsigned long mtdchar_get_unmapped_area(struct file *file,
 }
 #endif
 
+static inline unsigned long get_vm_size(struct vm_area_struct *vma)
+{
+       return vma->vm_end - vma->vm_start;
+}
+
+static inline resource_size_t get_vm_offset(struct vm_area_struct *vma)
+{
+       return (resource_size_t) vma->vm_pgoff << PAGE_SHIFT;
+}
+
+/*
+ * Set a new vm offset.
+ *
+ * Verify that the incoming offset really works as a page offset,
+ * and that the offset and size fit in a resource_size_t.
+ */
+static inline int set_vm_offset(struct vm_area_struct *vma, resource_size_t off)
+{
+       pgoff_t pgoff = off >> PAGE_SHIFT;
+       if (off != (resource_size_t) pgoff << PAGE_SHIFT)
+               return -EINVAL;
+       if (off + get_vm_size(vma) - 1 < off)
+               return -EINVAL;
+       vma->vm_pgoff = pgoff;
+       return 0;
+}
+
 /*
  * set up a mapping for shared memory segments
  */
@@ -1132,20 +1159,29 @@ static int mtdchar_mmap(struct file *file, struct vm_area_struct *vma)
        struct mtd_file_info *mfi = file->private_data;
        struct mtd_info *mtd = mfi->mtd;
        struct map_info *map = mtd->priv;
-       unsigned long start;
-       unsigned long off;
-       u32 len;
+       resource_size_t start, off;
+       unsigned long len, vma_len;
 
        if (mtd->type == MTD_RAM || mtd->type == MTD_ROM) {
-               off = vma->vm_pgoff << PAGE_SHIFT;
+               off = get_vm_offset(vma);
                start = map->phys;
                len = PAGE_ALIGN((start & ~PAGE_MASK) + map->size);
                start &= PAGE_MASK;
-               if ((vma->vm_end - vma->vm_start + off) > len)
+               vma_len = get_vm_size(vma);
+
+               /* Overflow in off+len? */
+               if (vma_len + off < off)
+                       return -EINVAL;
+               /* Does it fit in the mapping? */
+               if (vma_len + off > len)
                        return -EINVAL;
 
                off += start;
-               vma->vm_pgoff = off >> PAGE_SHIFT;
+               /* Did that overflow? */
+               if (off < start)
+                       return -EINVAL;
+               if (set_vm_offset(vma, off) < 0)
+                       return -EINVAL;
                vma->vm_flags |= VM_IO | VM_RESERVED;
 
 #ifdef pgprot_noncached
index b153666..bb9670f 100644 (file)
@@ -364,7 +364,7 @@ typhoon_inc_rxfree_index(u32 *index, const int count)
 static inline void
 typhoon_inc_tx_index(u32 *index, const int count)
 {
-       /* if we start using the Hi Tx ring, this needs updateing */
+       /* if we start using the Hi Tx ring, this needs updating */
        typhoon_inc_index(index, count, TXLO_ENTRIES);
 }
 
index 79cebd8..e48312f 100644 (file)
@@ -8564,7 +8564,7 @@ bnx2_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
        return 0;
 
 error:
-       iounmap(bp->regview);
+       pci_iounmap(pdev, bp->regview);
        pci_release_regions(pdev);
        pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
index 6d1a24a..eac2523 100644 (file)
@@ -1458,7 +1458,7 @@ struct bnx2x {
        int                             fw_stats_req_sz;
 
        /*
-        * FW statistics data shortcut (points at the begining of
+        * FW statistics data shortcut (points at the beginning of
         * fw_stats buffer + fw_stats_req_sz).
         */
        struct bnx2x_fw_stats_data      *fw_stats_data;
index f83e033..acf2fe4 100644 (file)
@@ -1321,7 +1321,7 @@ void bnx2x_init_mcast_obj(struct bnx2x *bp,
  * the current command will be enqueued to the tail of the
  * pending commands list.
  *
- * Return: 0 is operation was sucessfull and there are no pending completions,
+ * Return: 0 is operation was successfull and there are no pending completions,
  *         negative if there were errors, positive if there are pending
  *         completions.
  */
index c42bbb1..a688a2d 100644 (file)
@@ -722,10 +722,8 @@ static int octeon_mgmt_init_phy(struct net_device *netdev)
                                   octeon_mgmt_adjust_link, 0,
                                   PHY_INTERFACE_MODE_MII);
 
-       if (IS_ERR(p->phydev)) {
-               p->phydev = NULL;
+       if (!p->phydev)
                return -1;
-       }
 
        phy_start_aneg(p->phydev);
 
index e559dfa..6fa74d5 100644 (file)
@@ -1101,9 +1101,9 @@ static int pasemi_mac_phy_init(struct net_device *dev)
        phydev = of_phy_connect(dev, phy_dn, &pasemi_adjust_link, 0,
                                PHY_INTERFACE_MODE_SGMII);
 
-       if (IS_ERR(phydev)) {
+       if (!phydev) {
                printk(KERN_ERR "%s: Could not attach to phy\n", dev->name);
-               return PTR_ERR(phydev);
+               return -ENODEV;
        }
 
        mac->phydev = phydev;
index b8ead69..2a179d0 100644 (file)
@@ -15,7 +15,7 @@ qlcnic_poll_rsp(struct qlcnic_adapter *adapter)
 
        do {
                /* give atleast 1ms for firmware to respond */
-               msleep(1);
+               mdelay(1);
 
                if (++timeout > QLCNIC_OS_CRB_RETRY_COUNT)
                        return QLCNIC_CDRP_RSP_TIMEOUT;
@@ -601,7 +601,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)
                qlcnic_fw_cmd_destroy_tx_ctx(adapter);
 
                /* Allow dma queues to drain after context reset */
-               msleep(20);
+               mdelay(20);
        }
 }
 
index 2346b38..7997895 100644 (file)
@@ -229,3 +229,5 @@ static void __exit bcm87xx_exit(void)
                ARRAY_SIZE(bcm87xx_driver));
 }
 module_exit(bcm87xx_exit);
+
+MODULE_LICENSE("GPL");
index cf287e0..2165d5f 100644 (file)
 #include <linux/phy.h>
 #include <linux/micrel_phy.h>
 
+/* Operation Mode Strap Override */
+#define MII_KSZPHY_OMSO                                0x16
+#define KSZPHY_OMSO_B_CAST_OFF                 (1 << 9)
+#define KSZPHY_OMSO_RMII_OVERRIDE              (1 << 1)
+#define KSZPHY_OMSO_MII_OVERRIDE               (1 << 0)
+
 /* general Interrupt control/status reg in vendor specific block. */
 #define MII_KSZPHY_INTCS                       0x1B
 #define        KSZPHY_INTCS_JABBER                     (1 << 15)
@@ -101,6 +107,13 @@ static int kszphy_config_init(struct phy_device *phydev)
        return 0;
 }
 
+static int ksz8021_config_init(struct phy_device *phydev)
+{
+       const u16 val = KSZPHY_OMSO_B_CAST_OFF | KSZPHY_OMSO_RMII_OVERRIDE;
+       phy_write(phydev, MII_KSZPHY_OMSO, val);
+       return 0;
+}
+
 static int ks8051_config_init(struct phy_device *phydev)
 {
        int regval;
@@ -128,9 +141,22 @@ static struct phy_driver ksphy_driver[] = {
        .config_intr    = ks8737_config_intr,
        .driver         = { .owner = THIS_MODULE,},
 }, {
-       .phy_id         = PHY_ID_KS8041,
+       .phy_id         = PHY_ID_KSZ8021,
+       .phy_id_mask    = 0x00ffffff,
+       .name           = "Micrel KSZ8021",
+       .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause |
+                          SUPPORTED_Asym_Pause),
+       .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
+       .config_init    = ksz8021_config_init,
+       .config_aneg    = genphy_config_aneg,
+       .read_status    = genphy_read_status,
+       .ack_interrupt  = kszphy_ack_interrupt,
+       .config_intr    = kszphy_config_intr,
+       .driver         = { .owner = THIS_MODULE,},
+}, {
+       .phy_id         = PHY_ID_KSZ8041,
        .phy_id_mask    = 0x00fffff0,
-       .name           = "Micrel KS8041",
+       .name           = "Micrel KSZ8041",
        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
                                | SUPPORTED_Asym_Pause),
        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -141,9 +167,9 @@ static struct phy_driver ksphy_driver[] = {
        .config_intr    = kszphy_config_intr,
        .driver         = { .owner = THIS_MODULE,},
 }, {
-       .phy_id         = PHY_ID_KS8051,
+       .phy_id         = PHY_ID_KSZ8051,
        .phy_id_mask    = 0x00fffff0,
-       .name           = "Micrel KS8051",
+       .name           = "Micrel KSZ8051",
        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause
                                | SUPPORTED_Asym_Pause),
        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -154,8 +180,8 @@ static struct phy_driver ksphy_driver[] = {
        .config_intr    = kszphy_config_intr,
        .driver         = { .owner = THIS_MODULE,},
 }, {
-       .phy_id         = PHY_ID_KS8001,
-       .name           = "Micrel KS8001 or KS8721",
+       .phy_id         = PHY_ID_KSZ8001,
+       .name           = "Micrel KSZ8001 or KS8721",
        .phy_id_mask    = 0x00ffffff,
        .features       = (PHY_BASIC_FEATURES | SUPPORTED_Pause),
        .flags          = PHY_HAS_MAGICANEG | PHY_HAS_INTERRUPT,
@@ -201,10 +227,11 @@ MODULE_LICENSE("GPL");
 
 static struct mdio_device_id __maybe_unused micrel_tbl[] = {
        { PHY_ID_KSZ9021, 0x000ffffe },
-       { PHY_ID_KS8001, 0x00ffffff },
+       { PHY_ID_KSZ8001, 0x00ffffff },
        { PHY_ID_KS8737, 0x00fffff0 },
-       { PHY_ID_KS8041, 0x00fffff0 },
-       { PHY_ID_KS8051, 0x00fffff0 },
+       { PHY_ID_KSZ8021, 0x00ffffff },
+       { PHY_ID_KSZ8041, 0x00fffff0 },
+       { PHY_ID_KSZ8051, 0x00fffff0 },
        { }
 };
 
index 6d61923..88e3991 100644 (file)
@@ -56,6 +56,32 @@ static int smsc_phy_config_init(struct phy_device *phydev)
        return smsc_phy_ack_interrupt (phydev);
 }
 
+static int lan87xx_config_init(struct phy_device *phydev)
+{
+       /*
+        * Make sure the EDPWRDOWN bit is NOT set. Setting this bit on
+        * LAN8710/LAN8720 PHY causes the PHY to misbehave, likely due
+        * to a bug on the chip.
+        *
+        * When the system is powered on with the network cable being
+        * disconnected all the way until after ifconfig ethX up is
+        * issued for the LAN port with this PHY, connecting the cable
+        * afterwards does not cause LINK change detection, while the
+        * expected behavior is the Link UP being detected.
+        */
+       int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS);
+       if (rc < 0)
+               return rc;
+
+       rc &= ~MII_LAN83C185_EDPWRDOWN;
+
+       rc = phy_write(phydev, MII_LAN83C185_CTRL_STATUS, rc);
+       if (rc < 0)
+               return rc;
+
+       return smsc_phy_ack_interrupt(phydev);
+}
+
 static int lan911x_config_init(struct phy_device *phydev)
 {
        return smsc_phy_ack_interrupt(phydev);
@@ -162,7 +188,7 @@ static struct phy_driver smsc_phy_driver[] = {
        /* basic functions */
        .config_aneg    = genphy_config_aneg,
        .read_status    = genphy_read_status,
-       .config_init    = smsc_phy_config_init,
+       .config_init    = lan87xx_config_init,
 
        /* IRQ related */
        .ack_interrupt  = smsc_phy_ack_interrupt,
index cbf7047..20f31d0 100644 (file)
@@ -570,7 +570,7 @@ static int pppoe_release(struct socket *sock)
 
        po = pppox_sk(sk);
 
-       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND)) {
+       if (sk->sk_state & (PPPOX_CONNECTED | PPPOX_BOUND | PPPOX_ZOMBIE)) {
                dev_put(po->pppoe_dev);
                po->pppoe_dev = NULL;
        }
index 341b65d..f8cd61f 100644 (file)
@@ -848,7 +848,7 @@ static struct netpoll_info *team_netpoll_info(struct team *team)
 }
 #endif
 
-static void __team_port_change_check(struct team_port *port, bool linkup);
+static void __team_port_change_port_added(struct team_port *port, bool linkup);
 
 static int team_port_add(struct team *team, struct net_device *port_dev)
 {
@@ -948,7 +948,7 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
        team_port_enable(team, port);
        list_add_tail_rcu(&port->list, &team->port_list);
        __team_compute_features(team);
-       __team_port_change_check(port, !!netif_carrier_ok(port_dev));
+       __team_port_change_port_added(port, !!netif_carrier_ok(port_dev));
        __team_options_change_check(team);
 
        netdev_info(dev, "Port device %s added\n", portname);
@@ -983,6 +983,8 @@ err_set_mtu:
        return err;
 }
 
+static void __team_port_change_port_removed(struct team_port *port);
+
 static int team_port_del(struct team *team, struct net_device *port_dev)
 {
        struct net_device *dev = team->dev;
@@ -999,8 +1001,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
        __team_option_inst_mark_removed_port(team, port);
        __team_options_change_check(team);
        __team_option_inst_del_port(team, port);
-       port->removed = true;
-       __team_port_change_check(port, false);
+       __team_port_change_port_removed(port);
        team_port_disable(team, port);
        list_del_rcu(&port->list);
        netdev_rx_handler_unregister(port_dev);
@@ -1652,8 +1653,8 @@ static int team_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
 
        hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
                          &team_nl_family, 0, TEAM_CMD_NOOP);
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
+       if (!hdr) {
+               err = -EMSGSIZE;
                goto err_msg_put;
        }
 
@@ -1847,8 +1848,8 @@ start_again:
 
        hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags | NLM_F_MULTI,
                          TEAM_CMD_OPTIONS_GET);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       if (!hdr)
+               return -EMSGSIZE;
 
        if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
                goto nla_put_failure;
@@ -2067,8 +2068,8 @@ static int team_nl_fill_port_list_get(struct sk_buff *skb,
 
        hdr = genlmsg_put(skb, pid, seq, &team_nl_family, flags,
                          TEAM_CMD_PORT_LIST_GET);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       if (!hdr)
+               return -EMSGSIZE;
 
        if (nla_put_u32(skb, TEAM_ATTR_TEAM_IFINDEX, team->dev->ifindex))
                goto nla_put_failure;
@@ -2251,13 +2252,11 @@ static void __team_options_change_check(struct team *team)
 }
 
 /* rtnl lock is held */
-static void __team_port_change_check(struct team_port *port, bool linkup)
+
+static void __team_port_change_send(struct team_port *port, bool linkup)
 {
        int err;
 
-       if (!port->removed && port->state.linkup == linkup)
-               return;
-
        port->changed = true;
        port->state.linkup = linkup;
        team_refresh_port_linkup(port);
@@ -2282,6 +2281,23 @@ send_event:
 
 }
 
+static void __team_port_change_check(struct team_port *port, bool linkup)
+{
+       if (port->state.linkup != linkup)
+               __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_added(struct team_port *port, bool linkup)
+{
+       __team_port_change_send(port, linkup);
+}
+
+static void __team_port_change_port_removed(struct team_port *port)
+{
+       port->removed = true;
+       __team_port_change_send(port, false);
+}
+
 static void team_port_change_check(struct team_port *port, bool linkup)
 {
        struct team *team = port->team;
index f5ab6e6..376143e 100644 (file)
@@ -1253,6 +1253,7 @@ static struct usb_driver smsc75xx_driver = {
        .probe          = usbnet_probe,
        .suspend        = usbnet_suspend,
        .resume         = usbnet_resume,
+       .reset_resume   = usbnet_resume,
        .disconnect     = usbnet_disconnect,
        .disable_hub_initiated_lpm = 1,
 };
index 3876c7e..7a28d21 100644 (file)
@@ -34,8 +34,8 @@ config B43_BCMA
 config B43_BCMA_EXTRA
        bool "Hardware support that overlaps with the brcmsmac driver"
        depends on B43_BCMA
-       default n if BRCMSMAC || BRCMSMAC_MODULE
-       default y
+       default n if BRCMSMAC
+       default y
 
 config B43_SSB
        bool
index 1e86ea2..dbeebef 100644 (file)
@@ -1442,6 +1442,7 @@ static int iwl_trans_pcie_start_hw(struct iwl_trans *trans)
        return err;
 
 err_free_irq:
+       trans_pcie->irq_requested = false;
        free_irq(trans_pcie->irq, trans);
 error:
        iwl_free_isr_ict(trans);
index 44febfd..8a7b864 100644 (file)
@@ -315,7 +315,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
        u16 box_reg = 0, box_extreg = 0;
        u8 u1b_tmp;
        bool isfw_read = false;
-       bool bwrite_sucess = false;
+       bool bwrite_success = false;
        u8 wait_h2c_limmit = 100;
        u8 wait_writeh2c_limmit = 100;
        u8 boxcontent[4], boxextcontent[2];
@@ -354,7 +354,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
                }
        }
 
-       while (!bwrite_sucess) {
+       while (!bwrite_success) {
                wait_writeh2c_limmit--;
                if (wait_writeh2c_limmit == 0) {
                        RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -491,7 +491,7 @@ static void _rtl92c_fill_h2c_command(struct ieee80211_hw *hw,
                        break;
                }
 
-               bwrite_sucess = true;
+               bwrite_success = true;
 
                rtlhal->last_hmeboxnum = boxnum + 1;
                if (rtlhal->last_hmeboxnum == 4)
index 895ae6c..eb22dcc 100644 (file)
@@ -365,7 +365,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
        u8 u1b_tmp;
        bool isfw_read = false;
        u8 buf_index = 0;
-       bool bwrite_sucess = false;
+       bool bwrite_success = false;
        u8 wait_h2c_limmit = 100;
        u8 wait_writeh2c_limmit = 100;
        u8 boxcontent[4], boxextcontent[2];
@@ -408,7 +408,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
                        break;
                }
        }
-       while (!bwrite_sucess) {
+       while (!bwrite_success) {
                wait_writeh2c_limmit--;
                if (wait_writeh2c_limmit == 0) {
                        RT_TRACE(rtlpriv, COMP_ERR, DBG_EMERG,
@@ -515,7 +515,7 @@ static void _rtl92d_fill_h2c_command(struct ieee80211_hw *hw,
                                 "switch case not processed\n");
                        break;
                }
-               bwrite_sucess = true;
+               bwrite_success = true;
                rtlhal->last_hmeboxnum = boxnum + 1;
                if (rtlhal->last_hmeboxnum == 4)
                        rtlhal->last_hmeboxnum = 0;
diff --git a/drivers/pci/.gitignore b/drivers/pci/.gitignore
deleted file mode 100644 (file)
index f297ca8..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-classlist.h
-devlist.h
-gen-devlist
-
index 4e932cc..e98a5e7 100644 (file)
@@ -33,9 +33,8 @@ config REGULATOR_DUMMY
        help
          If this option is enabled then when a regulator lookup fails
          and the board has not specified that it has provided full
-         constraints then the regulator core will provide an always
-         enabled dummy regulator will be provided, allowing consumer
-         drivers to continue.
+         constraints the regulator core will provide an always
+         enabled dummy regulator, allowing consumer drivers to continue.
 
          A warning will be generated when this substitution is done.
 
@@ -50,11 +49,11 @@ config REGULATOR_VIRTUAL_CONSUMER
        tristate "Virtual regulator consumer support"
        help
          This driver provides a virtual consumer for the voltage and
-          current regulator API which provides sysfs controls for
-          configuring the supplies requested.  This is mainly useful
-          for test purposes.
+         current regulator API which provides sysfs controls for
+         configuring the supplies requested.  This is mainly useful
+         for test purposes.
 
-          If unsure, say no.
+         If unsure, say no.
 
 config REGULATOR_USERSPACE_CONSUMER
        tristate "Userspace regulator consumer support"
@@ -63,7 +62,7 @@ config REGULATOR_USERSPACE_CONSUMER
          from user space. Userspace consumer driver provides ability to
          control power supplies for such devices.
 
-          If unsure, say no.
+         If unsure, say no.
 
 config REGULATOR_GPIO
        tristate "GPIO regulator support"
@@ -110,6 +109,17 @@ config REGULATOR_DA9052
          This driver supports the voltage regulators of DA9052-BC and
          DA9053-AA/Bx PMIC.
 
+config REGULATOR_FAN53555
+       tristate "Fairchild FAN53555 Regulator"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         This driver supports Fairchild FAN53555 Digitally Programmable
+         TinyBuck Regulator. The FAN53555 is a step-down switching voltage
+         regulator that delivers a digitally programmable output from an
+         input voltage supply of 2.5V to 5.5V. The output voltage is
+         programmed through an I2C interface.
+
 config REGULATOR_ANATOP
        tristate "Freescale i.MX on-chip ANATOP LDO regulators"
        depends on MFD_ANATOP
@@ -172,6 +182,14 @@ config REGULATOR_MAX8660
          This driver controls a Maxim 8660/8661 voltage output
          regulator via I2C bus.
 
+config REGULATOR_MAX8907
+       tristate "Maxim 8907 voltage regulator"
+       depends on MFD_MAX8907
+       help
+         This driver controls a Maxim 8907 voltage output regulator
+         via I2C bus. The provided regulator is suitable for Tegra
+         chip to control Step-Down DC-DC and LDOs.
+
 config REGULATOR_MAX8925
        tristate "Maxim MAX8925 Power Management IC"
        depends on MFD_MAX8925
@@ -247,7 +265,7 @@ config REGULATOR_LP8788
 
 config REGULATOR_PCF50633
        tristate "NXP PCF50633 regulator driver"
-        depends on MFD_PCF50633
+       depends on MFD_PCF50633
        help
         Say Y here to support the voltage regulators and convertors
         on PCF50633
@@ -416,7 +434,7 @@ config REGULATOR_WM8350
        depends on MFD_WM8350
        help
          This driver provides support for the voltage and current regulators
-          of the WM8350 AudioPlus PMIC.
+         of the WM8350 AudioPlus PMIC.
 
 config REGULATOR_WM8400
        tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC"
index 3342615..e431eed 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_DA903X)        += da903x.o
 obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
 obj-$(CONFIG_REGULATOR_DBX500_PRCMU) += dbx500-prcmu.o
 obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
+obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
 obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
 obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
 obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
@@ -30,6 +31,7 @@ obj-$(CONFIG_REGULATOR_LP8788) += lp8788-ldo.o
 obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o
 obj-$(CONFIG_REGULATOR_MAX8649)        += max8649.o
 obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o
+obj-$(CONFIG_REGULATOR_MAX8907) += max8907-regulator.o
 obj-$(CONFIG_REGULATOR_MAX8925) += max8925-regulator.o
 obj-$(CONFIG_REGULATOR_MAX8952) += max8952.o
 obj-$(CONFIG_REGULATOR_MAX8997) += max8997.o
index 6f45bfd..167c93f 100644 (file)
@@ -162,7 +162,7 @@ static struct aat2870_regulator *aat2870_get_regulator(int id)
 static int aat2870_regulator_probe(struct platform_device *pdev)
 {
        struct aat2870_regulator *ri;
-       struct regulator_config config = { };
+       struct regulator_config config = { };
        struct regulator_dev *rdev;
 
        ri = aat2870_get_regulator(pdev->id);
index c151fd5..65ad2b3 100644 (file)
@@ -347,17 +347,11 @@ static int ab3100_get_voltage_regulator_external(struct regulator_dev *reg)
        return abreg->plfdata->external_voltage;
 }
 
-static int ab3100_get_fixed_voltage_regulator(struct regulator_dev *reg)
-{
-       return reg->desc->min_uV;
-}
-
 static struct regulator_ops regulator_ops_fixed = {
        .list_voltage = regulator_list_voltage_linear,
        .enable      = ab3100_enable_regulator,
        .disable     = ab3100_disable_regulator,
        .is_enabled  = ab3100_is_enabled_regulator,
-       .get_voltage = ab3100_get_fixed_voltage_regulator,
 };
 
 static struct regulator_ops regulator_ops_variable = {
index 10f2f4d..e3d1d06 100644 (file)
@@ -37,6 +37,7 @@
  * @voltage_bank: bank to control regulator voltage
  * @voltage_reg: register to control regulator voltage
  * @voltage_mask: mask to control regulator voltage
+ * @voltage_shift: shift to control regulator voltage
  * @delay: startup/set voltage delay in us
  */
 struct ab8500_regulator_info {
@@ -50,6 +51,7 @@ struct ab8500_regulator_info {
        u8 voltage_bank;
        u8 voltage_reg;
        u8 voltage_mask;
+       u8 voltage_shift;
        unsigned int delay;
 };
 
@@ -195,17 +197,14 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
        }
 
        dev_vdbg(rdev_get_dev(rdev),
-               "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
-               " 0x%x\n",
-               info->desc.name, info->voltage_bank, info->voltage_reg,
-               info->voltage_mask, regval);
+               "%s-get_voltage (bank, reg, mask, shift, value): "
+               "0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+               info->desc.name, info->voltage_bank,
+               info->voltage_reg, info->voltage_mask,
+               info->voltage_shift, regval);
 
-       /* vintcore has a different layout */
        val = regval & info->voltage_mask;
-       if (info->desc.id == AB8500_LDO_INTCORE)
-               return val >> 0x3;
-       else
-               return val;
+       return val >> info->voltage_shift;
 }
 
 static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
@@ -221,7 +220,7 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
        }
 
        /* set the registers for the request */
-       regval = (u8)selector;
+       regval = (u8)selector << info->voltage_shift;
        ret = abx500_mask_and_set_register_interruptible(info->dev,
                        info->voltage_bank, info->voltage_reg,
                        info->voltage_mask, regval);
@@ -238,13 +237,6 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
        return ret;
 }
 
-static int ab8500_regulator_enable_time(struct regulator_dev *rdev)
-{
-       struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-
-       return info->delay;
-}
-
 static int ab8500_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
                                             unsigned int old_sel,
                                             unsigned int new_sel)
@@ -261,22 +253,14 @@ static struct regulator_ops ab8500_regulator_ops = {
        .get_voltage_sel = ab8500_regulator_get_voltage_sel,
        .set_voltage_sel = ab8500_regulator_set_voltage_sel,
        .list_voltage   = regulator_list_voltage_table,
-       .enable_time    = ab8500_regulator_enable_time,
        .set_voltage_time_sel = ab8500_regulator_set_voltage_time_sel,
 };
 
-static int ab8500_fixed_get_voltage(struct regulator_dev *rdev)
-{
-       return rdev->desc->min_uV;
-}
-
 static struct regulator_ops ab8500_regulator_fixed_ops = {
        .enable         = ab8500_regulator_enable,
        .disable        = ab8500_regulator_disable,
        .is_enabled     = ab8500_regulator_is_enabled,
-       .get_voltage    = ab8500_fixed_get_voltage,
        .list_voltage   = regulator_list_voltage_linear,
-       .enable_time    = ab8500_regulator_enable_time,
 };
 
 static struct ab8500_regulator_info
@@ -358,6 +342,7 @@ static struct ab8500_regulator_info
                .voltage_bank           = 0x03,
                .voltage_reg            = 0x80,
                .voltage_mask           = 0x38,
+               .voltage_shift          = 3,
        },
 
        /*
@@ -374,6 +359,7 @@ static struct ab8500_regulator_info
                        .owner          = THIS_MODULE,
                        .n_voltages     = 1,
                        .min_uV         = 2000000,
+                       .enable_time    = 10000,
                },
                .delay                  = 10000,
                .update_bank            = 0x03,
index c8f95c0..d184aa3 100644 (file)
@@ -39,6 +39,8 @@ static struct regulator_ops arizona_ldo1_ops = {
        .map_voltage = regulator_map_voltage_linear,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 };
 
 static const struct regulator_desc arizona_ldo1 = {
@@ -49,9 +51,11 @@ static const struct regulator_desc arizona_ldo1 = {
 
        .vsel_reg = ARIZONA_LDO1_CONTROL_1,
        .vsel_mask = ARIZONA_LDO1_VSEL_MASK,
+       .bypass_reg = ARIZONA_LDO1_CONTROL_1,
+       .bypass_mask = ARIZONA_LDO1_BYPASS,
        .min_uV = 900000,
        .uV_step = 50000,
-       .n_voltages = 7,
+       .n_voltages = 6,
 
        .owner = THIS_MODULE,
 };
index 450a069..d9b1f82 100644 (file)
@@ -82,6 +82,9 @@ static struct regulator_ops arizona_micsupp_ops = {
 
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
+
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 };
 
 static const struct regulator_desc arizona_micsupp = {
@@ -95,6 +98,8 @@ static const struct regulator_desc arizona_micsupp = {
        .vsel_mask = ARIZONA_LDO2_VSEL_MASK,
        .enable_reg = ARIZONA_MIC_CHARGE_PUMP_1,
        .enable_mask = ARIZONA_CPMIC_ENA,
+       .bypass_reg = ARIZONA_MIC_CHARGE_PUMP_1,
+       .bypass_mask = ARIZONA_CPMIC_BYPASS,
 
        .owner = THIS_MODULE,
 };
index 4838531..2e0352d 100644 (file)
@@ -77,6 +77,7 @@ struct regulator {
        struct device *dev;
        struct list_head list;
        unsigned int always_on:1;
+       unsigned int bypass:1;
        int uA_load;
        int min_uV;
        int max_uV;
@@ -394,6 +395,9 @@ static ssize_t regulator_status_show(struct device *dev,
        case REGULATOR_STATUS_STANDBY:
                label = "standby";
                break;
+       case REGULATOR_STATUS_BYPASS:
+               label = "bypass";
+               break;
        case REGULATOR_STATUS_UNDEFINED:
                label = "undefined";
                break;
@@ -585,6 +589,27 @@ static ssize_t regulator_suspend_standby_state_show(struct device *dev,
 static DEVICE_ATTR(suspend_standby_state, 0444,
                regulator_suspend_standby_state_show, NULL);
 
+static ssize_t regulator_bypass_show(struct device *dev,
+                                    struct device_attribute *attr, char *buf)
+{
+       struct regulator_dev *rdev = dev_get_drvdata(dev);
+       const char *report;
+       bool bypass;
+       int ret;
+
+       ret = rdev->desc->ops->get_bypass(rdev, &bypass);
+
+       if (ret != 0)
+               report = "unknown";
+       else if (bypass)
+               report = "enabled";
+       else
+               report = "disabled";
+
+       return sprintf(buf, "%s\n", report);
+}
+static DEVICE_ATTR(bypass, 0444,
+                  regulator_bypass_show, NULL);
 
 /*
  * These are the only attributes are present for all regulators.
@@ -778,6 +803,9 @@ static void print_constraints(struct regulator_dev *rdev)
        if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
                count += sprintf(buf + count, "standby");
 
+       if (!count)
+               sprintf(buf, "no parameters");
+
        rdev_info(rdev, "%s\n", buf);
 
        if ((constraints->min_uV != constraints->max_uV) &&
@@ -974,6 +1002,7 @@ static int set_supply(struct regulator_dev *rdev,
                err = -ENOMEM;
                return err;
        }
+       supply_rdev->open_count++;
 
        return 0;
 }
@@ -1720,6 +1749,9 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
        if (regulator->always_on)
                return 0;
 
+       if (!ms)
+               return regulator_disable(regulator);
+
        mutex_lock(&rdev->mutex);
        rdev->deferred_disables++;
        mutex_unlock(&rdev->mutex);
@@ -2178,9 +2210,12 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                }
        }
 
-       if (ret == 0 && best_val >= 0)
+       if (ret == 0 && best_val >= 0) {
+               unsigned long data = best_val;
+
                _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE,
-                                    (void *)best_val);
+                                    (void *)data);
+       }
 
        trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);
 
@@ -2291,8 +2326,8 @@ int regulator_set_voltage_time(struct regulator *regulator,
 EXPORT_SYMBOL_GPL(regulator_set_voltage_time);
 
 /**
- *regulator_set_voltage_time_sel - get raise/fall time
- * @regulator: regulator source
+ * regulator_set_voltage_time_sel - get raise/fall time
+ * @rdev: regulator source device
  * @old_selector: selector for starting voltage
  * @new_selector: selector for target voltage
  *
@@ -2388,6 +2423,8 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
                ret = rdev->desc->ops->list_voltage(rdev, sel);
        } else if (rdev->desc->ops->get_voltage) {
                ret = rdev->desc->ops->get_voltage(rdev);
+       } else if (rdev->desc->ops->list_voltage) {
+               ret = rdev->desc->ops->list_voltage(rdev, 0);
        } else {
                return -EINVAL;
        }
@@ -2674,6 +2711,100 @@ out:
 EXPORT_SYMBOL_GPL(regulator_set_optimum_mode);
 
 /**
+ * regulator_set_bypass_regmap - Default set_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: state to set.
+ */
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
+{
+       unsigned int val;
+
+       if (enable)
+               val = rdev->desc->bypass_mask;
+       else
+               val = 0;
+
+       return regmap_update_bits(rdev->regmap, rdev->desc->bypass_reg,
+                                 rdev->desc->bypass_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
+
+/**
+ * regulator_get_bypass_regmap - Default get_bypass() using regmap
+ *
+ * @rdev: device to operate on.
+ * @enable: current state.
+ */
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable)
+{
+       unsigned int val;
+       int ret;
+
+       ret = regmap_read(rdev->regmap, rdev->desc->bypass_reg, &val);
+       if (ret != 0)
+               return ret;
+
+       *enable = val & rdev->desc->bypass_mask;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(regulator_get_bypass_regmap);
+
+/**
+ * regulator_allow_bypass - allow the regulator to go into bypass mode
+ *
+ * @regulator: Regulator to configure
+ * @allow: enable or disable bypass mode
+ *
+ * Allow the regulator to go into bypass mode if all other consumers
+ * for the regulator also enable bypass mode and the machine
+ * constraints allow this.  Bypass mode means that the regulator is
+ * simply passing the input directly to the output with no regulation.
+ */
+int regulator_allow_bypass(struct regulator *regulator, bool enable)
+{
+       struct regulator_dev *rdev = regulator->rdev;
+       int ret = 0;
+
+       if (!rdev->desc->ops->set_bypass)
+               return 0;
+
+       if (rdev->constraints &&
+           !(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_BYPASS))
+               return 0;
+
+       mutex_lock(&rdev->mutex);
+
+       if (enable && !regulator->bypass) {
+               rdev->bypass_count++;
+
+               if (rdev->bypass_count == rdev->open_count) {
+                       ret = rdev->desc->ops->set_bypass(rdev, enable);
+                       if (ret != 0)
+                               rdev->bypass_count--;
+               }
+
+       } else if (!enable && regulator->bypass) {
+               rdev->bypass_count--;
+
+               if (rdev->bypass_count != rdev->open_count) {
+                       ret = rdev->desc->ops->set_bypass(rdev, enable);
+                       if (ret != 0)
+                               rdev->bypass_count++;
+               }
+       }
+
+       if (ret == 0)
+               regulator->bypass = enable;
+
+       mutex_unlock(&rdev->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_allow_bypass);
+
+/**
  * regulator_register_notifier - register regulator event notifier
  * @regulator: regulator source
  * @nb: notifier block
@@ -3011,7 +3142,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
 
        /* some attributes need specific methods to be displayed */
        if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) ||
-           (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0)) {
+           (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) ||
+           (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) {
                status = device_create_file(dev, &dev_attr_microvolts);
                if (status < 0)
                        return status;
@@ -3036,6 +3168,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev)
                if (status < 0)
                        return status;
        }
+       if (ops->get_bypass) {
+               status = device_create_file(dev, &dev_attr_bypass);
+               if (status < 0)
+                       return status;
+       }
 
        /* some attributes are type-specific */
        if (rdev->desc->type == REGULATOR_CURRENT) {
@@ -3124,6 +3261,8 @@ static void rdev_init_debugfs(struct regulator_dev *rdev)
                           &rdev->use_count);
        debugfs_create_u32("open_count", 0444, rdev->debugfs,
                           &rdev->open_count);
+       debugfs_create_u32("bypass_count", 0444, rdev->debugfs,
+                          &rdev->bypass_count);
 }
 
 /**
@@ -3189,8 +3328,10 @@ regulator_register(const struct regulator_desc *regulator_desc,
        rdev->desc = regulator_desc;
        if (config->regmap)
                rdev->regmap = config->regmap;
-       else
+       else if (dev_get_regmap(dev, NULL))
                rdev->regmap = dev_get_regmap(dev, NULL);
+       else if (dev->parent)
+               rdev->regmap = dev_get_regmap(dev->parent, NULL);
        INIT_LIST_HEAD(&rdev->consumer_list);
        INIT_LIST_HEAD(&rdev->list);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
index 903299c..27355b1 100644 (file)
@@ -133,8 +133,8 @@ static int da9052_dcdc_set_current_limit(struct regulator_dev *rdev, int min_uA,
            max_uA < da9052_current_limits[row][DA9052_MIN_UA])
                return -EINVAL;
 
-       for (i = 0; i < DA9052_CURRENT_RANGE; i++) {
-               if (min_uA <= da9052_current_limits[row][i]) {
+       for (i = DA9052_CURRENT_RANGE - 1; i >= 0; i--) {
+               if (da9052_current_limits[row][i] <= max_uA) {
                        reg_val = i;
                        break;
                }
index 86f655c..03a1d7c 100644 (file)
@@ -30,7 +30,7 @@ static struct regulator_init_data dummy_initdata;
 static struct regulator_ops dummy_ops;
 
 static struct regulator_desc dummy_desc = {
-       .name = "dummy",
+       .name = "regulator-dummy",
        .id = -1,
        .type = REGULATOR_VOLTAGE,
        .owner = THIS_MODULE,
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
new file mode 100644 (file)
index 0000000..339f4d7
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * FAN53555 Fairchild Digitally Programmable TinyBuck Regulator Driver.
+ *
+ * Supported Part Numbers:
+ * FAN53555UC00X/01X/03X/04X/05X
+ *
+ * Copyright (c) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package 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/module.h>
+#include <linux/param.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+#include <linux/regulator/fan53555.h>
+
+/* Voltage setting */
+#define FAN53555_VSEL0         0x00
+#define FAN53555_VSEL1         0x01
+/* Control register */
+#define FAN53555_CONTROL       0x02
+/* IC Type */
+#define FAN53555_ID1           0x03
+/* IC mask version */
+#define FAN53555_ID2           0x04
+/* Monitor register */
+#define FAN53555_MONITOR       0x05
+
+/* VSEL bit definitions */
+#define VSEL_BUCK_EN   (1 << 7)
+#define VSEL_MODE              (1 << 6)
+#define VSEL_NSEL_MASK 0x3F
+/* Chip ID and Verison */
+#define DIE_ID         0x0F    /* ID1 */
+#define DIE_REV                0x0F    /* ID2 */
+/* Control bit definitions */
+#define CTL_OUTPUT_DISCHG      (1 << 7)
+#define CTL_SLEW_MASK          (0x7 << 4)
+#define CTL_SLEW_SHIFT         4
+#define CTL_RESET                      (1 << 2)
+
+#define FAN53555_NVOLTAGES     64      /* Numbers of voltages */
+
+/* IC Type */
+enum {
+       FAN53555_CHIP_ID_00 = 0,
+       FAN53555_CHIP_ID_01,
+       FAN53555_CHIP_ID_02,
+       FAN53555_CHIP_ID_03,
+       FAN53555_CHIP_ID_04,
+       FAN53555_CHIP_ID_05,
+};
+
+struct fan53555_device_info {
+       struct regmap *regmap;
+       struct device *dev;
+       struct regulator_desc desc;
+       struct regulator_dev *rdev;
+       struct regulator_init_data *regulator;
+       /* IC Type and Rev */
+       int chip_id;
+       int chip_rev;
+       /* Voltage setting register */
+       unsigned int vol_reg;
+       unsigned int sleep_reg;
+       /* Voltage range and step(linear) */
+       unsigned int vsel_min;
+       unsigned int vsel_step;
+       /* Voltage slew rate limiting */
+       unsigned int slew_rate;
+       /* Sleep voltage cache */
+       unsigned int sleep_vol_cache;
+};
+
+static int fan53555_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+       int ret;
+
+       if (di->sleep_vol_cache == uV)
+               return 0;
+       ret = regulator_map_voltage_linear(rdev, uV, uV);
+       if (ret < 0)
+               return -EINVAL;
+       ret = regmap_update_bits(di->regmap, di->sleep_reg,
+                                       VSEL_NSEL_MASK, ret);
+       if (ret < 0)
+               return -EINVAL;
+       /* Cache the sleep voltage setting.
+        * Might not be the real voltage which is rounded */
+       di->sleep_vol_cache = uV;
+
+       return 0;
+}
+
+static int fan53555_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               regmap_update_bits(di->regmap, di->vol_reg,
+                               VSEL_MODE, VSEL_MODE);
+               break;
+       case REGULATOR_MODE_NORMAL:
+               regmap_update_bits(di->regmap, di->vol_reg, VSEL_MODE, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static unsigned int fan53555_get_mode(struct regulator_dev *rdev)
+{
+       struct fan53555_device_info *di = rdev_get_drvdata(rdev);
+       unsigned int val;
+       int ret = 0;
+
+       ret = regmap_read(di->regmap, di->vol_reg, &val);
+       if (ret < 0)
+               return ret;
+       if (val & VSEL_MODE)
+               return REGULATOR_MODE_FAST;
+       else
+               return REGULATOR_MODE_NORMAL;
+}
+
+static struct regulator_ops fan53555_regulator_ops = {
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .map_voltage = regulator_map_voltage_linear,
+       .list_voltage = regulator_list_voltage_linear,
+       .set_suspend_voltage = fan53555_set_suspend_voltage,
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+       .set_mode = fan53555_set_mode,
+       .get_mode = fan53555_get_mode,
+};
+
+/* For 00,01,03,05 options:
+ * VOUT = 0.60V + NSELx * 10mV, from 0.60 to 1.23V.
+ * For 04 option:
+ * VOUT = 0.603V + NSELx * 12.826mV, from 0.603 to 1.411V.
+ * */
+static int fan53555_device_setup(struct fan53555_device_info *di,
+                               struct fan53555_platform_data *pdata)
+{
+       unsigned int reg, data, mask;
+
+       /* Setup voltage control register */
+       switch (pdata->sleep_vsel_id) {
+       case FAN53555_VSEL_ID_0:
+               di->sleep_reg = FAN53555_VSEL0;
+               di->vol_reg = FAN53555_VSEL1;
+               break;
+       case FAN53555_VSEL_ID_1:
+               di->sleep_reg = FAN53555_VSEL1;
+               di->vol_reg = FAN53555_VSEL0;
+               break;
+       default:
+               dev_err(di->dev, "Invalid VSEL ID!\n");
+               return -EINVAL;
+       }
+       /* Init voltage range and step */
+       switch (di->chip_id) {
+       case FAN53555_CHIP_ID_00:
+       case FAN53555_CHIP_ID_01:
+       case FAN53555_CHIP_ID_03:
+       case FAN53555_CHIP_ID_05:
+               di->vsel_min = 600000;
+               di->vsel_step = 10000;
+               break;
+       case FAN53555_CHIP_ID_04:
+               di->vsel_min = 603000;
+               di->vsel_step = 12826;
+               break;
+       default:
+               dev_err(di->dev,
+                       "Chip ID[%d]\n not supported!\n", di->chip_id);
+               return -EINVAL;
+       }
+       /* Init slew rate */
+       if (pdata->slew_rate & 0x7)
+               di->slew_rate = pdata->slew_rate;
+       else
+               di->slew_rate = FAN53555_SLEW_RATE_64MV;
+       reg = FAN53555_CONTROL;
+       data = di->slew_rate << CTL_SLEW_SHIFT;
+       mask = CTL_SLEW_MASK;
+       return regmap_update_bits(di->regmap, reg, mask, data);
+}
+
+static int fan53555_regulator_register(struct fan53555_device_info *di,
+                       struct regulator_config *config)
+{
+       struct regulator_desc *rdesc = &di->desc;
+
+       rdesc->name = "fan53555-reg";
+       rdesc->ops = &fan53555_regulator_ops;
+       rdesc->type = REGULATOR_VOLTAGE;
+       rdesc->n_voltages = FAN53555_NVOLTAGES;
+       rdesc->enable_reg = di->vol_reg;
+       rdesc->enable_mask = VSEL_BUCK_EN;
+       rdesc->min_uV = di->vsel_min;
+       rdesc->uV_step = di->vsel_step;
+       rdesc->vsel_reg = di->vol_reg;
+       rdesc->vsel_mask = VSEL_NSEL_MASK;
+       rdesc->owner = THIS_MODULE;
+
+       di->rdev = regulator_register(&di->desc, config);
+       if (IS_ERR(di->rdev))
+               return PTR_ERR(di->rdev);
+       return 0;
+
+}
+
+static struct regmap_config fan53555_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+};
+
+static int __devinit fan53555_regulator_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct fan53555_device_info *di;
+       struct fan53555_platform_data *pdata;
+       struct regulator_config config = { };
+       unsigned int val;
+       int ret;
+
+       pdata = client->dev.platform_data;
+       if (!pdata || !pdata->regulator) {
+               dev_err(&client->dev, "Platform data not found!\n");
+               return -ENODEV;
+       }
+
+       di = devm_kzalloc(&client->dev, sizeof(struct fan53555_device_info),
+                                       GFP_KERNEL);
+       if (!di) {
+               dev_err(&client->dev, "Failed to allocate device info data!\n");
+               return -ENOMEM;
+       }
+       di->regmap = devm_regmap_init_i2c(client, &fan53555_regmap_config);
+       if (IS_ERR(di->regmap)) {
+               dev_err(&client->dev, "Failed to allocate regmap!\n");
+               return PTR_ERR(di->regmap);
+       }
+       di->dev = &client->dev;
+       di->regulator = pdata->regulator;
+       i2c_set_clientdata(client, di);
+       /* Get chip ID */
+       ret = regmap_read(di->regmap, FAN53555_ID1, &val);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to get chip ID!\n");
+               return -ENODEV;
+       }
+       di->chip_id = val & DIE_ID;
+       /* Get chip revision */
+       ret = regmap_read(di->regmap, FAN53555_ID2, &val);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to get chip Rev!\n");
+               return -ENODEV;
+       }
+       di->chip_rev = val & DIE_REV;
+       dev_info(&client->dev, "FAN53555 Option[%d] Rev[%d] Detected!\n",
+                               di->chip_id, di->chip_rev);
+       /* Device init */
+       ret = fan53555_device_setup(di, pdata);
+       if (ret < 0) {
+               dev_err(&client->dev, "Failed to setup device!\n");
+               return ret;
+       }
+       /* Register regulator */
+       config.dev = di->dev;
+       config.init_data = di->regulator;
+       config.regmap = di->regmap;
+       config.driver_data = di;
+       ret = fan53555_regulator_register(di, &config);
+       if (ret < 0)
+               dev_err(&client->dev, "Failed to register regulator!\n");
+       return ret;
+
+}
+
+static int __devexit fan53555_regulator_remove(struct i2c_client *client)
+{
+       struct fan53555_device_info *di = i2c_get_clientdata(client);
+
+       regulator_unregister(di->rdev);
+       return 0;
+}
+
+static const struct i2c_device_id fan53555_id[] = {
+       {"fan53555", -1},
+       { },
+};
+
+static struct i2c_driver fan53555_regulator_driver = {
+       .driver = {
+               .name = "fan53555-regulator",
+       },
+       .probe = fan53555_regulator_probe,
+       .remove = __devexit_p(fan53555_regulator_remove),
+       .id_table = fan53555_id,
+};
+
+module_i2c_driver(fan53555_regulator_driver);
+
+MODULE_AUTHOR("Yunfan Zhang <yfzhang@marvell.com>");
+MODULE_DESCRIPTION("FAN53555 regulator driver");
+MODULE_LICENSE("GPL v2");
index 1d145a0..d8ecf49 100644 (file)
@@ -73,13 +73,7 @@ static struct regulator_ops isl_core_ops = {
        .map_voltage    = regulator_map_voltage_linear,
 };
 
-static int isl6271a_get_fixed_voltage(struct regulator_dev *dev)
-{
-       return dev->desc->min_uV;
-}
-
 static struct regulator_ops isl_fixed_ops = {
-       .get_voltage    = isl6271a_get_fixed_voltage,
        .list_voltage   = regulator_list_voltage_linear,
 };
 
index 212c38e..708f4b6 100644 (file)
 #define EXTERN_DVS_USED                        0
 #define MAX_DELAY                      6
 
+/* Default DVS Mode */
+#define LP8720_DEFAULT_DVS             0
+#define LP8725_DEFAULT_DVS             BIT(2)
+
 /* dump registers in regmap-debugfs */
 #define MAX_REGISTERS                  0x0F
 
@@ -269,9 +273,9 @@ static int lp872x_regulator_enable_time(struct regulator_dev *rdev)
        return val > MAX_DELAY ? 0 : val * time_step_us;
 }
 
-static void lp872x_set_dvs(struct lp872x *lp, int gpio)
+static void lp872x_set_dvs(struct lp872x *lp, enum lp872x_dvs_sel dvs_sel,
+                       int gpio)
 {
-       enum lp872x_dvs_sel dvs_sel = lp->pdata->dvs->vsel;
        enum lp872x_dvs_state state;
 
        state = dvs_sel == SEL_V1 ? DVS_HIGH : DVS_LOW;
@@ -339,10 +343,10 @@ static int lp872x_buck_set_voltage_sel(struct regulator_dev *rdev,
        struct lp872x *lp = rdev_get_drvdata(rdev);
        enum lp872x_regulator_id buck = rdev_get_id(rdev);
        u8 addr, mask = LP872X_VOUT_M;
-       struct lp872x_dvs *dvs = lp->pdata->dvs;
+       struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
 
        if (dvs && gpio_is_valid(dvs->gpio))
-               lp872x_set_dvs(lp, dvs->gpio);
+               lp872x_set_dvs(lp, dvs->vsel, dvs->gpio);
 
        addr = lp872x_select_buck_vout_addr(lp, buck);
        if (!lp872x_is_valid_buck_addr(addr))
@@ -374,8 +378,8 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
 {
        struct lp872x *lp = rdev_get_drvdata(rdev);
        enum lp872x_regulator_id buck = rdev_get_id(rdev);
-       int i, max = ARRAY_SIZE(lp8725_buck_uA);
-       u8 addr, val;
+       int i;
+       u8 addr;
 
        switch (buck) {
        case LP8725_ID_BUCK1:
@@ -388,17 +392,15 @@ static int lp8725_buck_set_current_limit(struct regulator_dev *rdev,
                return -EINVAL;
        }
 
-       for (i = 0 ; i < max ; i++)
+       for (i = ARRAY_SIZE(lp8725_buck_uA) - 1 ; i >= 0; i--) {
                if (lp8725_buck_uA[i] >= min_uA &&
                        lp8725_buck_uA[i] <= max_uA)
-                       break;
-
-       if (i == max)
-               return -EINVAL;
-
-       val = i << LP8725_BUCK_CL_S;
+                       return lp872x_update_bits(lp, addr,
+                                                 LP8725_BUCK_CL_M,
+                                                 i << LP8725_BUCK_CL_S);
+       }
 
-       return lp872x_update_bits(lp, addr, LP8725_BUCK_CL_M, val);
+       return -EINVAL;
 }
 
 static int lp8725_buck_get_current_limit(struct regulator_dev *rdev)
@@ -727,39 +729,16 @@ static struct regulator_desc lp8725_regulator_desc[] = {
        },
 };
 
-static int lp872x_check_dvs_validity(struct lp872x *lp)
-{
-       struct lp872x_dvs *dvs = lp->pdata->dvs;
-       u8 val = 0;
-       int ret;
-
-       ret = lp872x_read_byte(lp, LP872X_GENERAL_CFG, &val);
-       if (ret)
-               return ret;
-
-       ret = 0;
-       if (lp->chipid == LP8720) {
-               if (val & LP8720_EXT_DVS_M)
-                       ret = dvs ? 0 : -EINVAL;
-       } else {
-               if ((val & LP8725_DVS1_M) == EXTERN_DVS_USED)
-                       ret = dvs ? 0 : -EINVAL;
-       }
-
-       return ret;
-}
-
 static int lp872x_init_dvs(struct lp872x *lp)
 {
        int ret, gpio;
-       struct lp872x_dvs *dvs = lp->pdata->dvs;
+       struct lp872x_dvs *dvs = lp->pdata ? lp->pdata->dvs : NULL;
        enum lp872x_dvs_state pinstate;
+       u8 mask[] = { LP8720_EXT_DVS_M, LP8725_DVS1_M | LP8725_DVS2_M };
+       u8 default_dvs_mode[] = { LP8720_DEFAULT_DVS, LP8725_DEFAULT_DVS };
 
-       ret = lp872x_check_dvs_validity(lp);
-       if (ret) {
-               dev_warn(lp->dev, "invalid dvs data: %d\n", ret);
-               return ret;
-       }
+       if (!dvs)
+               goto set_default_dvs_mode;
 
        gpio = dvs->gpio;
        if (!gpio_is_valid(gpio)) {
@@ -778,6 +757,10 @@ static int lp872x_init_dvs(struct lp872x *lp)
        lp->dvs_gpio = gpio;
 
        return 0;
+
+set_default_dvs_mode:
+       return lp872x_update_bits(lp, LP872X_GENERAL_CFG, mask[lp->chipid],
+                               default_dvs_mode[lp->chipid]);
 }
 
 static int lp872x_config(struct lp872x *lp)
@@ -785,24 +768,29 @@ static int lp872x_config(struct lp872x *lp)
        struct lp872x_platform_data *pdata = lp->pdata;
        int ret;
 
-       if (!pdata->update_config)
-               return 0;
+       if (!pdata || !pdata->update_config)
+               goto init_dvs;
 
        ret = lp872x_write_byte(lp, LP872X_GENERAL_CFG, pdata->general_config);
        if (ret)
                return ret;
 
+init_dvs:
        return lp872x_init_dvs(lp);
 }
 
 static struct regulator_init_data
 *lp872x_find_regulator_init_data(int id, struct lp872x *lp)
 {
+       struct lp872x_platform_data *pdata = lp->pdata;
        int i;
 
+       if (!pdata)
+               return NULL;
+
        for (i = 0; i < lp->num_regulators; i++) {
-               if (lp->pdata->regulator_data[i].id == id)
-                       return lp->pdata->regulator_data[i].init_data;
+               if (pdata->regulator_data[i].id == id)
+                       return pdata->regulator_data[i].init_data;
        }
 
        return NULL;
@@ -863,18 +851,12 @@ static const struct regmap_config lp872x_regmap_config = {
 static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
 {
        struct lp872x *lp;
-       struct lp872x_platform_data *pdata = cl->dev.platform_data;
        int ret, size, num_regulators;
        const int lp872x_num_regulators[] = {
                [LP8720] = LP8720_NUM_REGULATORS,
                [LP8725] = LP8725_NUM_REGULATORS,
        };
 
-       if (!pdata) {
-               dev_err(&cl->dev, "no platform data\n");
-               return -EINVAL;
-       }
-
        lp = devm_kzalloc(&cl->dev, sizeof(struct lp872x), GFP_KERNEL);
        if (!lp)
                goto err_mem;
@@ -894,7 +876,7 @@ static int lp872x_probe(struct i2c_client *cl, const struct i2c_device_id *id)
        }
 
        lp->dev = &cl->dev;
-       lp->pdata = pdata;
+       lp->pdata = cl->dev.platform_data;
        lp->chipid = id->driver_data;
        lp->num_regulators = num_regulators;
        i2c_set_clientdata(cl, lp);
index 6356e82..ba3e0aa 100644 (file)
@@ -69,6 +69,9 @@
 #define PIN_HIGH                       1
 #define ENABLE_TIME_USEC               32
 
+#define BUCK_FPWM_MASK(x)              (1 << (x))
+#define BUCK_FPWM_SHIFT(x)             (x)
+
 enum lp8788_dvs_state {
        DVS_LOW  = GPIOF_OUT_INIT_LOW,
        DVS_HIGH = GPIOF_OUT_INIT_HIGH,
@@ -86,15 +89,9 @@ enum lp8788_buck_id {
        BUCK4,
 };
 
-struct lp8788_pwm_map {
-       u8 mask;
-       u8 shift;
-};
-
 struct lp8788_buck {
        struct lp8788 *lp;
        struct regulator_dev *regulator;
-       struct lp8788_pwm_map *pmap;
        void *dvs;
 };
 
@@ -106,29 +103,6 @@ static const int lp8788_buck_vtbl[] = {
        1950000, 2000000,
 };
 
-/* buck pwm mode selection : used for set/get_mode in regulator ops
- * @forced pwm : fast mode
- * @auto pwm   : normal mode
- */
-static struct lp8788_pwm_map buck_pmap[] = {
-       [BUCK1] = {
-               .mask = LP8788_FPWM_BUCK1_M,
-               .shift = LP8788_FPWM_BUCK1_S,
-       },
-       [BUCK2] = {
-               .mask = LP8788_FPWM_BUCK2_M,
-               .shift = LP8788_FPWM_BUCK2_S,
-       },
-       [BUCK3] = {
-               .mask = LP8788_FPWM_BUCK3_M,
-               .shift = LP8788_FPWM_BUCK3_S,
-       },
-       [BUCK4] = {
-               .mask = LP8788_FPWM_BUCK4_M,
-               .shift = LP8788_FPWM_BUCK4_S,
-       },
-};
-
 static const u8 buck1_vout_addr[] = {
        LP8788_BUCK1_VOUT0, LP8788_BUCK1_VOUT1,
        LP8788_BUCK1_VOUT2, LP8788_BUCK1_VOUT3,
@@ -347,41 +321,37 @@ static int lp8788_buck_enable_time(struct regulator_dev *rdev)
 static int lp8788_buck_set_mode(struct regulator_dev *rdev, unsigned int mode)
 {
        struct lp8788_buck *buck = rdev_get_drvdata(rdev);
-       struct lp8788_pwm_map *pmap = buck->pmap;
-       u8 val;
-
-       if (!pmap)
-               return -EINVAL;
+       enum lp8788_buck_id id = rdev_get_id(rdev);
+       u8 mask, val;
 
+       mask = BUCK_FPWM_MASK(id);
        switch (mode) {
        case REGULATOR_MODE_FAST:
-               val = LP8788_FORCE_PWM << pmap->shift;
+               val = LP8788_FORCE_PWM << BUCK_FPWM_SHIFT(id);
                break;
        case REGULATOR_MODE_NORMAL:
-               val = LP8788_AUTO_PWM << pmap->shift;
+               val = LP8788_AUTO_PWM << BUCK_FPWM_SHIFT(id);
                break;
        default:
                return -EINVAL;
        }
 
-       return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, pmap->mask, val);
+       return lp8788_update_bits(buck->lp, LP8788_BUCK_PWM, mask, val);
 }
 
 static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev)
 {
        struct lp8788_buck *buck = rdev_get_drvdata(rdev);
-       struct lp8788_pwm_map *pmap = buck->pmap;
+       enum lp8788_buck_id id = rdev_get_id(rdev);
        u8 val;
        int ret;
 
-       if (!pmap)
-               return -EINVAL;
-
        ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val);
        if (ret)
                return ret;
 
-       return val & pmap->mask ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
+       return val & BUCK_FPWM_MASK(id) ?
+                               REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
 }
 
 static struct regulator_ops lp8788_buck12_ops = {
@@ -459,27 +429,6 @@ static struct regulator_desc lp8788_buck_desc[] = {
        },
 };
 
-static int lp8788_set_default_dvs_ctrl_mode(struct lp8788 *lp,
-                                       enum lp8788_buck_id id)
-{
-       u8 mask, val;
-
-       switch (id) {
-       case BUCK1:
-               mask = LP8788_BUCK1_DVS_SEL_M;
-               val  = LP8788_BUCK1_DVS_I2C;
-               break;
-       case BUCK2:
-               mask = LP8788_BUCK2_DVS_SEL_M;
-               val  = LP8788_BUCK2_DVS_I2C;
-               break;
-       default:
-               return 0;
-       }
-
-       return lp8788_update_bits(lp, LP8788_BUCK_DVS_SEL, mask, val);
-}
-
 static int _gpio_request(struct lp8788_buck *buck, int gpio, char *name)
 {
        struct device *dev = buck->lp->dev;
@@ -530,6 +479,7 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
        struct lp8788_platform_data *pdata = buck->lp->pdata;
        u8 mask[] = { LP8788_BUCK1_DVS_SEL_M, LP8788_BUCK2_DVS_SEL_M };
        u8 val[]  = { LP8788_BUCK1_DVS_PIN, LP8788_BUCK2_DVS_PIN };
+       u8 default_dvs_mode[] = { LP8788_BUCK1_DVS_I2C, LP8788_BUCK2_DVS_I2C };
 
        /* no dvs for buck3, 4 */
        if (id == BUCK3 || id == BUCK4)
@@ -550,7 +500,8 @@ static int lp8788_init_dvs(struct lp8788_buck *buck, enum lp8788_buck_id id)
                                val[id]);
 
 set_default_dvs_mode:
-       return lp8788_set_default_dvs_ctrl_mode(buck->lp, id);
+       return lp8788_update_bits(buck->lp, LP8788_BUCK_DVS_SEL, mask[id],
+                                 default_dvs_mode[id]);
 }
 
 static __devinit int lp8788_buck_probe(struct platform_device *pdev)
@@ -567,7 +518,6 @@ static __devinit int lp8788_buck_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        buck->lp = lp;
-       buck->pmap = &buck_pmap[id];
 
        ret = lp8788_init_dvs(buck, id);
        if (ret)
index d2122e4..6796eeb 100644 (file)
@@ -496,6 +496,7 @@ static struct regulator_desc lp8788_dldo_desc[] = {
                .name = "dldo12",
                .id = DLDO12,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_B,
@@ -521,6 +522,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo2",
                .id = ALDO2,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_B,
@@ -530,6 +532,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo3",
                .id = ALDO3,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_B,
@@ -539,6 +542,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo4",
                .id = ALDO4,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_B,
@@ -548,6 +552,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo5",
                .id = ALDO5,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_C,
@@ -583,6 +588,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo8",
                .id = ALDO8,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_C,
@@ -592,6 +598,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo9",
                .id = ALDO9,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_C,
@@ -601,6 +608,7 @@ static struct regulator_desc lp8788_aldo_desc[] = {
                .name = "aldo10",
                .id = ALDO10,
                .ops = &lp8788_ldo_voltage_fixed_ops,
+               .n_voltages = 1,
                .type = REGULATOR_VOLTAGE,
                .owner = THIS_MODULE,
                .enable_reg = LP8788_EN_LDO_C,
index c564af6..2a67d08 100644 (file)
@@ -66,7 +66,7 @@ enum max77686_ramp_rate {
 };
 
 struct max77686_data {
-       struct regulator_dev **rdev;
+       struct regulator_dev *rdev[MAX77686_REGULATORS];
 };
 
 static int max77686_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
@@ -265,6 +265,7 @@ static int max77686_pmic_dt_parse_pdata(struct max77686_dev *iodev,
                rmatch.of_node = NULL;
                of_regulator_match(iodev->dev, regulators_np, &rmatch, 1);
                rdata[i].initdata = rmatch.init_data;
+               rdata[i].of_node = rmatch.of_node;
        }
 
        pdata->regulators = rdata;
@@ -283,10 +284,8 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
 {
        struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct max77686_platform_data *pdata = dev_get_platdata(iodev->dev);
-       struct regulator_dev **rdev;
        struct max77686_data *max77686;
-       int i,  size;
-       int ret = 0;
+       int i, ret = 0;
        struct regulator_config config = { };
 
        dev_dbg(&pdev->dev, "%s\n", __func__);
@@ -313,45 +312,38 @@ static __devinit int max77686_pmic_probe(struct platform_device *pdev)
        if (!max77686)
                return -ENOMEM;
 
-       size = sizeof(struct regulator_dev *) * MAX77686_REGULATORS;
-       max77686->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
-       if (!max77686->rdev)
-               return -ENOMEM;
-
-       rdev = max77686->rdev;
        config.dev = &pdev->dev;
        config.regmap = iodev->regmap;
        platform_set_drvdata(pdev, max77686);
 
        for (i = 0; i < MAX77686_REGULATORS; i++) {
                config.init_data = pdata->regulators[i].initdata;
+               config.of_node = pdata->regulators[i].of_node;
 
-               rdev[i] = regulator_register(&regulators[i], &config);
-               if (IS_ERR(rdev[i])) {
-                       ret = PTR_ERR(rdev[i]);
+               max77686->rdev[i] = regulator_register(&regulators[i], &config);
+               if (IS_ERR(max77686->rdev[i])) {
+                       ret = PTR_ERR(max77686->rdev[i]);
                        dev_err(&pdev->dev,
                                "regulator init failed for %d\n", i);
-                               rdev[i] = NULL;
-                               goto err;
+                       max77686->rdev[i] = NULL;
+                       goto err;
                }
        }
 
        return 0;
 err:
        while (--i >= 0)
-               regulator_unregister(rdev[i]);
+               regulator_unregister(max77686->rdev[i]);
        return ret;
 }
 
 static int __devexit max77686_pmic_remove(struct platform_device *pdev)
 {
        struct max77686_data *max77686 = platform_get_drvdata(pdev);
-       struct regulator_dev **rdev = max77686->rdev;
        int i;
 
        for (i = 0; i < MAX77686_REGULATORS; i++)
-               if (rdev[i])
-                       regulator_unregister(rdev[i]);
+               regulator_unregister(max77686->rdev[i]);
 
        return 0;
 }
diff --git a/drivers/regulator/max8907-regulator.c b/drivers/regulator/max8907-regulator.c
new file mode 100644 (file)
index 0000000..af76075
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * max8907-regulator.c -- support regulators in max8907
+ *
+ * Copyright (C) 2010 Gyungoh Yoo <jack.yoo@maxim-ic.com>
+ * Copyright (C) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Portions based on drivers/regulator/tps65910-regulator.c,
+ *     Copyright 2010 Texas Instruments Inc.
+ *     Author: Graeme Gregory <gg@slimlogic.co.uk>
+ *     Author: Jorge Eduardo Candelaria <jedu@slimlogic.co.uk>
+ *
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/max8907.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define MAX8907_II2RR_VERSION_MASK     0xF0
+#define MAX8907_II2RR_VERSION_REV_A    0x00
+#define MAX8907_II2RR_VERSION_REV_B    0x10
+#define MAX8907_II2RR_VERSION_REV_C    0x30
+
+struct max8907_regulator {
+       struct regulator_desc desc[MAX8907_NUM_REGULATORS];
+       struct regulator_dev *rdev[MAX8907_NUM_REGULATORS];
+};
+
+#define REG_MBATT() \
+       [MAX8907_MBATT] = { \
+               .name = "MBATT", \
+               .supply_name = "mbatt", \
+               .id = MAX8907_MBATT, \
+               .ops = &max8907_mbatt_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+       }
+
+#define REG_LDO(ids, supply, base, min, max, step) \
+       [MAX8907_##ids] = { \
+               .name = #ids, \
+               .supply_name = supply, \
+               .id = MAX8907_##ids, \
+               .n_voltages = ((max) - (min)) / (step) + 1, \
+               .ops = &max8907_ldo_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               .min_uV = (min), \
+               .uV_step = (step), \
+               .vsel_reg = (base) + MAX8907_VOUT, \
+               .vsel_mask = 0x3f, \
+               .enable_reg = (base) + MAX8907_CTL, \
+               .enable_mask = MAX8907_MASK_LDO_EN, \
+       }
+
+#define REG_FIXED(ids, supply, voltage) \
+       [MAX8907_##ids] = { \
+               .name = #ids, \
+               .supply_name = supply, \
+               .id = MAX8907_##ids, \
+               .n_voltages = 1, \
+               .ops = &max8907_fixed_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               .min_uV = (voltage), \
+       }
+
+#define REG_OUT5V(ids, supply, base, voltage) \
+       [MAX8907_##ids] = { \
+               .name = #ids, \
+               .supply_name = supply, \
+               .id = MAX8907_##ids, \
+               .n_voltages = 1, \
+               .ops = &max8907_out5v_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               .min_uV = (voltage), \
+               .enable_reg = (base), \
+               .enable_mask = MAX8907_MASK_OUT5V_EN, \
+       }
+
+#define REG_BBAT(ids, supply, base, min, max, step) \
+       [MAX8907_##ids] = { \
+               .name = #ids, \
+               .supply_name = supply, \
+               .id = MAX8907_##ids, \
+               .n_voltages = ((max) - (min)) / (step) + 1, \
+               .ops = &max8907_bbat_ops, \
+               .type = REGULATOR_VOLTAGE, \
+               .owner = THIS_MODULE, \
+               .min_uV = (min), \
+               .uV_step = (step), \
+               .vsel_reg = (base), \
+               .vsel_mask = MAX8907_MASK_VBBATTCV, \
+       }
+
+#define LDO_750_50(id, supply, base) REG_LDO(id, supply, (base), \
+                       750000, 3900000, 50000)
+#define LDO_650_25(id, supply, base) REG_LDO(id, supply, (base), \
+                       650000, 2225000, 25000)
+
+static struct regulator_ops max8907_mbatt_ops = {
+};
+
+static struct regulator_ops max8907_ldo_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_ldo_hwctl_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_ops max8907_fixed_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_out5v_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+       .is_enabled = regulator_is_enabled_regmap,
+};
+
+static struct regulator_ops max8907_out5v_hwctl_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_ops max8907_bbat_ops = {
+       .list_voltage = regulator_list_voltage_linear,
+       .set_voltage_sel = regulator_set_voltage_sel_regmap,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
+static struct regulator_desc max8907_regulators[] = {
+       REG_MBATT(),
+       REG_LDO(SD1, "in-v1", MAX8907_REG_SDCTL1, 650000, 2225000, 25000),
+       REG_LDO(SD2, "in-v2", MAX8907_REG_SDCTL2, 637500, 1425000, 12500),
+       REG_LDO(SD3, "in-v3", MAX8907_REG_SDCTL3, 750000, 3900000, 50000),
+       LDO_750_50(LDO1, "in1", MAX8907_REG_LDOCTL1),
+       LDO_650_25(LDO2, "in2", MAX8907_REG_LDOCTL2),
+       LDO_650_25(LDO3, "in3", MAX8907_REG_LDOCTL3),
+       LDO_750_50(LDO4, "in4", MAX8907_REG_LDOCTL4),
+       LDO_750_50(LDO5, "in5", MAX8907_REG_LDOCTL5),
+       LDO_750_50(LDO6, "in6", MAX8907_REG_LDOCTL6),
+       LDO_750_50(LDO7, "in7", MAX8907_REG_LDOCTL7),
+       LDO_750_50(LDO8, "in8", MAX8907_REG_LDOCTL8),
+       LDO_750_50(LDO9, "in9", MAX8907_REG_LDOCTL9),
+       LDO_750_50(LDO10, "in10", MAX8907_REG_LDOCTL10),
+       LDO_750_50(LDO11, "in11", MAX8907_REG_LDOCTL11),
+       LDO_750_50(LDO12, "in12", MAX8907_REG_LDOCTL12),
+       LDO_750_50(LDO13, "in13", MAX8907_REG_LDOCTL13),
+       LDO_750_50(LDO14, "in14", MAX8907_REG_LDOCTL14),
+       LDO_750_50(LDO15, "in15", MAX8907_REG_LDOCTL15),
+       LDO_750_50(LDO16, "in16", MAX8907_REG_LDOCTL16),
+       LDO_650_25(LDO17, "in17", MAX8907_REG_LDOCTL17),
+       LDO_650_25(LDO18, "in18", MAX8907_REG_LDOCTL18),
+       LDO_750_50(LDO19, "in19", MAX8907_REG_LDOCTL19),
+       LDO_750_50(LDO20, "in20", MAX8907_REG_LDOCTL20),
+       REG_OUT5V(OUT5V, "mbatt", MAX8907_REG_OUT5VEN, 5000000),
+       REG_OUT5V(OUT33V, "mbatt",  MAX8907_REG_OUT33VEN, 3300000),
+       REG_BBAT(BBAT, "MBATT", MAX8907_REG_BBAT_CNFG,
+                                               2400000, 3000000, 200000),
+       REG_FIXED(SDBY, "MBATT", 1200000),
+       REG_FIXED(VRTC, "MBATT", 3300000),
+};
+
+#ifdef CONFIG_OF
+
+#define MATCH(_name, _id) \
+       [MAX8907_##_id] = { \
+               .name = #_name, \
+               .driver_data = (void *)&max8907_regulators[MAX8907_##_id], \
+       }
+
+static struct of_regulator_match max8907_matches[] = {
+       MATCH(mbatt, MBATT),
+       MATCH(sd1, SD1),
+       MATCH(sd2, SD2),
+       MATCH(sd3, SD3),
+       MATCH(ldo1, LDO1),
+       MATCH(ldo2, LDO2),
+       MATCH(ldo3, LDO3),
+       MATCH(ldo4, LDO4),
+       MATCH(ldo5, LDO5),
+       MATCH(ldo6, LDO6),
+       MATCH(ldo7, LDO7),
+       MATCH(ldo8, LDO8),
+       MATCH(ldo9, LDO9),
+       MATCH(ldo10, LDO10),
+       MATCH(ldo11, LDO11),
+       MATCH(ldo12, LDO12),
+       MATCH(ldo13, LDO13),
+       MATCH(ldo14, LDO14),
+       MATCH(ldo15, LDO15),
+       MATCH(ldo16, LDO16),
+       MATCH(ldo17, LDO17),
+       MATCH(ldo18, LDO18),
+       MATCH(ldo19, LDO19),
+       MATCH(ldo20, LDO20),
+       MATCH(out5v, OUT5V),
+       MATCH(out33v, OUT33V),
+       MATCH(bbat, BBAT),
+       MATCH(sdby, SDBY),
+       MATCH(vrtc, VRTC),
+};
+
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.parent->of_node;
+       struct device_node *regulators;
+       int ret;
+
+       if (!pdev->dev.parent->of_node)
+               return 0;
+
+       regulators = of_find_node_by_name(np, "regulators");
+       if (!regulators) {
+               dev_err(&pdev->dev, "regulators node not found\n");
+               return -EINVAL;
+       }
+
+       ret = of_regulator_match(pdev->dev.parent, regulators,
+                                max8907_matches,
+                                ARRAY_SIZE(max8907_matches));
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Error parsing regulator init data: %d\n",
+                       ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+       return max8907_matches[index].init_data;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+       return max8907_matches[index].of_node;
+}
+#else
+static int max8907_regulator_parse_dt(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline struct regulator_init_data *match_init_data(int index)
+{
+       return NULL;
+}
+
+static inline struct device_node *match_of_node(int index)
+{
+       return NULL;
+}
+#endif
+
+static __devinit int max8907_regulator_probe(struct platform_device *pdev)
+{
+       struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
+       struct max8907_platform_data *pdata = dev_get_platdata(max8907->dev);
+       int ret;
+       struct max8907_regulator *pmic;
+       unsigned int val;
+       int i;
+       struct regulator_config config = {};
+       struct regulator_init_data *idata;
+       const char *mbatt_rail_name = NULL;
+
+       ret = max8907_regulator_parse_dt(pdev);
+       if (ret)
+               return ret;
+
+       pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+       if (!pmic) {
+               dev_err(&pdev->dev, "Failed to alloc pmic\n");
+               return -ENOMEM;
+       }
+       platform_set_drvdata(pdev, pmic);
+
+       memcpy(pmic->desc, max8907_regulators, sizeof(pmic->desc));
+
+       /* Backwards compatibility with MAX8907B; SD1 uses different voltages */
+       regmap_read(max8907->regmap_gen, MAX8907_REG_II2RR, &val);
+       if ((val & MAX8907_II2RR_VERSION_MASK) ==
+           MAX8907_II2RR_VERSION_REV_B) {
+               pmic->desc[MAX8907_SD1].min_uV = 637500;
+               pmic->desc[MAX8907_SD1].uV_step = 12500;
+               pmic->desc[MAX8907_SD1].n_voltages =
+                                               (1425000 - 637500) / 12500 + 1;
+       }
+
+       for (i = 0; i < MAX8907_NUM_REGULATORS; i++) {
+               config.dev = pdev->dev.parent;
+               if (pdata)
+                       idata = pdata->init_data[i];
+               else
+                       idata = match_init_data(i);
+               config.init_data = idata;
+               config.driver_data = pmic;
+               config.regmap = max8907->regmap_gen;
+               config.of_node = match_of_node(i);
+
+               switch (pmic->desc[i].id) {
+               case MAX8907_MBATT:
+                       if (idata && idata->constraints.name)
+                               mbatt_rail_name = idata->constraints.name;
+                       else
+                               mbatt_rail_name = pmic->desc[i].name;
+                       break;
+               case MAX8907_BBAT:
+               case MAX8907_SDBY:
+               case MAX8907_VRTC:
+                       idata->supply_regulator = mbatt_rail_name;
+                       break;
+               }
+
+               if (pmic->desc[i].ops == &max8907_ldo_ops) {
+                       regmap_read(config.regmap, pmic->desc[i].enable_reg,
+                                   &val);
+                       if ((val & MAX8907_MASK_LDO_SEQ) !=
+                           MAX8907_MASK_LDO_SEQ)
+                               pmic->desc[i].ops = &max8907_ldo_hwctl_ops;
+               } else if (pmic->desc[i].ops == &max8907_out5v_ops) {
+                       regmap_read(config.regmap, pmic->desc[i].enable_reg,
+                                   &val);
+                       if ((val & (MAX8907_MASK_OUT5V_VINEN |
+                                               MAX8907_MASK_OUT5V_ENSRC)) !=
+                           MAX8907_MASK_OUT5V_ENSRC)
+                               pmic->desc[i].ops = &max8907_out5v_hwctl_ops;
+               }
+
+               pmic->rdev[i] = regulator_register(&pmic->desc[i], &config);
+               if (IS_ERR(pmic->rdev[i])) {
+                       dev_err(&pdev->dev,
+                               "failed to register %s regulator\n",
+                               pmic->desc[i].name);
+                       ret = PTR_ERR(pmic->rdev[i]);
+                       goto err_unregister_regulator;
+               }
+       }
+
+       return 0;
+
+err_unregister_regulator:
+       while (--i >= 0)
+               regulator_unregister(pmic->rdev[i]);
+       return ret;
+}
+
+static __devexit int max8907_regulator_remove(struct platform_device *pdev)
+{
+       struct max8907_regulator *pmic = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < MAX8907_NUM_REGULATORS; i++)
+               regulator_unregister(pmic->rdev[i]);
+
+       return 0;
+}
+
+static struct platform_driver max8907_regulator_driver = {
+       .driver = {
+                  .name = "max8907-regulator",
+                  .owner = THIS_MODULE,
+                  },
+       .probe = max8907_regulator_probe,
+       .remove = __devexit_p(max8907_regulator_remove),
+};
+
+static int __init max8907_regulator_init(void)
+{
+       return platform_driver_register(&max8907_regulator_driver);
+}
+
+subsys_initcall(max8907_regulator_init);
+
+static void __exit max8907_reg_exit(void)
+{
+       platform_driver_unregister(&max8907_regulator_driver);
+}
+
+module_exit(max8907_reg_exit);
+
+MODULE_DESCRIPTION("MAX8907 regulator driver");
+MODULE_AUTHOR("Gyungoh Yoo <jack.yoo@maxim-ic.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:max8907-regulator");
index 4932e34..0801a6d 100644 (file)
 #include <linux/module.h>
 #include "mc13xxx.h"
 
+#define MC13783_REG_SWITCHERS0                 24
+/* Enable does not exist for SW1A */
+#define MC13783_REG_SWITCHERS0_SW1AEN                  0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL                        0
+#define MC13783_REG_SWITCHERS0_SW1AVSEL_M              (63 << 0)
+
+#define MC13783_REG_SWITCHERS1                 25
+/* Enable does not exist for SW1B */
+#define MC13783_REG_SWITCHERS1_SW1BEN                  0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL                        0
+#define MC13783_REG_SWITCHERS1_SW1BVSEL_M              (63 << 0)
+
+#define MC13783_REG_SWITCHERS2                 26
+/* Enable does not exist for SW2A */
+#define MC13783_REG_SWITCHERS2_SW2AEN                  0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL                        0
+#define MC13783_REG_SWITCHERS2_SW2AVSEL_M              (63 << 0)
+
+#define MC13783_REG_SWITCHERS3                 27
+/* Enable does not exist for SW2B */
+#define MC13783_REG_SWITCHERS3_SW2BEN                  0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL                        0
+#define MC13783_REG_SWITCHERS3_SW2BVSEL_M              (63 << 0)
+
 #define MC13783_REG_SWITCHERS5                 29
 #define MC13783_REG_SWITCHERS5_SW3EN                   (1 << 20)
 #define MC13783_REG_SWITCHERS5_SW3VSEL                 18
 
 
 /* Voltage Values */
+static const int mc13783_sw1x_val[] = {
+       900000, 925000, 950000, 975000,
+       1000000, 1025000, 1050000, 1075000,
+       1100000, 1125000, 1150000, 1175000,
+       1200000, 1225000, 1250000, 1275000,
+       1300000, 1325000, 1350000, 1375000,
+       1400000, 1425000, 1450000, 1475000,
+       1500000, 1525000, 1550000, 1575000,
+       1600000, 1625000, 1650000, 1675000,
+       1700000, 1700000, 1700000, 1700000,
+       1800000, 1800000, 1800000, 1800000,
+       1850000, 1850000, 1850000, 1850000,
+       2000000, 2000000, 2000000, 2000000,
+       2100000, 2100000, 2100000, 2100000,
+       2200000, 2200000, 2200000, 2200000,
+       2200000, 2200000, 2200000, 2200000,
+       2200000, 2200000, 2200000, 2200000,
+};
+
+static const int mc13783_sw2x_val[] = {
+       900000, 925000, 950000, 975000,
+       1000000, 1025000, 1050000, 1075000,
+       1100000, 1125000, 1150000, 1175000,
+       1200000, 1225000, 1250000, 1275000,
+       1300000, 1325000, 1350000, 1375000,
+       1400000, 1425000, 1450000, 1475000,
+       1500000, 1525000, 1550000, 1575000,
+       1600000, 1625000, 1650000, 1675000,
+       1700000, 1700000, 1700000, 1700000,
+       1800000, 1800000, 1800000, 1800000,
+       1900000, 1900000, 1900000, 1900000,
+       2000000, 2000000, 2000000, 2000000,
+       2100000, 2100000, 2100000, 2100000,
+       2200000, 2200000, 2200000, 2200000,
+       2200000, 2200000, 2200000, 2200000,
+       2200000, 2200000, 2200000, 2200000,
+};
+
 static const unsigned int mc13783_sw3_val[] = {
        5000000, 5000000, 5000000, 5500000,
 };
@@ -188,6 +250,10 @@ static struct regulator_ops mc13783_gpo_regulator_ops;
        MC13783_DEFINE(REG, _name, _reg, _vsel_reg, _voltages)
 
 static struct mc13xxx_regulator mc13783_regulators[] = {
+       MC13783_DEFINE_SW(SW1A, SWITCHERS0, SWITCHERS0, mc13783_sw1x_val),
+       MC13783_DEFINE_SW(SW1B, SWITCHERS1, SWITCHERS1, mc13783_sw1x_val),
+       MC13783_DEFINE_SW(SW2A, SWITCHERS2, SWITCHERS2, mc13783_sw2x_val),
+       MC13783_DEFINE_SW(SW2B, SWITCHERS3, SWITCHERS3, mc13783_sw2x_val),
        MC13783_DEFINE_SW(SW3, SWITCHERS5, SWITCHERS5, mc13783_sw3_val),
 
        MC13783_FIXED_DEFINE(REG, VAUDIO, REGULATORMODE0, mc13783_vaudio_val),
@@ -238,9 +304,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
 
        BUG_ON(val & ~mask);
 
+       mc13xxx_lock(priv->mc13xxx);
        ret = mc13xxx_reg_read(mc13783, MC13783_REG_POWERMISC, &valread);
        if (ret)
-               return ret;
+               goto out;
 
        /* Update the stored state for Power Gates. */
        priv->powermisc_pwgt_state =
@@ -253,7 +320,10 @@ static int mc13783_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
        valread = (valread & ~MC13783_REG_POWERMISC_PWGTSPI_M) |
                                                priv->powermisc_pwgt_state;
 
-       return mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+       ret = mc13xxx_reg_write(mc13783, MC13783_REG_POWERMISC, valread);
+out:
+       mc13xxx_unlock(priv->mc13xxx);
+       return ret;
 }
 
 static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
@@ -261,7 +331,6 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
        struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
        int id = rdev_get_id(rdev);
-       int ret;
        u32 en_val = mc13xxx_regulators[id].enable_bit;
 
        dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -271,12 +340,8 @@ static int mc13783_gpo_regulator_enable(struct regulator_dev *rdev)
            id == MC13783_REG_PWGT2SPI)
                en_val = 0;
 
-       mc13xxx_lock(priv->mc13xxx);
-       ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+       return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
                                        en_val);
-       mc13xxx_unlock(priv->mc13xxx);
-
-       return ret;
 }
 
 static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
@@ -284,7 +349,6 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
        struct mc13xxx_regulator *mc13xxx_regulators = priv->mc13xxx_regulators;
        int id = rdev_get_id(rdev);
-       int ret;
        u32 dis_val = 0;
 
        dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -294,12 +358,8 @@ static int mc13783_gpo_regulator_disable(struct regulator_dev *rdev)
            id == MC13783_REG_PWGT2SPI)
                dis_val = mc13xxx_regulators[id].enable_bit;
 
-       mc13xxx_lock(priv->mc13xxx);
-       ret = mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
+       return mc13783_powermisc_rmw(priv, mc13xxx_regulators[id].enable_bit,
                                        dis_val);
-       mc13xxx_unlock(priv->mc13xxx);
-
-       return ret;
 }
 
 static int mc13783_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -330,7 +390,6 @@ static struct regulator_ops mc13783_gpo_regulator_ops = {
        .is_enabled = mc13783_gpo_regulator_is_enabled,
        .list_voltage = regulator_list_voltage_table,
        .set_voltage = mc13xxx_fixed_regulator_set_voltage,
-       .get_voltage = mc13xxx_fixed_regulator_get_voltage,
 };
 
 static int __devinit mc13783_regulator_probe(struct platform_device *pdev)
index b388b74..1fa6381 100644 (file)
@@ -305,9 +305,10 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
 
        BUG_ON(val & ~mask);
 
+       mc13xxx_lock(priv->mc13xxx);
        ret = mc13xxx_reg_read(mc13892, MC13892_POWERMISC, &valread);
        if (ret)
-               return ret;
+               goto out;
 
        /* Update the stored state for Power Gates. */
        priv->powermisc_pwgt_state =
@@ -320,14 +321,16 @@ static int mc13892_powermisc_rmw(struct mc13xxx_regulator_priv *priv, u32 mask,
        valread = (valread & ~MC13892_POWERMISC_PWGTSPI_M) |
                priv->powermisc_pwgt_state;
 
-       return mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+       ret = mc13xxx_reg_write(mc13892, MC13892_POWERMISC, valread);
+out:
+       mc13xxx_unlock(priv->mc13xxx);
+       return ret;
 }
 
 static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
 {
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
        int id = rdev_get_id(rdev);
-       int ret;
        u32 en_val = mc13892_regulators[id].enable_bit;
        u32 mask = mc13892_regulators[id].enable_bit;
 
@@ -340,18 +343,13 @@ static int mc13892_gpo_regulator_enable(struct regulator_dev *rdev)
        if (id == MC13892_GPO4)
                mask |= MC13892_POWERMISC_GPO4ADINEN;
 
-       mc13xxx_lock(priv->mc13xxx);
-       ret = mc13892_powermisc_rmw(priv, mask, en_val);
-       mc13xxx_unlock(priv->mc13xxx);
-
-       return ret;
+       return mc13892_powermisc_rmw(priv, mask, en_val);
 }
 
 static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
 {
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
        int id = rdev_get_id(rdev);
-       int ret;
        u32 dis_val = 0;
 
        dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
@@ -360,12 +358,8 @@ static int mc13892_gpo_regulator_disable(struct regulator_dev *rdev)
        if (id == MC13892_PWGT1SPI || id == MC13892_PWGT2SPI)
                dis_val = mc13892_regulators[id].enable_bit;
 
-       mc13xxx_lock(priv->mc13xxx);
-       ret = mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
+       return mc13892_powermisc_rmw(priv, mc13892_regulators[id].enable_bit,
                dis_val);
-       mc13xxx_unlock(priv->mc13xxx);
-
-       return ret;
 }
 
 static int mc13892_gpo_regulator_is_enabled(struct regulator_dev *rdev)
@@ -396,14 +390,13 @@ static struct regulator_ops mc13892_gpo_regulator_ops = {
        .is_enabled = mc13892_gpo_regulator_is_enabled,
        .list_voltage = regulator_list_voltage_table,
        .set_voltage = mc13xxx_fixed_regulator_set_voltage,
-       .get_voltage = mc13xxx_fixed_regulator_get_voltage,
 };
 
-static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
+static int mc13892_sw_regulator_get_voltage_sel(struct regulator_dev *rdev)
 {
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
        int ret, id = rdev_get_id(rdev);
-       unsigned int val, hi;
+       unsigned int val;
 
        dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
 
@@ -414,17 +407,11 @@ static int mc13892_sw_regulator_get_voltage(struct regulator_dev *rdev)
        if (ret)
                return ret;
 
-       hi  = val & MC13892_SWITCHERS0_SWxHI;
        val = (val & mc13892_regulators[id].vsel_mask)
                >> mc13892_regulators[id].vsel_shift;
 
        dev_dbg(rdev_get_dev(rdev), "%s id: %d val: %d\n", __func__, id, val);
 
-       if (hi)
-               val = (25000 * val) + 1100000;
-       else
-               val = (25000 * val) + 600000;
-
        return val;
 }
 
@@ -432,37 +419,25 @@ static int mc13892_sw_regulator_set_voltage_sel(struct regulator_dev *rdev,
                                                unsigned selector)
 {
        struct mc13xxx_regulator_priv *priv = rdev_get_drvdata(rdev);
-       int hi, value, mask, id = rdev_get_id(rdev);
-       u32 valread;
+       int volt, mask, id = rdev_get_id(rdev);
+       u32 reg_value;
        int ret;
 
-       value = rdev->desc->volt_table[selector];
+       volt = rdev->desc->volt_table[selector];
+       mask = mc13892_regulators[id].vsel_mask;
+       reg_value = selector << mc13892_regulators[id].vsel_shift;
+
+       if (volt > 1375000) {
+               mask |= MC13892_SWITCHERS0_SWxHI;
+               reg_value |= MC13892_SWITCHERS0_SWxHI;
+       } else if (volt < 1100000) {
+               mask |= MC13892_SWITCHERS0_SWxHI;
+               reg_value &= ~MC13892_SWITCHERS0_SWxHI;
+       }
 
        mc13xxx_lock(priv->mc13xxx);
-       ret = mc13xxx_reg_read(priv->mc13xxx,
-               mc13892_regulators[id].vsel_reg, &valread);
-       if (ret)
-               goto err;
-
-       if (value > 1375000)
-               hi = 1;
-       else if (value < 1100000)
-               hi = 0;
-       else
-               hi = valread & MC13892_SWITCHERS0_SWxHI;
-
-       if (hi) {
-               value = (value - 1100000) / 25000;
-               value |= MC13892_SWITCHERS0_SWxHI;
-       } else
-               value = (value - 600000) / 25000;
-
-       mask = mc13892_regulators[id].vsel_mask | MC13892_SWITCHERS0_SWxHI;
-       valread = (valread & ~mask) |
-                       (value << mc13892_regulators[id].vsel_shift);
-       ret = mc13xxx_reg_write(priv->mc13xxx, mc13892_regulators[id].vsel_reg,
-                       valread);
-err:
+       ret = mc13xxx_reg_rmw(priv->mc13xxx, mc13892_regulators[id].reg, mask,
+                             reg_value);
        mc13xxx_unlock(priv->mc13xxx);
 
        return ret;
@@ -471,7 +446,7 @@ err:
 static struct regulator_ops mc13892_sw_regulator_ops = {
        .list_voltage = regulator_list_voltage_table,
        .set_voltage_sel = mc13892_sw_regulator_set_voltage_sel,
-       .get_voltage = mc13892_sw_regulator_get_voltage,
+       .get_voltage_sel = mc13892_sw_regulator_get_voltage_sel,
 };
 
 static int mc13892_vcam_set_mode(struct regulator_dev *rdev, unsigned int mode)
index d6eda28..88cbb83 100644 (file)
@@ -143,30 +143,21 @@ int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
                __func__, id, min_uV, max_uV);
 
        if (min_uV <= rdev->desc->volt_table[0] &&
-           rdev->desc->volt_table[0] <= max_uV)
+           rdev->desc->volt_table[0] <= max_uV) {
+               *selector = 0;
                return 0;
-       else
+       } else {
                return -EINVAL;
+       }
 }
 EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_set_voltage);
 
-int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev)
-{
-       int id = rdev_get_id(rdev);
-
-       dev_dbg(rdev_get_dev(rdev), "%s id: %d\n", __func__, id);
-
-       return rdev->desc->volt_table[0];
-}
-EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_get_voltage);
-
 struct regulator_ops mc13xxx_fixed_regulator_ops = {
        .enable = mc13xxx_regulator_enable,
        .disable = mc13xxx_regulator_disable,
        .is_enabled = mc13xxx_regulator_is_enabled,
        .list_voltage = regulator_list_voltage_table,
        .set_voltage = mc13xxx_fixed_regulator_set_voltage,
-       .get_voltage = mc13xxx_fixed_regulator_get_voltage,
 };
 EXPORT_SYMBOL_GPL(mc13xxx_fixed_regulator_ops);
 
index eaff551..06c8903 100644 (file)
@@ -34,7 +34,6 @@ struct mc13xxx_regulator_priv {
 
 extern int mc13xxx_fixed_regulator_set_voltage(struct regulator_dev *rdev,
                int min_uV, int max_uV, unsigned *selector);
-extern int mc13xxx_fixed_regulator_get_voltage(struct regulator_dev *rdev);
 
 #ifdef CONFIG_OF
 extern int mc13xxx_get_num_regulators_dt(struct platform_device *pdev);
index 3e4106f..6f68491 100644 (file)
@@ -92,16 +92,18 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev,
 EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
 
 /**
- * of_regulator_match - extract regulator init data when node
- * property "regulator-compatible" matches with the regulator name.
+ * of_regulator_match - extract multiple regulator init data from device tree.
  * @dev: device requesting the data
  * @node: parent device node of the regulators
  * @matches: match table for the regulators
  * @num_matches: number of entries in match table
  *
- * This function uses a match table specified by the regulator driver and
- * looks up the corresponding init data in the device tree  if
- * regulator-compatible matches. Note that the match table is modified
+ * This function uses a match table specified by the regulator driver to
+ * parse regulator init data from the device tree. @node is expected to
+ * contain a set of child nodes, each providing the init data for one
+ * regulator. The data parsed from a child node will be matched to a regulator
+ * based on either the deprecated property regulator-compatible if present,
+ * or otherwise the child node's name. Note that the match table is modified
  * in place.
  *
  * Returns the number of matches found or a negative error code on failure.
@@ -112,26 +114,23 @@ int of_regulator_match(struct device *dev, struct device_node *node,
 {
        unsigned int count = 0;
        unsigned int i;
-       const char *regulator_comp;
+       const char *name;
        struct device_node *child;
 
        if (!dev || !node)
                return -EINVAL;
 
        for_each_child_of_node(node, child) {
-               regulator_comp = of_get_property(child,
+               name = of_get_property(child,
                                        "regulator-compatible", NULL);
-               if (!regulator_comp) {
-                       dev_err(dev, "regulator-compatible is missing for node %s\n",
-                               child->name);
-                       continue;
-               }
+               if (!name)
+                       name = child->name;
                for (i = 0; i < num_matches; i++) {
                        struct of_regulator_match *match = &matches[i];
                        if (match->of_node)
                                continue;
 
-                       if (strcmp(match->name, regulator_comp))
+                       if (strcmp(match->name, name))
                                continue;
 
                        match->init_data =
index 46c7e88..2ba7502 100644 (file)
@@ -443,44 +443,6 @@ static int palmas_list_voltage_ldo(struct regulator_dev *dev,
        return  850000 + (selector * 50000);
 }
 
-static int palmas_get_voltage_ldo_sel(struct regulator_dev *dev)
-{
-       struct palmas_pmic *pmic = rdev_get_drvdata(dev);
-       int id = rdev_get_id(dev);
-       int selector;
-       unsigned int reg;
-       unsigned int addr;
-
-       addr = palmas_regs_info[id].vsel_addr;
-
-       palmas_ldo_read(pmic->palmas, addr, &reg);
-
-       selector = reg & PALMAS_LDO1_VOLTAGE_VSEL_MASK;
-
-       /* Adjust selector to match list_voltage ranges */
-       if (selector > 49)
-               selector = 49;
-
-       return selector;
-}
-
-static int palmas_set_voltage_ldo_sel(struct regulator_dev *dev,
-               unsigned selector)
-{
-       struct palmas_pmic *pmic = rdev_get_drvdata(dev);
-       int id = rdev_get_id(dev);
-       unsigned int reg = 0;
-       unsigned int addr;
-
-       addr = palmas_regs_info[id].vsel_addr;
-
-       reg = selector;
-
-       palmas_ldo_write(pmic->palmas, addr, reg);
-
-       return 0;
-}
-
 static int palmas_map_voltage_ldo(struct regulator_dev *rdev,
                int min_uV, int max_uV)
 {
@@ -505,8 +467,8 @@ static struct regulator_ops palmas_ops_ldo = {
        .is_enabled             = palmas_is_enabled_ldo,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
-       .get_voltage_sel        = palmas_get_voltage_ldo_sel,
-       .set_voltage_sel        = palmas_set_voltage_ldo_sel,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .list_voltage           = palmas_list_voltage_ldo,
        .map_voltage            = palmas_map_voltage_ldo,
 };
@@ -757,6 +719,9 @@ static __devinit int palmas_probe(struct platform_device *pdev)
 
                pmic->desc[id].type = REGULATOR_VOLTAGE;
                pmic->desc[id].owner = THIS_MODULE;
+               pmic->desc[id].vsel_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
+                                               palmas_regs_info[id].vsel_addr);
+               pmic->desc[id].vsel_mask = PALMAS_LDO1_VOLTAGE_VSEL_MASK;
                pmic->desc[id].enable_reg = PALMAS_BASE_TO_REG(PALMAS_LDO_BASE,
                                                palmas_regs_info[id].ctrl_addr);
                pmic->desc[id].enable_mask = PALMAS_LDO1_CTRL_MODE_ACTIVE;
index 4669dc9..926f9c8 100644 (file)
@@ -24,7 +24,7 @@
 #include <linux/mfd/samsung/s2mps11.h>
 
 struct s2mps11_info {
-       struct regulator_dev **rdev;
+       struct regulator_dev *rdev[S2MPS11_REGULATOR_MAX];
 
        int ramp_delay2;
        int ramp_delay34;
@@ -236,9 +236,8 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
        struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent);
        struct sec_platform_data *pdata = dev_get_platdata(iodev->dev);
        struct regulator_config config = { };
-       struct regulator_dev **rdev;
        struct s2mps11_info *s2mps11;
-       int i, ret, size;
+       int i, ret;
        unsigned char ramp_enable, ramp_reg = 0;
 
        if (!pdata) {
@@ -251,13 +250,6 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
        if (!s2mps11)
                return -ENOMEM;
 
-       size = sizeof(struct regulator_dev *) * S2MPS11_REGULATOR_MAX;
-       s2mps11->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
-       if (!s2mps11->rdev) {
-               return -ENOMEM;
-       }
-
-       rdev = s2mps11->rdev;
        platform_set_drvdata(pdev, s2mps11);
 
        s2mps11->ramp_delay2 = pdata->buck2_ramp_delay;
@@ -297,12 +289,12 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
                config.init_data = pdata->regulators[i].initdata;
                config.driver_data = s2mps11;
 
-               rdev[i] = regulator_register(&regulators[i], &config);
-               if (IS_ERR(rdev[i])) {
-                       ret = PTR_ERR(rdev[i]);
+               s2mps11->rdev[i] = regulator_register(&regulators[i], &config);
+               if (IS_ERR(s2mps11->rdev[i])) {
+                       ret = PTR_ERR(s2mps11->rdev[i]);
                        dev_err(&pdev->dev, "regulator init failed for %d\n",
                                i);
-                       rdev[i] = NULL;
+                       s2mps11->rdev[i] = NULL;
                        goto err;
                }
        }
@@ -310,8 +302,7 @@ static __devinit int s2mps11_pmic_probe(struct platform_device *pdev)
        return 0;
 err:
        for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
-               if (rdev[i])
-                       regulator_unregister(rdev[i]);
+               regulator_unregister(s2mps11->rdev[i]);
 
        return ret;
 }
@@ -319,12 +310,10 @@ err:
 static int __devexit s2mps11_pmic_remove(struct platform_device *pdev)
 {
        struct s2mps11_info *s2mps11 = platform_get_drvdata(pdev);
-       struct regulator_dev **rdev = s2mps11->rdev;
        int i;
 
        for (i = 0; i < S2MPS11_REGULATOR_MAX; i++)
-               if (rdev[i])
-                       regulator_unregister(rdev[i]);
+               regulator_unregister(s2mps11->rdev[i]);
 
        return 0;
 }
index 947ece9..058d2f2 100644 (file)
@@ -502,15 +502,13 @@ static int set_current_limit(struct regulator_dev *rdev, int min_uA,
        if (info->n_ilimsels == 1)
                return -EINVAL;
 
-       for (i = 0; i < info->n_ilimsels; i++)
+       for (i = info->n_ilimsels - 1; i >= 0; i--) {
                if (min_uA <= info->ilimsels[i] &&
                    max_uA >= info->ilimsels[i])
-                       break;
-
-       if (i >= info->n_ilimsels)
-               return -EINVAL;
+                       return write_field(hw, &info->ilimsel, i);
+       }
 
-       return write_field(hw, &info->ilimsel, i);
+       return -EINVAL;
 }
 
 static int get_current_limit(struct regulator_dev *rdev)
index 19241fc..ce1e7cb 100644 (file)
@@ -57,9 +57,6 @@
 struct tps6586x_regulator {
        struct regulator_desc desc;
 
-       int volt_reg;
-       int volt_shift;
-       int volt_nbits;
        int enable_bit[2];
        int enable_reg[2];
 
@@ -81,10 +78,10 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
        int ret, val, rid = rdev_get_id(rdev);
        uint8_t mask;
 
-       val = selector << ri->volt_shift;
-       mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
+       val = selector << (ffs(rdev->desc->vsel_mask) - 1);
+       mask = rdev->desc->vsel_mask;
 
-       ret = tps6586x_update(parent, ri->volt_reg, val, mask);
+       ret = tps6586x_update(parent, rdev->desc->vsel_reg, val, mask);
        if (ret)
                return ret;
 
@@ -100,66 +97,17 @@ static int tps6586x_set_voltage_sel(struct regulator_dev *rdev,
        return ret;
 }
 
-static int tps6586x_get_voltage_sel(struct regulator_dev *rdev)
-{
-       struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
-       struct device *parent = to_tps6586x_dev(rdev);
-       uint8_t val, mask;
-       int ret;
-
-       ret = tps6586x_read(parent, ri->volt_reg, &val);
-       if (ret)
-               return ret;
-
-       mask = ((1 << ri->volt_nbits) - 1) << ri->volt_shift;
-       val = (val & mask) >> ri->volt_shift;
-
-       if (val >= ri->desc.n_voltages)
-               BUG();
-
-       return val;
-}
-
-static int tps6586x_regulator_enable(struct regulator_dev *rdev)
-{
-       struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
-       struct device *parent = to_tps6586x_dev(rdev);
-
-       return tps6586x_set_bits(parent, ri->enable_reg[0],
-                                1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_disable(struct regulator_dev *rdev)
-{
-       struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
-       struct device *parent = to_tps6586x_dev(rdev);
-
-       return tps6586x_clr_bits(parent, ri->enable_reg[0],
-                                1 << ri->enable_bit[0]);
-}
-
-static int tps6586x_regulator_is_enabled(struct regulator_dev *rdev)
-{
-       struct tps6586x_regulator *ri = rdev_get_drvdata(rdev);
-       struct device *parent = to_tps6586x_dev(rdev);
-       uint8_t reg_val;
-       int ret;
-
-       ret = tps6586x_read(parent, ri->enable_reg[0], &reg_val);
-       if (ret)
-               return ret;
-
-       return !!(reg_val & (1 << ri->enable_bit[0]));
-}
-
 static struct regulator_ops tps6586x_regulator_ops = {
        .list_voltage = regulator_list_voltage_table,
-       .get_voltage_sel = tps6586x_get_voltage_sel,
+       .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_sel = tps6586x_set_voltage_sel,
 
-       .is_enabled = tps6586x_regulator_is_enabled,
-       .enable = tps6586x_regulator_enable,
-       .disable = tps6586x_regulator_disable,
+       .is_enabled = regulator_is_enabled_regmap,
+       .enable = regulator_enable_regmap,
+       .disable = regulator_disable_regmap,
+};
+
+static struct regulator_ops tps6586x_sys_regulator_ops = {
 };
 
 static const unsigned int tps6586x_ldo0_voltages[] = {
@@ -202,10 +150,11 @@ static const unsigned int tps6586x_dvm_voltages[] = {
                .n_voltages = ARRAY_SIZE(tps6586x_##vdata##_voltages),  \
                .volt_table = tps6586x_##vdata##_voltages,              \
                .owner  = THIS_MODULE,                                  \
+               .enable_reg = TPS6586X_SUPPLY##ereg0,                   \
+               .enable_mask = 1 << (ebit0),                            \
+               .vsel_reg = TPS6586X_##vreg,                            \
+               .vsel_mask = ((1 << (nbits)) - 1) << (shift),           \
        },                                                              \
-       .volt_reg       = TPS6586X_##vreg,                              \
-       .volt_shift     = (shift),                                      \
-       .volt_nbits     = (nbits),                                      \
        .enable_reg[0]  = TPS6586X_SUPPLY##ereg0,                       \
        .enable_bit[0]  = (ebit0),                                      \
        .enable_reg[1]  = TPS6586X_SUPPLY##ereg1,                       \
@@ -230,15 +179,28 @@ static const unsigned int tps6586x_dvm_voltages[] = {
        TPS6586X_REGULATOR_DVM_GOREG(goreg, gobit)                      \
 }
 
+#define TPS6586X_SYS_REGULATOR()                                       \
+{                                                                      \
+       .desc   = {                                                     \
+               .supply_name = "sys",                                   \
+               .name   = "REG-SYS",                                    \
+               .ops    = &tps6586x_sys_regulator_ops,                  \
+               .type   = REGULATOR_VOLTAGE,                            \
+               .id     = TPS6586X_ID_SYS,                              \
+               .owner  = THIS_MODULE,                                  \
+       },                                                              \
+}
+
 static struct tps6586x_regulator tps6586x_regulator[] = {
+       TPS6586X_SYS_REGULATOR(),
        TPS6586X_LDO(LDO_0, "vinldo01", ldo0, SUPPLYV1, 5, 3, ENC, 0, END, 0),
        TPS6586X_LDO(LDO_3, "vinldo23", ldo, SUPPLYV4, 0, 3, ENC, 2, END, 2),
-       TPS6586X_LDO(LDO_5, NULL, ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
+       TPS6586X_LDO(LDO_5, "REG-SYS", ldo, SUPPLYV6, 0, 3, ENE, 6, ENE, 6),
        TPS6586X_LDO(LDO_6, "vinldo678", ldo, SUPPLYV3, 0, 3, ENC, 4, END, 4),
        TPS6586X_LDO(LDO_7, "vinldo678", ldo, SUPPLYV3, 3, 3, ENC, 5, END, 5),
        TPS6586X_LDO(LDO_8, "vinldo678", ldo, SUPPLYV2, 5, 3, ENC, 6, END, 6),
        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_RTC, "REG-SYS", 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, "vin-sm2", sm2, SUPPLYV2, 0, 5, ENC, 7, END, 7),
 
index 77a71a5..7eb986a 100644 (file)
@@ -10,6 +10,8 @@
  */
 
 #include <linux/module.h>
+#include <linux/string.h>
+#include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/platform_device.h>
@@ -624,18 +626,9 @@ static int twlfixed_list_voltage(struct regulator_dev *rdev, unsigned index)
        return info->min_mV * 1000;
 }
 
-static int twlfixed_get_voltage(struct regulator_dev *rdev)
-{
-       struct twlreg_info      *info = rdev_get_drvdata(rdev);
-
-       return info->min_mV * 1000;
-}
-
 static struct regulator_ops twl4030fixed_ops = {
        .list_voltage   = twlfixed_list_voltage,
 
-       .get_voltage    = twlfixed_get_voltage,
-
        .enable         = twl4030reg_enable,
        .disable        = twl4030reg_disable,
        .is_enabled     = twl4030reg_is_enabled,
@@ -648,8 +641,6 @@ static struct regulator_ops twl4030fixed_ops = {
 static struct regulator_ops twl6030fixed_ops = {
        .list_voltage   = twlfixed_list_voltage,
 
-       .get_voltage    = twlfixed_get_voltage,
-
        .enable         = twl6030reg_enable,
        .disable        = twl6030reg_disable,
        .is_enabled     = twl6030reg_is_enabled,
@@ -659,13 +650,6 @@ static struct regulator_ops twl6030fixed_ops = {
        .get_status     = twl6030reg_get_status,
 };
 
-static struct regulator_ops twl6030_fixed_resource = {
-       .enable         = twl6030reg_enable,
-       .disable        = twl6030reg_disable,
-       .is_enabled     = twl6030reg_is_enabled,
-       .get_status     = twl6030reg_get_status,
-};
-
 /*
  * SMPS status and control
  */
@@ -757,37 +741,32 @@ static int twl6030smps_list_voltage(struct regulator_dev *rdev, unsigned index)
        return voltage;
 }
 
-static int
-twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
-                       unsigned int *selector)
+static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV,
+                                  int max_uV)
 {
-       struct twlreg_info      *info = rdev_get_drvdata(rdev);
-       int     vsel = 0;
+       struct twlreg_info *info = rdev_get_drvdata(rdev);
+       int vsel = 0;
 
        switch (info->flags) {
        case 0:
                if (min_uV == 0)
                        vsel = 0;
                else if ((min_uV >= 600000) && (min_uV <= 1300000)) {
-                       int calc_uV;
                        vsel = DIV_ROUND_UP(min_uV - 600000, 12500);
                        vsel++;
-                       calc_uV = twl6030smps_list_voltage(rdev, vsel);
-                       if (calc_uV > max_uV)
-                               return -EINVAL;
                }
                /* Values 1..57 for vsel are linear and can be calculated
                 * values 58..62 are non linear.
                 */
-               else if ((min_uV > 1900000) && (max_uV >= 2100000))
+               else if ((min_uV > 1900000) && (min_uV <= 2100000))
                        vsel = 62;
-               else if ((min_uV > 1800000) && (max_uV >= 1900000))
+               else if ((min_uV > 1800000) && (min_uV <= 1900000))
                        vsel = 61;
-               else if ((min_uV > 1500000) && (max_uV >= 1800000))
+               else if ((min_uV > 1500000) && (min_uV <= 1800000))
                        vsel = 60;
-               else if ((min_uV > 1350000) && (max_uV >= 1500000))
+               else if ((min_uV > 1350000) && (min_uV <= 1500000))
                        vsel = 59;
-               else if ((min_uV > 1300000) && (max_uV >= 1350000))
+               else if ((min_uV > 1300000) && (min_uV <= 1350000))
                        vsel = 58;
                else
                        return -EINVAL;
@@ -796,25 +775,21 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
                if (min_uV == 0)
                        vsel = 0;
                else if ((min_uV >= 700000) && (min_uV <= 1420000)) {
-                       int calc_uV;
                        vsel = DIV_ROUND_UP(min_uV - 700000, 12500);
                        vsel++;
-                       calc_uV = twl6030smps_list_voltage(rdev, vsel);
-                       if (calc_uV > max_uV)
-                               return -EINVAL;
                }
                /* Values 1..57 for vsel are linear and can be calculated
                 * values 58..62 are non linear.
                 */
-               else if ((min_uV > 1900000) && (max_uV >= 2100000))
+               else if ((min_uV > 1900000) && (min_uV <= 2100000))
                        vsel = 62;
-               else if ((min_uV > 1800000) && (max_uV >= 1900000))
+               else if ((min_uV > 1800000) && (min_uV <= 1900000))
                        vsel = 61;
-               else if ((min_uV > 1350000) && (max_uV >= 1800000))
+               else if ((min_uV > 1350000) && (min_uV <= 1800000))
                        vsel = 60;
-               else if ((min_uV > 1350000) && (max_uV >= 1500000))
+               else if ((min_uV > 1350000) && (min_uV <= 1500000))
                        vsel = 59;
-               else if ((min_uV > 1300000) && (max_uV >= 1350000))
+               else if ((min_uV > 1300000) && (min_uV <= 1350000))
                        vsel = 58;
                else
                        return -EINVAL;
@@ -830,17 +805,23 @@ twl6030smps_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV,
        case SMPS_OFFSET_EN|SMPS_EXTENDED_EN:
                if (min_uV == 0) {
                        vsel = 0;
-               } else if ((min_uV >= 2161000) && (max_uV <= 4321000)) {
+               } else if ((min_uV >= 2161000) && (min_uV <= 4321000)) {
                        vsel = DIV_ROUND_UP(min_uV - 2161000, 38600);
                        vsel++;
                }
                break;
        }
 
-       *selector = vsel;
+       return vsel;
+}
+
+static int twl6030smps_set_voltage_sel(struct regulator_dev *rdev,
+                                      unsigned int selector)
+{
+       struct twlreg_info *info = rdev_get_drvdata(rdev);
 
        return twlreg_write(info, TWL_MODULE_PM_RECEIVER, VREG_VOLTAGE_SMPS,
-                                                       vsel);
+                           selector);
 }
 
 static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
@@ -852,8 +833,9 @@ static int twl6030smps_get_voltage_sel(struct regulator_dev *rdev)
 
 static struct regulator_ops twlsmps_ops = {
        .list_voltage           = twl6030smps_list_voltage,
+       .map_voltage            = twl6030smps_map_voltage,
 
-       .set_voltage            = twl6030smps_set_voltage,
+       .set_voltage_sel        = twl6030smps_set_voltage_sel,
        .get_voltage_sel        = twl6030smps_get_voltage_sel,
 
        .enable                 = twl6030reg_enable,
@@ -876,7 +858,7 @@ static struct regulator_ops twlsmps_ops = {
                        0x0, TWL6030, twl6030fixed_ops)
 
 #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
        .base = offset, \
        .id = num, \
        .table_len = ARRAY_SIZE(label##_VSEL_table), \
@@ -894,7 +876,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
        }
 
 #define TWL4030_ADJUSTABLE_SMPS(label, offset, num, turnon_delay, remap_conf) \
-static struct twlreg_info TWL4030_INFO_##label = { \
+static const struct twlreg_info TWL4030_INFO_##label = { \
        .base = offset, \
        .id = num, \
        .remap = remap_conf, \
@@ -909,7 +891,7 @@ static struct twlreg_info TWL4030_INFO_##label = { \
        }
 
 #define TWL6030_ADJUSTABLE_SMPS(label) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
        .desc = { \
                .name = #label, \
                .id = TWL6030_REG_##label, \
@@ -920,7 +902,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
        }
 
 #define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6030_INFO_##label = { \
+static const struct twlreg_info TWL6030_INFO_##label = { \
        .base = offset, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
@@ -935,7 +917,7 @@ static struct twlreg_info TWL6030_INFO_##label = { \
        }
 
 #define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) \
-static struct twlreg_info TWL6025_INFO_##label = { \
+static const struct twlreg_info TWL6025_INFO_##label = { \
        .base = offset, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
@@ -951,7 +933,7 @@ static struct twlreg_info TWL6025_INFO_##label = { \
 
 #define TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, remap_conf, \
                family, operations) \
-static struct twlreg_info TWLFIXED_INFO_##label = { \
+static const struct twlreg_info TWLFIXED_INFO_##label = { \
        .base = offset, \
        .id = num, \
        .min_mV = mVolts, \
@@ -981,7 +963,7 @@ static struct twlreg_info TWLRES_INFO_##label = { \
        }
 
 #define TWL6025_ADJUSTABLE_SMPS(label, offset) \
-static struct twlreg_info TWLSMPS_INFO_##label = { \
+static const struct twlreg_info TWLSMPS_INFO_##label = { \
        .base = offset, \
        .min_mV = 600, \
        .max_mV = 2100, \
@@ -1138,6 +1120,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
 {
        int                             i, id;
        struct twlreg_info              *info;
+       const struct twlreg_info        *template;
        struct regulator_init_data      *initdata;
        struct regulation_constraints   *c;
        struct regulator_dev            *rdev;
@@ -1147,17 +1130,17 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
 
        match = of_match_device(twl_of_match, &pdev->dev);
        if (match) {
-               info = match->data;
-               id = info->desc.id;
+               template = match->data;
+               id = template->desc.id;
                initdata = of_get_regulator_init_data(&pdev->dev,
                                                      pdev->dev.of_node);
                drvdata = NULL;
        } else {
                id = pdev->id;
                initdata = pdev->dev.platform_data;
-               for (i = 0, info = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
-                       info = twl_of_match[i].data;
-                       if (info && info->desc.id == id)
+               for (i = 0, template = NULL; i < ARRAY_SIZE(twl_of_match); i++) {
+                       template = twl_of_match[i].data;
+                       if (template && template->desc.id == id)
                                break;
                }
                if (i == ARRAY_SIZE(twl_of_match))
@@ -1168,12 +1151,16 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
                        return -EINVAL;
        }
 
-       if (!info)
+       if (!template)
                return -ENODEV;
 
        if (!initdata)
                return -EINVAL;
 
+       info = kmemdup(template, sizeof (*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
        if (drvdata) {
                /* copy the driver data into regulator data */
                info->features = drvdata->features;
@@ -1234,6 +1221,7 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
        if (IS_ERR(rdev)) {
                dev_err(&pdev->dev, "can't register %s, %ld\n",
                                info->desc.name, PTR_ERR(rdev));
+               kfree(info);
                return PTR_ERR(rdev);
        }
        platform_set_drvdata(pdev, rdev);
@@ -1255,7 +1243,11 @@ static int __devinit twlreg_probe(struct platform_device *pdev)
 
 static int __devexit twlreg_remove(struct platform_device *pdev)
 {
-       regulator_unregister(platform_get_drvdata(pdev));
+       struct regulator_dev *rdev = platform_get_drvdata(pdev);
+       struct twlreg_info *info = rdev->reg_data;
+
+       regulator_unregister(rdev);
+       kfree(info);
        return 0;
 }
 
index 7413885..90cbcc6 100644 (file)
@@ -339,16 +339,15 @@ static int wm831x_buckv_set_current_limit(struct regulator_dev *rdev,
        u16 reg = dcdc->base + WM831X_DCDC_CONTROL_2;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(wm831x_dcdc_ilim); i++) {
+       for (i = ARRAY_SIZE(wm831x_dcdc_ilim) - 1; i >= 0; i--) {
                if ((min_uA <= wm831x_dcdc_ilim[i]) &&
                    (wm831x_dcdc_ilim[i] <= max_uA))
-                       break;
+                       return wm831x_set_bits(wm831x, reg,
+                                              WM831X_DC1_HC_THR_MASK,
+                                               i << WM831X_DC1_HC_THR_SHIFT);
        }
-       if (i == ARRAY_SIZE(wm831x_dcdc_ilim))
-               return -EINVAL;
 
-       return wm831x_set_bits(wm831x, reg, WM831X_DC1_HC_THR_MASK,
-                              i << WM831X_DC1_HC_THR_SHIFT);
+       return -EINVAL;
 }
 
 static int wm831x_buckv_get_current_limit(struct regulator_dev *rdev)
index 5cb70ca..9af5126 100644 (file)
@@ -205,6 +205,8 @@ static int wm831x_gp_ldo_get_status(struct regulator_dev *rdev)
 
        /* Is it reporting under voltage? */
        ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+       if (ret < 0)
+               return ret;
        if (ret & mask)
                return REGULATOR_STATUS_ERROR;
 
@@ -237,6 +239,8 @@ static struct regulator_ops wm831x_gp_ldo_ops = {
        .set_mode = wm831x_gp_ldo_set_mode,
        .get_status = wm831x_gp_ldo_get_status,
        .get_optimum_mode = wm831x_gp_ldo_get_optimum_mode,
+       .get_bypass = regulator_get_bypass_regmap,
+       .set_bypass = regulator_set_bypass_regmap,
 
        .is_enabled = regulator_is_enabled_regmap,
        .enable = regulator_enable_regmap,
@@ -293,6 +297,8 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev)
        ldo->desc.vsel_mask = WM831X_LDO1_ON_VSEL_MASK;
        ldo->desc.enable_reg = WM831X_LDO_ENABLE;
        ldo->desc.enable_mask = 1 << id;
+       ldo->desc.bypass_reg = ldo->base;
+       ldo->desc.bypass_mask = WM831X_LDO1_SWI;
 
        config.dev = pdev->dev.parent;
        if (pdata)
@@ -469,6 +475,8 @@ static int wm831x_aldo_get_status(struct regulator_dev *rdev)
 
        /* Is it reporting under voltage? */
        ret = wm831x_reg_read(wm831x, WM831X_LDO_UV_STATUS);
+       if (ret < 0)
+               return ret;
        if (ret & mask)
                return REGULATOR_STATUS_ERROR;
 
@@ -488,6 +496,8 @@ static struct regulator_ops wm831x_aldo_ops = {
        .get_mode = wm831x_aldo_get_mode,
        .set_mode = wm831x_aldo_set_mode,
        .get_status = wm831x_aldo_get_status,
+       .set_bypass = regulator_set_bypass_regmap,
+       .get_bypass = regulator_get_bypass_regmap,
 
        .is_enabled = regulator_is_enabled_regmap,
        .enable = regulator_enable_regmap,
@@ -544,6 +554,8 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev)
        ldo->desc.vsel_mask = WM831X_LDO7_ON_VSEL_MASK;
        ldo->desc.enable_reg = WM831X_LDO_ENABLE;
        ldo->desc.enable_mask = 1 << id;
+       ldo->desc.bypass_reg = ldo->base;
+       ldo->desc.bypass_mask = WM831X_LDO7_SWI;
 
        config.dev = pdev->dev.parent;
        if (pdata)
index 9035dd0..27c746e 100644 (file)
@@ -120,13 +120,8 @@ static int wm8400_dcdc_set_mode(struct regulator_dev *dev, unsigned int mode)
 
        case REGULATOR_MODE_IDLE:
                /* Datasheet: standby */
-               ret = wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
-                                     WM8400_DC1_ACTIVE, 0);
-               if (ret != 0)
-                       return ret;
                return wm8400_set_bits(wm8400, WM8400_DCDC1_CONTROL_1 + offset,
-                                      WM8400_DC1_SLEEP, 0);
-
+                                      WM8400_DC1_ACTIVE | WM8400_DC1_SLEEP, 0);
        default:
                return -EINVAL;
        }
index 25417d0..0bcacf7 100644 (file)
@@ -2888,7 +2888,7 @@ ahd_handle_lqiphase_error(struct ahd_softc *ahd, u_int lqistat1)
                ahd_outb(ahd, CLRINT, CLRSCSIINT);
                ahd_unpause(ahd);
        } else {
-               printk("Reseting Channel for LQI Phase error\n");
+               printk("Resetting Channel for LQI Phase error\n");
                ahd_dump_card_state(ahd);
                ahd_reset_channel(ahd, 'A', /*Initiate Reset*/TRUE);
        }
index 8cdb79c..21ad290 100644 (file)
@@ -5587,7 +5587,7 @@ static bfa_status_t bfa_dconf_flash_write(struct bfa_dconf_mod_s *dconf);
 static void bfa_dconf_init_cb(void *arg, bfa_status_t status);
 
 /*
- * Begining state of dconf module. Waiting for an event to start.
+ * Beginning state of dconf module. Waiting for an event to start.
  */
 static void
 bfa_dconf_sm_uninit(struct bfa_dconf_mod_s *dconf, enum bfa_dconf_event event)
index 1a99d4b..7b916e0 100644 (file)
@@ -530,7 +530,7 @@ struct bfa_diag_results_fwping {
 
 struct bfa_diag_qtest_result_s {
        u32     status;
-       u16     count;  /* sucessful queue test count */
+       u16     count;  /* successful queue test count */
        u8      queue;
        u8      rsvd;   /* 64-bit align */
 };
index ae1cb76..e055865 100644 (file)
@@ -908,7 +908,7 @@ static void bnx2fc_indicate_netevent(void *context, unsigned long event,
                return;
 
        default:
-               printk(KERN_ERR PFX "Unkonwn netevent %ld", event);
+               printk(KERN_ERR PFX "Unknown netevent %ld", event);
                return;
        }
 
@@ -1738,7 +1738,7 @@ static int bnx2fc_ulp_get_stats(void *handle)
 /**
  * bnx2fc_ulp_start - cnic callback to initialize & start adapter instance
  *
- * @handle:    transport handle pointing to adapter struture
+ * @handle:    transport handle pointing to adapter structure
  *
  * This function maps adapter structure to pcidev structure and initiates
  *     firmware handshake to enable/initialize on-chip FCoE components.
index d3e4d7c..fbf6f0f 100644 (file)
 /* GDT_ISA */
 #define GDT2_ID         0x0120941c              /* GDT2000/2020 */
 
-/* vendor ID, device IDs (PCI) */
-/* these defines should already exist in <linux/pci.h> */
-#ifndef PCI_VENDOR_ID_VORTEX
-#define PCI_VENDOR_ID_VORTEX            0x1119  /* PCI controller vendor ID */
-#endif
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL             0x8086  
-#endif
-
 #ifndef PCI_DEVICE_ID_VORTEX_GDT60x0
 /* GDT_PCI */
 #define PCI_DEVICE_ID_VORTEX_GDT60x0    0       /* GDT6000/6020/6050 */
index 467dc38..1059c99 100644 (file)
@@ -192,7 +192,7 @@ static const struct ipr_chip_t ipr_chip[] = {
        { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CROCODILE, IPR_USE_MSI, IPR_SIS64, IPR_MMIO, &ipr_chip_cfg[2] }
 };
 
-static int ipr_max_bus_speeds [] = {
+static int ipr_max_bus_speeds[] = {
        IPR_80MBs_SCSI_RATE, IPR_U160_SCSI_RATE, IPR_U320_SCSI_RATE
 };
 
@@ -562,7 +562,7 @@ static void ipr_trc_hook(struct ipr_cmnd *ipr_cmd,
        trace_entry->u.add_data = add_data;
 }
 #else
-#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while(0)
+#define ipr_trc_hook(ipr_cmd, type, add_data) do { } while (0)
 #endif
 
 /**
@@ -1002,7 +1002,7 @@ static void ipr_send_hcam(struct ipr_ioa_cfg *ioa_cfg, u8 type,
  **/
 static void ipr_update_ata_class(struct ipr_resource_entry *res, unsigned int proto)
 {
-       switch(proto) {
+       switch (proto) {
        case IPR_PROTO_SATA:
        case IPR_PROTO_SAS_STP:
                res->ata_class = ATA_DEV_ATA;
@@ -3043,7 +3043,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
 }
 
 #else
-#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while(0)
+#define ipr_get_ioa_dump(ioa_cfg, dump) do { } while (0)
 #endif
 
 /**
@@ -3055,7 +3055,7 @@ static void ipr_get_ioa_dump(struct ipr_ioa_cfg *ioa_cfg, struct ipr_dump *dump)
  **/
 static void ipr_release_dump(struct kref *kref)
 {
-       struct ipr_dump *dump = container_of(kref,struct ipr_dump,kref);
+       struct ipr_dump *dump = container_of(kref, struct ipr_dump, kref);
        struct ipr_ioa_cfg *ioa_cfg = dump->ioa_cfg;
        unsigned long lock_flags = 0;
        int i;
@@ -3142,7 +3142,7 @@ restart:
                                break;
                        }
                }
-       } while(did_work);
+       } while (did_work);
 
        list_for_each_entry(res, &ioa_cfg->used_res_q, queue) {
                if (res->add_to_ml) {
@@ -3268,7 +3268,7 @@ static ssize_t ipr_show_log_level(struct device *dev,
  *     number of bytes printed to buffer
  **/
 static ssize_t ipr_store_log_level(struct device *dev,
-                                  struct device_attribute *attr,
+                                  struct device_attribute *attr,
                                   const char *buf, size_t count)
 {
        struct Scsi_Host *shost = class_to_shost(dev);
@@ -3315,7 +3315,7 @@ static ssize_t ipr_store_diagnostics(struct device *dev,
                return -EACCES;
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3682,7 +3682,7 @@ static int ipr_update_ioa_ucode(struct ipr_ioa_cfg *ioa_cfg,
        unsigned long lock_flags;
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -3746,7 +3746,7 @@ static ssize_t ipr_store_update_fw(struct device *dev,
        len = snprintf(fname, 99, "%s", buf);
        fname[len-1] = '\0';
 
-       if(request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
+       if (request_firmware(&fw_entry, fname, &ioa_cfg->pdev->dev)) {
                dev_err(&ioa_cfg->pdev->dev, "Firmware file %s not found\n", fname);
                return -EIO;
        }
@@ -4612,7 +4612,7 @@ static int ipr_slave_alloc(struct scsi_device *sdev)
  * Return value:
  *     SUCCESS / FAILED
  **/
-static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_host_reset(struct scsi_cmnd *scsi_cmd)
 {
        struct ipr_ioa_cfg *ioa_cfg;
        int rc;
@@ -4634,7 +4634,7 @@ static int __ipr_eh_host_reset(struct scsi_cmnd * scsi_cmd)
        return rc;
 }
 
-static int ipr_eh_host_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_host_reset(struct scsi_cmnd *cmd)
 {
        int rc;
 
@@ -4701,7 +4701,7 @@ static int ipr_device_reset(struct ipr_ioa_cfg *ioa_cfg,
        }
 
        LEAVE;
-       return (IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0);
+       return IPR_IOASC_SENSE_KEY(ioasc) ? -EIO : 0;
 }
 
 /**
@@ -4725,7 +4725,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
 
        ENTER;
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
@@ -4753,7 +4753,7 @@ static int ipr_sata_reset(struct ata_link *link, unsigned int *classes,
  * Return value:
  *     SUCCESS / FAILED
  **/
-static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
+static int __ipr_eh_dev_reset(struct scsi_cmnd *scsi_cmd)
 {
        struct ipr_cmnd *ipr_cmd;
        struct ipr_ioa_cfg *ioa_cfg;
@@ -4811,10 +4811,10 @@ static int __ipr_eh_dev_reset(struct scsi_cmnd * scsi_cmd)
        res->resetting_device = 0;
 
        LEAVE;
-       return (rc ? FAILED : SUCCESS);
+       return rc ? FAILED : SUCCESS;
 }
 
-static int ipr_eh_dev_reset(struct scsi_cmnd * cmd)
+static int ipr_eh_dev_reset(struct scsi_cmnd *cmd)
 {
        int rc;
 
@@ -4910,7 +4910,7 @@ static void ipr_abort_timeout(struct ipr_cmnd *ipr_cmd)
  * Return value:
  *     SUCCESS / FAILED
  **/
-static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
+static int ipr_cancel_op(struct scsi_cmnd *scsi_cmd)
 {
        struct ipr_cmnd *ipr_cmd;
        struct ipr_ioa_cfg *ioa_cfg;
@@ -4979,7 +4979,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
                res->needs_sync_complete = 1;
 
        LEAVE;
-       return (IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS);
+       return IPR_IOASC_SENSE_KEY(ioasc) ? FAILED : SUCCESS;
 }
 
 /**
@@ -4989,7 +4989,7 @@ static int ipr_cancel_op(struct scsi_cmnd * scsi_cmd)
  * Return value:
  *     SUCCESS / FAILED
  **/
-static int ipr_eh_abort(struct scsi_cmnd * scsi_cmd)
+static int ipr_eh_abort(struct scsi_cmnd *scsi_cmd)
 {
        unsigned long flags;
        int rc;
@@ -5907,7 +5907,7 @@ static int ipr_ioctl(struct scsi_device *sdev, int cmd, void __user *arg)
  * Return value:
  *     pointer to buffer with description string
  **/
-static const char * ipr_ioa_info(struct Scsi_Host *host)
+static const char *ipr_ioa_info(struct Scsi_Host *host)
 {
        static char buffer[512];
        struct ipr_ioa_cfg *ioa_cfg;
@@ -5965,7 +5965,7 @@ static void ipr_ata_phy_reset(struct ata_port *ap)
 
        ENTER;
        spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6005,7 +6005,7 @@ static void ipr_ata_post_internal(struct ata_queued_cmd *qc)
        unsigned long flags;
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, flags);
@@ -6330,7 +6330,7 @@ static int ipr_invalid_adapter(struct ipr_ioa_cfg *ioa_cfg)
        int i;
 
        if ((ioa_cfg->type == 0x5702) && (ioa_cfg->pdev->revision < 4)) {
-               for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++){
+               for (i = 0; i < ARRAY_SIZE(ipr_blocked_processors); i++) {
                        if (__is_processor(ipr_blocked_processors[i]))
                                return 1;
                }
@@ -6608,7 +6608,7 @@ static void ipr_scsi_bus_speed_limit(struct ipr_ioa_cfg *ioa_cfg)
  *     none
  **/
 static void ipr_modify_ioafp_mode_page_28(struct ipr_ioa_cfg *ioa_cfg,
-                                               struct ipr_mode_pages *mode_pages)
+                                         struct ipr_mode_pages *mode_pages)
 {
        int i, entry_length;
        struct ipr_dev_bus_entry *bus;
@@ -8022,7 +8022,7 @@ static void ipr_reset_ioa_job(struct ipr_cmnd *ipr_cmd)
                ipr_reinit_ipr_cmnd(ipr_cmd);
                ipr_cmd->job_step_failed = ipr_reset_cmd_failed;
                rc = ipr_cmd->job_step(ipr_cmd);
-       } while(rc == IPR_RC_JOB_CONTINUE);
+       } while (rc == IPR_RC_JOB_CONTINUE);
 }
 
 /**
@@ -8283,7 +8283,7 @@ static void ipr_free_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
        }
 
        if (ioa_cfg->ipr_cmd_pool)
-               pci_pool_destroy (ioa_cfg->ipr_cmd_pool);
+               pci_pool_destroy(ioa_cfg->ipr_cmd_pool);
 
        kfree(ioa_cfg->ipr_cmnd_list);
        kfree(ioa_cfg->ipr_cmnd_list_dma);
@@ -8363,8 +8363,8 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
        dma_addr_t dma_addr;
        int i;
 
-       ioa_cfg->ipr_cmd_pool = pci_pool_create (IPR_NAME, ioa_cfg->pdev,
-                                                sizeof(struct ipr_cmnd), 512, 0);
+       ioa_cfg->ipr_cmd_pool = pci_pool_create(IPR_NAME, ioa_cfg->pdev,
+                                               sizeof(struct ipr_cmnd), 512, 0);
 
        if (!ioa_cfg->ipr_cmd_pool)
                return -ENOMEM;
@@ -8378,7 +8378,7 @@ static int __devinit ipr_alloc_cmd_blks(struct ipr_ioa_cfg *ioa_cfg)
        }
 
        for (i = 0; i < IPR_NUM_CMD_BLKS; i++) {
-               ipr_cmd = pci_pool_alloc (ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
+               ipr_cmd = pci_pool_alloc(ioa_cfg->ipr_cmd_pool, GFP_KERNEL, &dma_addr);
 
                if (!ipr_cmd) {
                        ipr_free_cmd_blks(ioa_cfg);
@@ -8964,7 +8964,7 @@ static void ipr_scan_vsets(struct ipr_ioa_cfg *ioa_cfg)
        int target, lun;
 
        for (target = 0; target < IPR_MAX_NUM_TARGETS_PER_BUS; target++)
-               for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++ )
+               for (lun = 0; lun < IPR_MAX_NUM_VSET_LUNS_PER_TARGET; lun++)
                        scsi_add_device(ioa_cfg->host, IPR_VSET_BUS, target, lun);
 }
 
@@ -9010,7 +9010,7 @@ static void __ipr_remove(struct pci_dev *pdev)
        ENTER;
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, host_lock_flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, host_lock_flags);
@@ -9139,7 +9139,7 @@ static void ipr_shutdown(struct pci_dev *pdev)
        unsigned long lock_flags = 0;
 
        spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
-       while(ioa_cfg->in_reset_reload) {
+       while (ioa_cfg->in_reset_reload) {
                spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags);
                wait_event(ioa_cfg->reset_wait_q, !ioa_cfg->in_reset_reload);
                spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags);
index 45385f5..b334fdc 100644 (file)
@@ -492,7 +492,7 @@ static void sci_controller_process_completions(struct isci_host *ihost)
        u32 event_cycle;
 
        dev_dbg(&ihost->pdev->dev,
-               "%s: completion queue begining get:0x%08x\n",
+               "%s: completion queue beginning get:0x%08x\n",
                __func__,
                ihost->completion_queue_get);
 
index 92c1d86..9be45a2 100644 (file)
@@ -222,7 +222,7 @@ static struct sas_domain_function_template isci_transport_ops  = {
  * @isci_host: This parameter specifies the lldd specific wrapper for the
  *    libsas sas_ha struct.
  *
- * This method returns an error code indicating sucess or failure. The user
+ * This method returns an error code indicating success or failure. The user
  * should check for possible memory allocation error return otherwise, a zero
  * indicates success.
  */
index 2fb85bf..13098b0 100644 (file)
@@ -212,7 +212,7 @@ static void isci_port_link_up(struct isci_host *isci_host,
                memcpy(iphy->sas_phy.attached_sas_addr,
                       iphy->frame_rcvd.iaf.sas_addr, SAS_ADDR_SIZE);
        } else {
-               dev_err(&isci_host->pdev->dev, "%s: unkown target\n", __func__);
+               dev_err(&isci_host->pdev->dev, "%s: unknown target\n", __func__);
                success = false;
        }
 
index 7a0431c..c1bafc3 100644 (file)
@@ -2240,7 +2240,7 @@ static enum sci_status atapi_data_tc_completion_handler(struct isci_request *ire
                        status = ireq->sci_status;
                        sci_change_state(&idev->sm, SCI_STP_DEV_ATAPI_ERROR);
                } else {
-                       /* If receiving any non-sucess TC status, no UF
+                       /* If receiving any non-success TC status, no UF
                         * received yet, then an UF for the status fis
                         * is coming after (XXX: suspect this is
                         * actually a protocol error or a bug like the
index 6bc74eb..b6f19a1 100644 (file)
@@ -532,7 +532,7 @@ int isci_task_abort_task(struct sas_task *task)
                /* The request has already completed and there
                * is nothing to do here other than to set the task
                * done bit, and indicate that the task abort function
-               * was sucessful.
+               * was successful.
                */
                spin_lock_irqsave(&task->task_state_lock, flags);
                task->task_state_flags |= SAS_TASK_STATE_DONE;
index 45c1520..29937b6 100644 (file)
@@ -6607,7 +6607,7 @@ out_error:
  * we just use some constant number as place holder.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      -ENOMEM - No availble memory
  *      -EIO - The mailbox failed to complete successfully.
  **/
index 9cbd20b..0e7e144 100644 (file)
@@ -4739,7 +4739,7 @@ lpfc_sli4_read_rev(struct lpfc_hba *phba, LPFC_MBOXQ_t *mboxq,
  * is attached to.
  *
  * Return codes
- *      0 - sucessful
+ *      0 - successful
  *      otherwise - failed to retrieve physical port name
  **/
 static int
@@ -15209,7 +15209,7 @@ lpfc_check_next_fcf_pri_level(struct lpfc_hba *phba)
        /*
         * if next_fcf_pri was not set above and the list is not empty then
         * we have failed flogis on all of them. So reset flogi failed
-        * and start at the begining.
+        * and start at the beginning.
         */
        if (!next_fcf_pri && !list_empty(&phba->fcf.fcf_pri_list)) {
                list_for_each_entry(fcf_pri, &phba->fcf.fcf_pri_list, list) {
index 97825f1..76ad72d 100644 (file)
@@ -305,12 +305,11 @@ mega_query_adapter(adapter_t *adapter)
 
        adapter->host->sg_tablesize = adapter->sglen;
 
-
        /* use HP firmware and bios version encoding
           Note: fw_version[0|1] and bios_version[0|1] were originally shifted
           right 8 bits making them zero. This 0 value was hardcoded to fix
           sparse warnings. */
-       if (adapter->product_info.subsysvid == HP_SUBSYS_VID) {
+       if (adapter->product_info.subsysvid == PCI_VENDOR_ID_HP) {
                sprintf (adapter->fw_version, "%c%d%d.%d%d",
                         adapter->product_info.fw_version[2],
                         0,
@@ -4716,7 +4715,7 @@ megaraid_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
         * support, since this firmware cannot handle 64 bit
         * addressing
         */
-       if ((subsysvid == HP_SUBSYS_VID) &&
+       if ((subsysvid == PCI_VENDOR_ID_HP) &&
            ((subsysid == 0x60E7) || (subsysid == 0x60E8))) {
                /*
                 * which firmware
index 9a7897f..4fb2adf 100644 (file)
 
 #define MAX_DEV_TYPE   32
 
-#ifndef PCI_VENDOR_ID_LSI_LOGIC
-#define PCI_VENDOR_ID_LSI_LOGIC                0x1000
-#endif
-
-#ifndef PCI_VENDOR_ID_AMI
-#define PCI_VENDOR_ID_AMI              0x101E
-#endif
-
-#ifndef PCI_VENDOR_ID_DELL
-#define PCI_VENDOR_ID_DELL             0x1028
-#endif
-
-#ifndef PCI_VENDOR_ID_INTEL
-#define PCI_VENDOR_ID_INTEL            0x8086
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID
-#define PCI_DEVICE_ID_AMI_MEGARAID     0x9010
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID2
-#define PCI_DEVICE_ID_AMI_MEGARAID2    0x9060
-#endif
-
-#ifndef PCI_DEVICE_ID_AMI_MEGARAID3
-#define PCI_DEVICE_ID_AMI_MEGARAID3    0x1960
-#endif
-
 #define PCI_DEVICE_ID_DISCOVERY                0x000E
 #define PCI_DEVICE_ID_PERC4_DI         0x000F
 #define PCI_DEVICE_ID_PERC4_QC_VERDE   0x0407
 
-/* Sub-System Vendor IDs */
-#define        AMI_SUBSYS_VID                  0x101E
-#define DELL_SUBSYS_VID                        0x1028
-#define        HP_SUBSYS_VID                   0x103C
-#define LSI_SUBSYS_VID                 0x1000
-#define INTEL_SUBSYS_VID               0x8086
-
 #define HBA_SIGNATURE                  0x3344
 #define HBA_SIGNATURE_471              0xCCCC
 #define HBA_SIGNATURE_64BIT            0x0299
index 88cf1db..783edc7 100644 (file)
@@ -122,7 +122,7 @@ static struct mvumi_res *mvumi_alloc_mem_resource(struct mvumi_hba *mhba,
 
        if (!res) {
                dev_err(&mhba->pdev->dev,
-                       "Failed to allocate memory for resouce manager.\n");
+                       "Failed to allocate memory for resource manager.\n");
                return NULL;
        }
 
@@ -1007,13 +1007,13 @@ static int mvumi_handshake(struct mvumi_hba *mhba)
                tmp |= INT_MAP_COMAOUT | INT_MAP_COMAERR;
                iowrite32(tmp, regs + CPU_ENPOINTA_MASK_REG);
                iowrite32(mhba->list_num_io, mhba->ib_shadow);
-               /* Set InBound List Avaliable count shadow */
+               /* Set InBound List Available count shadow */
                iowrite32(lower_32_bits(mhba->ib_shadow_phys),
                                        regs + CLA_INB_AVAL_COUNT_BASEL);
                iowrite32(upper_32_bits(mhba->ib_shadow_phys),
                                        regs + CLA_INB_AVAL_COUNT_BASEH);
 
-               /* Set OutBound List Avaliable count shadow */
+               /* Set OutBound List Available count shadow */
                iowrite32((mhba->list_num_io-1) | CL_POINTER_TOGGLE,
                                                mhba->ob_shadow);
                iowrite32(lower_32_bits(mhba->ob_shadow_phys), regs + 0x5B0);
index 9da4266..487e3c8 100644 (file)
@@ -803,7 +803,7 @@ static void qla4xxx_conn_get_stats(struct iscsi_cls_conn *cls_conn,
                                     iscsi_stats_dma);
        if (ret != QLA_SUCCESS) {
                ql4_printk(KERN_ERR, ha,
-                          "Unable to retreive iscsi stats\n");
+                          "Unable to retrieve iscsi stats\n");
                goto free_stats;
        }
 
@@ -4338,7 +4338,7 @@ static int qla4xxx_compare_tuple_ddb(struct scsi_qla_host *ha,
                return QLA_ERROR;
 
        /* For multi sessions, driver generates the ISID, so do not compare
-        * ISID in reset path since it would be a comparision between the
+        * ISID in reset path since it would be a comparison between the
         * driver generated ISID and firmware generated ISID. This could
         * lead to adding duplicated DDBs in the list as driver generated
         * ISID would not match firmware generated ISID.
@@ -5326,7 +5326,7 @@ static void qla4xxx_destroy_fw_ddb_session(struct scsi_qla_host *ha)
        }
 }
 /**
- * qla4xxx_remove_adapter - calback function to remove adapter.
+ * qla4xxx_remove_adapter - callback function to remove adapter.
  * @pci_dev: PCI device pointer
  **/
 static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
index 4411d42..20b3a48 100644 (file)
@@ -295,7 +295,7 @@ static void ll_adapter_reset(const struct pvscsi_adapter *adapter)
 
 static void ll_bus_reset(const struct pvscsi_adapter *adapter)
 {
-       dev_dbg(pvscsi_dev(adapter), "Reseting bus on %p\n", adapter);
+       dev_dbg(pvscsi_dev(adapter), "Resetting bus on %p\n", adapter);
 
        pvscsi_write_cmd_desc(adapter, PVSCSI_CMD_RESET_BUS, NULL, 0);
 }
@@ -304,7 +304,7 @@ static void ll_device_reset(const struct pvscsi_adapter *adapter, u32 target)
 {
        struct PVSCSICmdDescResetDevice cmd = { 0 };
 
-       dev_dbg(pvscsi_dev(adapter), "Reseting device: target=%u\n", target);
+       dev_dbg(pvscsi_dev(adapter), "Resetting device: target=%u\n", target);
 
        cmd.target = target;
 
index a3ac39b..0646bf6 100644 (file)
@@ -208,6 +208,8 @@ static int sh_pfc_gpio_request_enable(struct pinctrl_dev *pctldev,
 
                break;
        case PINMUX_TYPE_GPIO:
+       case PINMUX_TYPE_INPUT:
+       case PINMUX_TYPE_OUTPUT:
                break;
        default:
                pr_err("Unsupported mux type (%d), bailing...\n", pinmux_type);
index 5784c87..4de66d1 100644 (file)
@@ -475,7 +475,7 @@ static irqreturn_t au1550_spi_dma_irq_callback(struct au1550_spi *hw)
                /*
                 * due to an spi error we consider transfer as done,
                 * so mask all events until before next transfer start
-                * and stop the possibly running dma immediatelly
+                * and stop the possibly running dma immediately
                 */
                au1550_spi_mask_ack_all(hw);
                au1xxx_dbdma_stop(hw->dma_rx_ch);
index 1fe5119..6555ecd 100644 (file)
@@ -467,7 +467,7 @@ bfin_sport_spi_pump_transfers(unsigned long data)
                dev_dbg(drv_data->dev, "IO write error!\n");
                drv_data->state = ERROR_STATE;
        } else {
-               /* Update total byte transfered */
+               /* Update total byte transferred */
                message->actual_length += transfer->len;
                /* Move to next transfer of this msg */
                drv_data->state = bfin_sport_spi_next_transfer(drv_data);
index 698018f..9d9071b 100644 (file)
@@ -129,7 +129,7 @@ static int tiny_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t)
        unsigned int i;
 
        if (hw->irq >= 0) {
-               /* use intrrupt driven data transfer */
+               /* use interrupt driven data transfer */
                hw->len = t->len;
                hw->txp = t->tx_buf;
                hw->rxp = t->rx_buf;
index 75ac9d4..7a85f22 100644 (file)
@@ -101,7 +101,7 @@ struct spi_ppc4xx_regs {
        u8 dummy;
        /*
         * Clock divisor modulus register
-        * This uses the follwing formula:
+        * This uses the following formula:
         *    SCPClkOut = OPBCLK/(4(CDM + 1))
         * or
         *    CDM = (OPBCLK/4*SCPClkOut) - 1
@@ -201,7 +201,7 @@ static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
                return -EINVAL;
        }
 
-       /* Write new configration */
+       /* Write new configuration */
        out_8(&hw->regs->mode, cs->mode);
 
        /* Set the clock */
index cd56dcf..1284c9b 100644 (file)
@@ -505,7 +505,7 @@ static int pch_spi_transfer(struct spi_device *pspi, struct spi_message *pmsg)
        }
 
        if (unlikely(pspi->max_speed_hz == 0)) {
-               dev_err(&pspi->dev, "%s pch_spi_tranfer maxspeed=%d\n",
+               dev_err(&pspi->dev, "%s pch_spi_transfer maxspeed=%d\n",
                        __func__, pspi->max_speed_hz);
                retval = -EINVAL;
                goto err_out;
index 0c4760f..240f7aa 100644 (file)
@@ -662,7 +662,7 @@ int iscsi_extract_key_value(char *textbuf, char **key, char **value)
 {
        *value = strchr(textbuf, '=');
        if (!*value) {
-               pr_err("Unable to locate \"=\" seperator for key,"
+               pr_err("Unable to locate \"=\" separator for key,"
                                " ignoring request.\n");
                return -1;
        }
@@ -1269,7 +1269,7 @@ static int iscsi_check_value(struct iscsi_param *param, char *value)
                comma_ptr = strchr(value, ',');
 
                if (comma_ptr && !IS_TYPE_VALUE_LIST(param)) {
-                       pr_err("Detected value seperator \",\", but"
+                       pr_err("Detected value separator \",\", but"
                                " key \"%s\" does not allow a value list,"
                                " protocol error.\n", param->name);
                        return -1;
index d956965..3440812 100644 (file)
@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
        /* print devices for all busses */
        list_for_each_entry(bus, &usb_bus_list, bus_list) {
                /* recurse through all children of the root hub */
-               if (!bus->root_hub)
+               if (!bus_to_hcd(bus)->rh_registered)
                        continue;
                usb_lock_device(bus->root_hub);
                ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
index bc84106..75ba209 100644 (file)
@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
        if (retval) {
                dev_err (parent_dev, "can't register root hub for %s, %d\n",
                                dev_name(&usb_dev->dev), retval);
-       }
-       mutex_unlock(&usb_bus_list_lock);
-
-       if (retval == 0) {
+       } else {
                spin_lock_irq (&hcd_root_hub_lock);
                hcd->rh_registered = 1;
                spin_unlock_irq (&hcd_root_hub_lock);
@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
                if (HCD_DEAD(hcd))
                        usb_hc_died (hcd);      /* This time clean up */
        }
+       mutex_unlock(&usb_bus_list_lock);
 
        return retval;
 }
index aaa8d2b..0bf72f9 100644 (file)
@@ -467,7 +467,8 @@ static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
        /* From the GPIO notifying the over-current situation, find
         * out the corresponding port */
        at91_for_each_port(port) {
-               if (gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
+               if (gpio_is_valid(pdata->overcurrent_pin[port]) &&
+                               gpio_to_irq(pdata->overcurrent_pin[port]) == irq) {
                        gpio = pdata->overcurrent_pin[port];
                        break;
                }
index 211a492..d8dedc7 100644 (file)
@@ -76,9 +76,24 @@ static int virqfd_wakeup(wait_queue_t *wait, unsigned mode, int sync, void *key)
                        schedule_work(&virqfd->inject);
        }
 
-       if (flags & POLLHUP)
-               /* The eventfd is closing, detach from VFIO */
-               virqfd_deactivate(virqfd);
+       if (flags & POLLHUP) {
+               unsigned long flags;
+               spin_lock_irqsave(&virqfd->vdev->irqlock, flags);
+
+               /*
+                * The eventfd is closing, if the virqfd has not yet been
+                * queued for release, as determined by testing whether the
+                * vdev pointer to it is still valid, queue it now.  As
+                * with kvm irqfds, we know we won't race against the virqfd
+                * going away because we hold wqh->lock to get here.
+                */
+               if (*(virqfd->pvirqfd) == virqfd) {
+                       *(virqfd->pvirqfd) = NULL;
+                       virqfd_deactivate(virqfd);
+               }
+
+               spin_unlock_irqrestore(&virqfd->vdev->irqlock, flags);
+       }
 
        return 0;
 }
@@ -93,7 +108,6 @@ static void virqfd_ptable_queue_proc(struct file *file,
 static void virqfd_shutdown(struct work_struct *work)
 {
        struct virqfd *virqfd = container_of(work, struct virqfd, shutdown);
-       struct virqfd **pvirqfd = virqfd->pvirqfd;
        u64 cnt;
 
        eventfd_ctx_remove_wait_queue(virqfd->eventfd, &virqfd->wait, &cnt);
@@ -101,7 +115,6 @@ static void virqfd_shutdown(struct work_struct *work)
        eventfd_ctx_put(virqfd->eventfd);
 
        kfree(virqfd);
-       *pvirqfd = NULL;
 }
 
 static void virqfd_inject(struct work_struct *work)
@@ -122,15 +135,11 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
        int ret = 0;
        unsigned int events;
 
-       if (*pvirqfd)
-               return -EBUSY;
-
        virqfd = kzalloc(sizeof(*virqfd), GFP_KERNEL);
        if (!virqfd)
                return -ENOMEM;
 
        virqfd->pvirqfd = pvirqfd;
-       *pvirqfd = virqfd;
        virqfd->vdev = vdev;
        virqfd->handler = handler;
        virqfd->thread = thread;
@@ -154,6 +163,23 @@ static int virqfd_enable(struct vfio_pci_device *vdev,
        virqfd->eventfd = ctx;
 
        /*
+        * virqfds can be released by closing the eventfd or directly
+        * through ioctl.  These are both done through a workqueue, so
+        * we update the pointer to the virqfd under lock to avoid
+        * pushing multiple jobs to release the same virqfd.
+        */
+       spin_lock_irq(&vdev->irqlock);
+
+       if (*pvirqfd) {
+               spin_unlock_irq(&vdev->irqlock);
+               ret = -EBUSY;
+               goto fail;
+       }
+       *pvirqfd = virqfd;
+
+       spin_unlock_irq(&vdev->irqlock);
+
+       /*
         * Install our own custom wake-up handling so we are notified via
         * a callback whenever someone signals the underlying eventfd.
         */
@@ -187,19 +213,29 @@ fail:
                fput(file);
 
        kfree(virqfd);
-       *pvirqfd = NULL;
 
        return ret;
 }
 
-static void virqfd_disable(struct virqfd *virqfd)
+static void virqfd_disable(struct vfio_pci_device *vdev,
+                          struct virqfd **pvirqfd)
 {
-       if (!virqfd)
-               return;
+       unsigned long flags;
+
+       spin_lock_irqsave(&vdev->irqlock, flags);
+
+       if (*pvirqfd) {
+               virqfd_deactivate(*pvirqfd);
+               *pvirqfd = NULL;
+       }
 
-       virqfd_deactivate(virqfd);
+       spin_unlock_irqrestore(&vdev->irqlock, flags);
 
-       /* Block until we know all outstanding shutdown jobs have completed. */
+       /*
+        * Block until we know all outstanding shutdown jobs have completed.
+        * Even if we don't queue the job, flush the wq to be sure it's
+        * been released.
+        */
        flush_workqueue(vfio_irqfd_cleanup_wq);
 }
 
@@ -392,8 +428,8 @@ static int vfio_intx_set_signal(struct vfio_pci_device *vdev, int fd)
 static void vfio_intx_disable(struct vfio_pci_device *vdev)
 {
        vfio_intx_set_signal(vdev, -1);
-       virqfd_disable(vdev->ctx[0].unmask);
-       virqfd_disable(vdev->ctx[0].mask);
+       virqfd_disable(vdev, &vdev->ctx[0].unmask);
+       virqfd_disable(vdev, &vdev->ctx[0].mask);
        vdev->irq_type = VFIO_PCI_NUM_IRQS;
        vdev->num_ctx = 0;
        kfree(vdev->ctx);
@@ -539,8 +575,8 @@ static void vfio_msi_disable(struct vfio_pci_device *vdev, bool msix)
        vfio_msi_set_block(vdev, 0, vdev->num_ctx, NULL, msix);
 
        for (i = 0; i < vdev->num_ctx; i++) {
-               virqfd_disable(vdev->ctx[i].unmask);
-               virqfd_disable(vdev->ctx[i].mask);
+               virqfd_disable(vdev, &vdev->ctx[i].unmask);
+               virqfd_disable(vdev, &vdev->ctx[i].mask);
        }
 
        if (msix) {
@@ -577,7 +613,7 @@ static int vfio_pci_set_intx_unmask(struct vfio_pci_device *vdev,
                                             vfio_send_intx_eventfd, NULL,
                                             &vdev->ctx[0].unmask, fd);
 
-               virqfd_disable(vdev->ctx[0].unmask);
+               virqfd_disable(vdev, &vdev->ctx[0].unmask);
        }
 
        return 0;
index f75da87..f49181c 100644 (file)
@@ -228,7 +228,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)
        data->port = pdata->flags;
        if (data->port < 0) {
                dev_err(&pdev->dev, "wrong platform data is assigned");
-               kfree(data);
                return -EINVAL;
        }
 
index 4bc2b8a..663c308 100644 (file)
@@ -461,7 +461,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
 done:
        platform_set_drvdata(pdev, dsim);
 
-       dev_dbg(&pdev->dev, "%s() completed sucessfuly (%s mode)\n", __func__,
+       dev_dbg(&pdev->dev, "%s() completed successfully (%s mode)\n", __func__,
                dsim_config->e_interface == DSIM_COMMAND ? "CPU" : "RGB");
 
        return 0;
index 8e4a446..b244f06 100644 (file)
@@ -694,6 +694,10 @@ static int __devinit tmiofb_probe(struct platform_device *dev)
                dev_err(&dev->dev, "NULL platform data!\n");
                return -EINVAL;
        }
+       if (ccr == NULL || lcr == NULL || vram == NULL || irq < 0) {
+               dev_err(&dev->dev, "missing resources\n");
+               return -EINVAL;
+       }
 
        info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev);
 
index 530a2d3..7c294f4 100644 (file)
@@ -349,7 +349,7 @@ static void ds1wm_search(void *data, struct w1_master *master_dev,
                        "pass: %d entering ASM\n", pass);
                ds1wm_write_register(ds1wm_data, DS1WM_CMD, DS1WM_CMD_SRA);
                dev_dbg(&ds1wm_data->pdev->dev,
-                       "pass: %d begining nibble loop\n", pass);
+                       "pass: %d beginning nibble loop\n", pass);
 
                r_prime = 0;
                d = 0;
index 0d195b5..9821b67 100644 (file)
@@ -116,7 +116,7 @@ struct btrfs_ordered_sum;
 #define BTRFS_FREE_SPACE_OBJECTID -11ULL
 
 /*
- * The inode number assigned to the special inode for sotring
+ * The inode number assigned to the special inode for storing
  * free ino cache
  */
 #define BTRFS_FREE_INO_OBJECTID -12ULL
index ab53005..c9d7036 100644 (file)
@@ -18,7 +18,7 @@
 #ifndef __DELAYED_REF__
 #define __DELAYED_REF__
 
-/* these are the possible values of struct btrfs_delayed_ref->action */
+/* these are the possible values of struct btrfs_delayed_ref_node->action */
 #define BTRFS_ADD_DELAYED_REF    1 /* add one backref to the tree */
 #define BTRFS_DROP_DELAYED_REF   2 /* delete one backref from the tree */
 #define BTRFS_ADD_DELAYED_EXTENT 3 /* record a full extent allocation */
index ec154f9..316b07a 100644 (file)
@@ -1971,8 +1971,8 @@ out:
                                      ordered_extent->len - 1, NULL, GFP_NOFS);
 
        /*
-        * This needs to be dont to make sure anybody waiting knows we are done
-        * upating everything for this ordered extent.
+        * This needs to be done to make sure anybody waiting knows we are done
+        * updating everything for this ordered extent.
         */
        btrfs_remove_ordered_extent(inode, ordered_extent);
 
index 16521a9..693f95b 100644 (file)
@@ -1134,6 +1134,8 @@ positive:
        return 1;
 
 rename_retry:
+       if (locked)
+               goto again;
        locked = 1;
        write_seqlock(&rename_lock);
        goto again;
@@ -1141,7 +1143,7 @@ rename_retry:
 EXPORT_SYMBOL(have_submounts);
 
 /*
- * Search the dentry child list for the specified parent,
+ * Search the dentry child list of the specified parent,
  * and move any unused dentries to the end of the unused
  * list for prune_dcache(). We descend to the next level
  * whenever the d_subdirs list is non-empty and continue
@@ -1236,6 +1238,8 @@ out:
 rename_retry:
        if (found)
                return found;
+       if (locked)
+               goto again;
        locked = 1;
        write_seqlock(&rename_lock);
        goto again;
@@ -3035,6 +3039,8 @@ resume:
        return;
 
 rename_retry:
+       if (locked)
+               goto again;
        locked = 1;
        write_seqlock(&rename_lock);
        goto again;
index 376aa77..2616d0e 100644 (file)
@@ -479,7 +479,7 @@ void ext2_discard_reservation(struct inode *inode)
 /**
  * ext2_free_blocks() -- Free given blocks and update quota and i_blocks
  * @inode:             inode
- * @block:             start physcial block to free
+ * @block:             start physical block to free
  * @count:             number of blocks to free
  */
 void ext2_free_blocks (struct inode * inode, unsigned long block,
index 90d901f..7320a66 100644 (file)
@@ -483,7 +483,7 @@ void ext3_discard_reservation(struct inode *inode)
  * ext3_free_blocks_sb() -- Free given blocks and update quota
  * @handle:                    handle to this transaction
  * @sb:                                super block
- * @block:                     start physcial block to free
+ * @block:                     start physical block to free
  * @count:                     number of blocks to free
  * @pdquot_freed_blocks:       pointer to quota
  */
index ff574b4..7e87e37 100644 (file)
@@ -3207,7 +3207,7 @@ out_brelse:
  *
  * - Within generic_file_write() for O_SYNC files.
  *   Here, there will be no transaction running. We wait for any running
- *   trasnaction to commit.
+ *   transaction to commit.
  *
  * - Within sys_sync(), kupdate and such.
  *   We wait on commit, if tol to.
index dff171c..c862ee5 100644 (file)
@@ -3313,7 +3313,7 @@ int ext4_discard_partial_page_buffers(handle_t *handle,
  * handle: The journal handle
  * inode:  The files inode
  * page:   A locked page that contains the offset "from"
- * from:   The starting byte offset (from the begining of the file)
+ * from:   The starting byte offset (from the beginning of the file)
  *         to begin discarding
  * len:    The length of bytes to discard
  * flags:  Optional flags that may be used:
@@ -3321,11 +3321,11 @@ int ext4_discard_partial_page_buffers(handle_t *handle,
  *         EXT4_DISCARD_PARTIAL_PG_ZERO_UNMAPPED
  *         Only zero the regions of the page whose buffer heads
  *         have already been unmapped.  This flag is appropriate
- *         for updateing the contents of a page whose blocks may
+ *         for updating the contents of a page whose blocks may
  *         have already been released, and we only want to zero
  *         out the regions that correspond to those released blocks.
  *
- * Returns zero on sucess or negative on failure.
+ * Returns zero on success or negative on failure.
  */
 static int ext4_discard_partial_page_buffers_no_lock(handle_t *handle,
                struct inode *inode, struct page *page, loff_t from,
@@ -3486,7 +3486,7 @@ int ext4_can_truncate(struct inode *inode)
  * @offset: The offset where the hole will begin
  * @len:    The length of the hole
  *
- * Returns: 0 on sucess or negative on failure
+ * Returns: 0 on success or negative on failure
  */
 
 int ext4_punch_hole(struct file *file, loff_t offset, loff_t length)
@@ -4008,7 +4008,7 @@ static int ext4_inode_blocks_set(handle_t *handle,
 
        if (i_blocks <= ~0U) {
                /*
-                * i_blocks can be represnted in a 32 bit variable
+                * i_blocks can be represented in a 32 bit variable
                 * as multiple of 512 bytes
                 */
                raw_inode->i_blocks_lo   = cpu_to_le32(i_blocks);
@@ -4169,7 +4169,7 @@ out_brelse:
  *
  * - Within generic_file_write() for O_SYNC files.
  *   Here, there will be no transaction running. We wait for any running
- *   trasnaction to commit.
+ *   transaction to commit.
  *
  * - Within sys_sync(), kupdate and such.
  *   We wait on commit, if tol to.
@@ -4413,7 +4413,7 @@ static int ext4_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
  * worse case, the indexs blocks spread over different block groups
  *
  * If datablocks are discontiguous, they are possible to spread over
- * different block groups too. If they are contiuguous, with flexbg,
+ * different block groups too. If they are contiguous, with flexbg,
  * they could still across block group boundary.
  *
  * Also account for superblock, inode, quota and xattr blocks
index 8eae947..08778f6 100644 (file)
@@ -4709,7 +4709,7 @@ error_return:
  * ext4_group_add_blocks() -- Add given blocks to an existing group
  * @handle:                    handle to this transaction
  * @sb:                                super block
- * @block:                     start physcial block to add to the block group
+ * @block:                     start physical block to add to the block group
  * @count:                     number of blocks to free
  *
  * This marks the blocks as free in the bitmap and buddy.
index be3efc4..6d46c0d 100644 (file)
@@ -577,10 +577,6 @@ static long writeback_chunk_size(struct backing_dev_info *bdi,
 /*
  * Write a portion of b_io inodes which belong to @sb.
  *
- * If @only_this_sb is true, then find and write all such
- * inodes. Otherwise write only ones which go sequentially
- * in reverse order.
- *
  * Return the number of pages and/or inodes written.
  */
 static long writeback_sb_inodes(struct super_block *sb,
index d652634..01c4975 100644 (file)
@@ -612,6 +612,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
        struct gfs2_sbd *sdp = GFS2_SB(mapping->host);
        struct gfs2_inode *m_ip = GFS2_I(sdp->sd_statfs_inode);
        unsigned int data_blocks = 0, ind_blocks = 0, rblocks;
+       unsigned requested = 0;
        int alloc_required;
        int error = 0;
        pgoff_t index = pos >> PAGE_CACHE_SHIFT;
@@ -641,7 +642,8 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
                if (error)
                        goto out_unlock;
 
-               error = gfs2_inplace_reserve(ip, data_blocks + ind_blocks);
+               requested = data_blocks + ind_blocks;
+               error = gfs2_inplace_reserve(ip, requested);
                if (error)
                        goto out_qunlock;
        }
@@ -654,7 +656,7 @@ static int gfs2_write_begin(struct file *file, struct address_space *mapping,
        if (&ip->i_inode == sdp->sd_rindex)
                rblocks += 2 * RES_STATFS;
        if (alloc_required)
-               rblocks += gfs2_rg_blocks(ip);
+               rblocks += gfs2_rg_blocks(ip, requested);
 
        error = gfs2_trans_begin(sdp, rblocks,
                                 PAGE_CACHE_SIZE/sdp->sd_sb.sb_bsize);
@@ -868,8 +870,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
        brelse(dibh);
 failed:
        gfs2_trans_end(sdp);
-       if (gfs2_mb_reserved(ip))
-               gfs2_inplace_release(ip);
+       gfs2_inplace_release(ip);
        if (ip->i_res->rs_qa_qd_num)
                gfs2_quota_unlock(ip);
        if (inode == sdp->sd_rindex) {
@@ -1023,7 +1024,7 @@ static ssize_t gfs2_direct_IO(int rw, struct kiocb *iocb,
                                  offset, nr_segs, gfs2_get_block_direct,
                                  NULL, NULL, 0);
 out:
-       gfs2_glock_dq_m(1, &gh);
+       gfs2_glock_dq(&gh);
        gfs2_holder_uninit(&gh);
        return rv;
 }
index 49cd7dd..1fd3ae2 100644 (file)
@@ -786,7 +786,7 @@ static int do_strip(struct gfs2_inode *ip, struct buffer_head *dibh,
                goto out_rlist;
 
        if (gfs2_rs_active(ip->i_res)) /* needs to be done with the rgrp glock held */
-               gfs2_rs_deltree(ip->i_res);
+               gfs2_rs_deltree(ip, ip->i_res);
 
        error = gfs2_trans_begin(sdp, rg_blocks + RES_DINODE +
                                 RES_INDIRECT + RES_STATFS + RES_QUOTA,
index 382000f..30e2199 100644 (file)
@@ -441,7 +441,7 @@ static int gfs2_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf)
                rblocks += data_blocks ? data_blocks : 1;
        if (ind_blocks || data_blocks) {
                rblocks += RES_STATFS + RES_QUOTA;
-               rblocks += gfs2_rg_blocks(ip);
+               rblocks += gfs2_rg_blocks(ip, data_blocks + ind_blocks);
        }
        ret = gfs2_trans_begin(sdp, rblocks, 0);
        if (ret)
@@ -845,7 +845,7 @@ retry:
                                &max_bytes, &data_blocks, &ind_blocks);
 
                rblocks = RES_DINODE + ind_blocks + RES_STATFS + RES_QUOTA +
-                         RES_RG_HDR + gfs2_rg_blocks(ip);
+                         RES_RG_HDR + gfs2_rg_blocks(ip, data_blocks + ind_blocks);
                if (gfs2_is_jdata(ip))
                        rblocks += data_blocks ? data_blocks : 1;
 
index 1ed81f4..e6c2fd5 100644 (file)
@@ -186,20 +186,6 @@ static void gfs2_glock_remove_from_lru(struct gfs2_glock *gl)
 }
 
 /**
- * __gfs2_glock_schedule_for_reclaim - Add a glock to the reclaim list
- * @gl: the glock
- *
- * If the glock is demotable, then we add it (or move it) to the end
- * of the glock LRU list.
- */
-
-static void __gfs2_glock_schedule_for_reclaim(struct gfs2_glock *gl)
-{
-       if (demote_ok(gl))
-               gfs2_glock_add_to_lru(gl);
-}
-
-/**
  * gfs2_glock_put_nolock() - Decrement reference count on glock
  * @gl: The glock to put
  *
@@ -883,7 +869,14 @@ static int gfs2_glock_demote_wait(void *word)
        return 0;
 }
 
-static void wait_on_holder(struct gfs2_holder *gh)
+/**
+ * gfs2_glock_wait - wait on a glock acquisition
+ * @gh: the glock holder
+ *
+ * Returns: 0 on success
+ */
+
+int gfs2_glock_wait(struct gfs2_holder *gh)
 {
        unsigned long time1 = jiffies;
 
@@ -894,12 +887,7 @@ static void wait_on_holder(struct gfs2_holder *gh)
                gh->gh_gl->gl_hold_time = min(gh->gh_gl->gl_hold_time +
                                              GL_GLOCK_HOLD_INCR,
                                              GL_GLOCK_MAX_HOLD);
-}
-
-static void wait_on_demote(struct gfs2_glock *gl)
-{
-       might_sleep();
-       wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE);
+       return gh->gh_error;
 }
 
 /**
@@ -929,19 +917,6 @@ static void handle_callback(struct gfs2_glock *gl, unsigned int state,
        trace_gfs2_demote_rq(gl);
 }
 
-/**
- * gfs2_glock_wait - wait on a glock acquisition
- * @gh: the glock holder
- *
- * Returns: 0 on success
- */
-
-int gfs2_glock_wait(struct gfs2_holder *gh)
-{
-       wait_on_holder(gh);
-       return gh->gh_error;
-}
-
 void gfs2_print_dbg(struct seq_file *seq, const char *fmt, ...)
 {
        struct va_format vaf;
@@ -979,7 +954,7 @@ __acquires(&gl->gl_spin)
        struct gfs2_sbd *sdp = gl->gl_sbd;
        struct list_head *insert_pt = NULL;
        struct gfs2_holder *gh2;
-       int try_lock = 0;
+       int try_futile = 0;
 
        BUG_ON(gh->gh_owner_pid == NULL);
        if (test_and_set_bit(HIF_WAIT, &gh->gh_iflags))
@@ -987,7 +962,7 @@ __acquires(&gl->gl_spin)
 
        if (gh->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) {
                if (test_bit(GLF_LOCK, &gl->gl_flags))
-                       try_lock = 1;
+                       try_futile = !may_grant(gl, gh);
                if (test_bit(GLF_INVALIDATE_IN_PROGRESS, &gl->gl_flags))
                        goto fail;
        }
@@ -996,9 +971,8 @@ __acquires(&gl->gl_spin)
                if (unlikely(gh2->gh_owner_pid == gh->gh_owner_pid &&
                    (gh->gh_gl->gl_ops->go_type != LM_TYPE_FLOCK)))
                        goto trap_recursive;
-               if (try_lock &&
-                   !(gh2->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB)) &&
-                   !may_grant(gl, gh)) {
+               if (try_futile &&
+                   !(gh2->gh_flags & (LM_FLAG_TRY | LM_FLAG_TRY_1CB))) {
 fail:
                        gh->gh_error = GLR_TRYFAILED;
                        gfs2_holder_wake(gh);
@@ -1121,8 +1095,9 @@ void gfs2_glock_dq(struct gfs2_holder *gh)
                    !test_bit(GLF_DEMOTE, &gl->gl_flags))
                        fast_path = 1;
        }
-       if (!test_bit(GLF_LFLUSH, &gl->gl_flags))
-               __gfs2_glock_schedule_for_reclaim(gl);
+       if (!test_bit(GLF_LFLUSH, &gl->gl_flags) && demote_ok(gl))
+               gfs2_glock_add_to_lru(gl);
+
        trace_gfs2_glock_queue(gh, 0);
        spin_unlock(&gl->gl_spin);
        if (likely(fast_path))
@@ -1141,7 +1116,8 @@ void gfs2_glock_dq_wait(struct gfs2_holder *gh)
 {
        struct gfs2_glock *gl = gh->gh_gl;
        gfs2_glock_dq(gh);
-       wait_on_demote(gl);
+       might_sleep();
+       wait_on_bit(&gl->gl_flags, GLF_DEMOTE, gfs2_glock_demote_wait, TASK_UNINTERRUPTIBLE);
 }
 
 /**
index 4bdcf37..32cc4fd 100644 (file)
@@ -94,6 +94,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
        /* A shortened, inline version of gfs2_trans_begin() */
        tr.tr_reserved = 1 + gfs2_struct2blk(sdp, tr.tr_revokes, sizeof(u64));
        tr.tr_ip = (unsigned long)__builtin_return_address(0);
+       sb_start_intwrite(sdp->sd_vfs);
        gfs2_log_reserve(sdp, tr.tr_reserved);
        BUG_ON(current->journal_info);
        current->journal_info = &tr;
index aaecc80..3d469d3 100644 (file)
@@ -99,9 +99,26 @@ struct gfs2_rgrpd {
 #define GFS2_RDF_MASK          0xf0000000 /* mask for internal flags */
        spinlock_t rd_rsspin;           /* protects reservation related vars */
        struct rb_root rd_rstree;       /* multi-block reservation tree */
-       u32 rd_rs_cnt;                  /* count of current reservations */
 };
 
+struct gfs2_rbm {
+       struct gfs2_rgrpd *rgd;
+       struct gfs2_bitmap *bi; /* Bitmap must belong to the rgd */
+       u32 offset;             /* The offset is bitmap relative */
+};
+
+static inline u64 gfs2_rbm_to_block(const struct gfs2_rbm *rbm)
+{
+       return rbm->rgd->rd_data0 + (rbm->bi->bi_start * GFS2_NBBY) + rbm->offset;
+}
+
+static inline bool gfs2_rbm_eq(const struct gfs2_rbm *rbm1,
+                              const struct gfs2_rbm *rbm2)
+{
+       return (rbm1->rgd == rbm2->rgd) && (rbm1->bi == rbm2->bi) && 
+              (rbm1->offset == rbm2->offset);
+}
+
 enum gfs2_state_bits {
        BH_Pinned = BH_PrivateStart,
        BH_Escaped = BH_PrivateStart + 1,
@@ -250,18 +267,11 @@ struct gfs2_blkreserv {
        /* components used during write (step 1): */
        atomic_t rs_sizehint;         /* hint of the write size */
 
-       /* components used during inplace_reserve (step 2): */
-       u32 rs_requested; /* Filled in by caller of gfs2_inplace_reserve() */
-
-       /* components used during get_local_rgrp (step 3): */
-       struct gfs2_rgrpd *rs_rgd;    /* pointer to the gfs2_rgrpd */
        struct gfs2_holder rs_rgd_gh; /* Filled in by get_local_rgrp */
        struct rb_node rs_node;       /* link to other block reservations */
-
-       /* components used during block searches and assignments (step 4): */
-       struct gfs2_bitmap *rs_bi;    /* bitmap for the current allocation */
-       u32 rs_biblk;                 /* start block relative to the bi */
+       struct gfs2_rbm rs_rbm;       /* Start of reservation */
        u32 rs_free;                  /* how many blocks are still free */
+       u64 rs_inum;                  /* Inode number for reservation */
 
        /* ancillary quota stuff */
        struct gfs2_quota_data *rs_qa_qd[2 * MAXQUOTAS];
index 753af3d..381893c 100644 (file)
@@ -712,14 +712,9 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
        if (error)
                goto fail_gunlock2;
 
-       /* The newly created inode needs a reservation so it can allocate
-          xattrs. At the same time, we want new blocks allocated to the new
-          dinode to be as contiguous as possible. Since we allocated the
-          dinode block under the directory's reservation, we transfer
-          ownership of that reservation to the new inode. The directory
-          doesn't need a reservation unless it needs a new allocation. */
-       ip->i_res = dip->i_res;
-       dip->i_res = NULL;
+       error = gfs2_rs_alloc(ip);
+       if (error)
+               goto fail_gunlock2;
 
        error = gfs2_acl_create(dip, inode);
        if (error)
@@ -737,10 +732,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
                brelse(bh);
 
        gfs2_trans_end(sdp);
-       /* Check if we reserved space in the rgrp. Function link_dinode may
-          not, depending on whether alloc is required. */
-       if (gfs2_mb_reserved(dip))
-               gfs2_inplace_release(dip);
+       gfs2_inplace_release(dip);
        gfs2_quota_unlock(dip);
        mark_inode_dirty(inode);
        gfs2_glock_dq_uninit_m(2, ghs);
@@ -897,7 +889,7 @@ static int gfs2_link(struct dentry *old_dentry, struct inode *dir,
                        goto out_gunlock_q;
 
                error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
-                                        gfs2_rg_blocks(dip) +
+                                        gfs2_rg_blocks(dip, sdp->sd_max_dirres) +
                                         2 * RES_DINODE + RES_STATFS +
                                         RES_QUOTA, 0);
                if (error)
@@ -1378,7 +1370,7 @@ static int gfs2_rename(struct inode *odir, struct dentry *odentry,
                        goto out_gunlock_q;
 
                error = gfs2_trans_begin(sdp, sdp->sd_max_dirres +
-                                        gfs2_rg_blocks(ndip) +
+                                        gfs2_rg_blocks(ndip, sdp->sd_max_dirres) +
                                         4 * RES_DINODE + 4 * RES_LEAF +
                                         RES_STATFS + RES_QUOTA + 4, 0);
                if (error)
index e5af9dc..e443966 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/mount.h>
 #include <linux/gfs2_ondisk.h>
 #include <linux/quotaops.h>
+#include <linux/lockdep.h>
 
 #include "gfs2.h"
 #include "incore.h"
@@ -766,6 +767,7 @@ fail:
        return error;
 }
 
+static struct lock_class_key gfs2_quota_imutex_key;
 
 static int init_inodes(struct gfs2_sbd *sdp, int undo)
 {
@@ -803,6 +805,12 @@ static int init_inodes(struct gfs2_sbd *sdp, int undo)
                fs_err(sdp, "can't get quota file inode: %d\n", error);
                goto fail_rindex;
        }
+       /*
+        * i_mutex on quota files is special. Since this inode is hidden system
+        * file, we are safe to define locking ourselves.
+        */
+       lockdep_set_class(&sdp->sd_quota_inode->i_mutex,
+                         &gfs2_quota_imutex_key);
 
        error = gfs2_rindex_update(sdp);
        if (error)
index a3bde91..4021dec 100644 (file)
@@ -765,6 +765,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
        struct gfs2_holder *ghs, i_gh;
        unsigned int qx, x;
        struct gfs2_quota_data *qd;
+       unsigned reserved;
        loff_t offset;
        unsigned int nalloc = 0, blocks;
        int error;
@@ -781,7 +782,7 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
                return -ENOMEM;
 
        sort(qda, num_qd, sizeof(struct gfs2_quota_data *), sort_qd, NULL);
-       mutex_lock_nested(&ip->i_inode.i_mutex, I_MUTEX_QUOTA);
+       mutex_lock(&ip->i_inode.i_mutex);
        for (qx = 0; qx < num_qd; qx++) {
                error = gfs2_glock_nq_init(qda[qx]->qd_gl, LM_ST_EXCLUSIVE,
                                           GL_NOCACHE, &ghs[qx]);
@@ -811,13 +812,13 @@ static int do_sync(unsigned int num_qd, struct gfs2_quota_data **qda)
         * two blocks need to be updated instead of 1 */
        blocks = num_qd * data_blocks + RES_DINODE + num_qd + 3;
 
-       error = gfs2_inplace_reserve(ip, 1 +
-                                    (nalloc * (data_blocks + ind_blocks)));
+       reserved = 1 + (nalloc * (data_blocks + ind_blocks));
+       error = gfs2_inplace_reserve(ip, reserved);
        if (error)
                goto out_alloc;
 
        if (nalloc)
-               blocks += gfs2_rg_blocks(ip) + nalloc * ind_blocks + RES_STATFS;
+               blocks += gfs2_rg_blocks(ip, reserved) + nalloc * ind_blocks + RES_STATFS;
 
        error = gfs2_trans_begin(sdp, blocks, 0);
        if (error)
@@ -1598,7 +1599,7 @@ static int gfs2_set_dqblk(struct super_block *sb, int type, qid_t id,
                error = gfs2_inplace_reserve(ip, blocks);
                if (error)
                        goto out_i;
-               blocks += gfs2_rg_blocks(ip);
+               blocks += gfs2_rg_blocks(ip, blocks);
        }
 
        /* Some quotas span block boundaries and can update two blocks,
index c9ed814..3cc402c 100644 (file)
@@ -35,9 +35,6 @@
 #define BFITNOENT ((u32)~0)
 #define NO_BLOCK ((u64)~0)
 
-#define RSRV_CONTENTION_FACTOR 4
-#define RGRP_RSRV_MAX_CONTENDERS 2
-
 #if BITS_PER_LONG == 32
 #define LBITMASK   (0x55555555UL)
 #define LBITSKIP55 (0x55555555UL)
@@ -67,53 +64,48 @@ static const char valid_change[16] = {
                1, 0, 0, 0
 };
 
-static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal,
-                       unsigned char old_state,
-                       struct gfs2_bitmap **rbi);
+static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 minext,
+                         const struct gfs2_inode *ip, bool nowrap);
+
 
 /**
  * gfs2_setbit - Set a bit in the bitmaps
- * @rgd: the resource group descriptor
- * @buf2: the clone buffer that holds the bitmaps
- * @bi: the bitmap structure
- * @block: the block to set
+ * @rbm: The position of the bit to set
+ * @do_clone: Also set the clone bitmap, if it exists
  * @new_state: the new state of the block
  *
  */
 
-static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf2,
-                              struct gfs2_bitmap *bi, u32 block,
+static inline void gfs2_setbit(const struct gfs2_rbm *rbm, bool do_clone,
                               unsigned char new_state)
 {
        unsigned char *byte1, *byte2, *end, cur_state;
-       unsigned int buflen = bi->bi_len;
-       const unsigned int bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
+       unsigned int buflen = rbm->bi->bi_len;
+       const unsigned int bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
 
-       byte1 = bi->bi_bh->b_data + bi->bi_offset + (block / GFS2_NBBY);
-       end = bi->bi_bh->b_data + bi->bi_offset + buflen;
+       byte1 = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset + (rbm->offset / GFS2_NBBY);
+       end = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset + buflen;
 
        BUG_ON(byte1 >= end);
 
        cur_state = (*byte1 >> bit) & GFS2_BIT_MASK;
 
        if (unlikely(!valid_change[new_state * 4 + cur_state])) {
-               printk(KERN_WARNING "GFS2: buf_blk = 0x%llx old_state=%d, "
-                      "new_state=%d\n",
-                      (unsigned long long)block, cur_state, new_state);
-               printk(KERN_WARNING "GFS2: rgrp=0x%llx bi_start=0x%lx\n",
-                      (unsigned long long)rgd->rd_addr,
-                      (unsigned long)bi->bi_start);
-               printk(KERN_WARNING "GFS2: bi_offset=0x%lx bi_len=0x%lx\n",
-                      (unsigned long)bi->bi_offset,
-                      (unsigned long)bi->bi_len);
+               printk(KERN_WARNING "GFS2: buf_blk = 0x%x old_state=%d, "
+                      "new_state=%d\n", rbm->offset, cur_state, new_state);
+               printk(KERN_WARNING "GFS2: rgrp=0x%llx bi_start=0x%x\n",
+                      (unsigned long long)rbm->rgd->rd_addr,
+                      rbm->bi->bi_start);
+               printk(KERN_WARNING "GFS2: bi_offset=0x%x bi_len=0x%x\n",
+                      rbm->bi->bi_offset, rbm->bi->bi_len);
                dump_stack();
-               gfs2_consist_rgrpd(rgd);
+               gfs2_consist_rgrpd(rbm->rgd);
                return;
        }
        *byte1 ^= (cur_state ^ new_state) << bit;
 
-       if (buf2) {
-               byte2 = buf2 + bi->bi_offset + (block / GFS2_NBBY);
+       if (do_clone && rbm->bi->bi_clone) {
+               byte2 = rbm->bi->bi_clone + rbm->bi->bi_offset + (rbm->offset / GFS2_NBBY);
                cur_state = (*byte2 >> bit) & GFS2_BIT_MASK;
                *byte2 ^= (cur_state ^ new_state) << bit;
        }
@@ -121,30 +113,21 @@ static inline void gfs2_setbit(struct gfs2_rgrpd *rgd, unsigned char *buf2,
 
 /**
  * gfs2_testbit - test a bit in the bitmaps
- * @rgd: the resource group descriptor
- * @buffer: the buffer that holds the bitmaps
- * @buflen: the length (in bytes) of the buffer
- * @block: the block to read
+ * @rbm: The bit to test
  *
+ * Returns: The two bit block state of the requested bit
  */
 
-static inline unsigned char gfs2_testbit(struct gfs2_rgrpd *rgd,
-                                        const unsigned char *buffer,
-                                        unsigned int buflen, u32 block)
+static inline u8 gfs2_testbit(const struct gfs2_rbm *rbm)
 {
-       const unsigned char *byte, *end;
-       unsigned char cur_state;
+       const u8 *buffer = rbm->bi->bi_bh->b_data + rbm->bi->bi_offset;
+       const u8 *byte;
        unsigned int bit;
 
-       byte = buffer + (block / GFS2_NBBY);
-       bit = (block % GFS2_NBBY) * GFS2_BIT_SIZE;
-       end = buffer + buflen;
-
-       gfs2_assert(rgd->rd_sbd, byte < end);
+       byte = buffer + (rbm->offset / GFS2_NBBY);
+       bit = (rbm->offset % GFS2_NBBY) * GFS2_BIT_SIZE;
 
-       cur_state = (*byte >> bit) & GFS2_BIT_MASK;
-
-       return cur_state;
+       return (*byte >> bit) & GFS2_BIT_MASK;
 }
 
 /**
@@ -192,7 +175,7 @@ static inline u64 gfs2_bit_search(const __le64 *ptr, u64 mask, u8 state)
  */
 static inline int rs_cmp(u64 blk, u32 len, struct gfs2_blkreserv *rs)
 {
-       u64 startblk = gfs2_rs_startblk(rs);
+       u64 startblk = gfs2_rbm_to_block(&rs->rs_rbm);
 
        if (blk >= startblk + rs->rs_free)
                return 1;
@@ -202,36 +185,6 @@ static inline int rs_cmp(u64 blk, u32 len, struct gfs2_blkreserv *rs)
 }
 
 /**
- * rs_find - Find a rgrp multi-block reservation that contains a given block
- * @rgd: The rgrp
- * @rgblk: The block we're looking for, relative to the rgrp
- */
-static struct gfs2_blkreserv *rs_find(struct gfs2_rgrpd *rgd, u32 rgblk)
-{
-       struct rb_node **newn;
-       int rc;
-       u64 fsblk = rgblk + rgd->rd_data0;
-
-       spin_lock(&rgd->rd_rsspin);
-       newn = &rgd->rd_rstree.rb_node;
-       while (*newn) {
-               struct gfs2_blkreserv *cur =
-                       rb_entry(*newn, struct gfs2_blkreserv, rs_node);
-               rc = rs_cmp(fsblk, 1, cur);
-               if (rc < 0)
-                       newn = &((*newn)->rb_left);
-               else if (rc > 0)
-                       newn = &((*newn)->rb_right);
-               else {
-                       spin_unlock(&rgd->rd_rsspin);
-                       return cur;
-               }
-       }
-       spin_unlock(&rgd->rd_rsspin);
-       return NULL;
-}
-
-/**
  * gfs2_bitfit - Search an rgrp's bitmap buffer to find a bit-pair representing
  *       a block in a given allocation state.
  * @buf: the buffer that holds the bitmaps
@@ -262,8 +215,6 @@ static u32 gfs2_bitfit(const u8 *buf, const unsigned int len,
        u64 mask = 0x5555555555555555ULL;
        u32 bit;
 
-       BUG_ON(state > 3);
-
        /* Mask off bits we don't care about at the start of the search */
        mask <<= spoint;
        tmp = gfs2_bit_search(ptr, mask, state);
@@ -285,6 +236,131 @@ static u32 gfs2_bitfit(const u8 *buf, const unsigned int len,
 }
 
 /**
+ * gfs2_rbm_from_block - Set the rbm based upon rgd and block number
+ * @rbm: The rbm with rgd already set correctly
+ * @block: The block number (filesystem relative)
+ *
+ * This sets the bi and offset members of an rbm based on a
+ * resource group and a filesystem relative block number. The
+ * resource group must be set in the rbm on entry, the bi and
+ * offset members will be set by this function.
+ *
+ * Returns: 0 on success, or an error code
+ */
+
+static int gfs2_rbm_from_block(struct gfs2_rbm *rbm, u64 block)
+{
+       u64 rblock = block - rbm->rgd->rd_data0;
+       u32 goal = (u32)rblock;
+       int x;
+
+       if (WARN_ON_ONCE(rblock > UINT_MAX))
+               return -EINVAL;
+       if (block >= rbm->rgd->rd_data0 + rbm->rgd->rd_data)
+               return -E2BIG;
+
+       for (x = 0; x < rbm->rgd->rd_length; x++) {
+               rbm->bi = rbm->rgd->rd_bits + x;
+               if (goal < (rbm->bi->bi_start + rbm->bi->bi_len) * GFS2_NBBY) {
+                       rbm->offset = goal - (rbm->bi->bi_start * GFS2_NBBY);
+                       break;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * gfs2_unaligned_extlen - Look for free blocks which are not byte aligned
+ * @rbm: Position to search (value/result)
+ * @n_unaligned: Number of unaligned blocks to check
+ * @len: Decremented for each block found (terminate on zero)
+ *
+ * Returns: true if a non-free block is encountered
+ */
+
+static bool gfs2_unaligned_extlen(struct gfs2_rbm *rbm, u32 n_unaligned, u32 *len)
+{
+       u64 block;
+       u32 n;
+       u8 res;
+
+       for (n = 0; n < n_unaligned; n++) {
+               res = gfs2_testbit(rbm);
+               if (res != GFS2_BLKST_FREE)
+                       return true;
+               (*len)--;
+               if (*len == 0)
+                       return true;
+               block = gfs2_rbm_to_block(rbm);
+               if (gfs2_rbm_from_block(rbm, block + 1))
+                       return true;
+       }
+
+       return false;
+}
+
+/**
+ * gfs2_free_extlen - Return extent length of free blocks
+ * @rbm: Starting position
+ * @len: Max length to check
+ *
+ * Starting at the block specified by the rbm, see how many free blocks
+ * there are, not reading more than len blocks ahead. This can be done
+ * using memchr_inv when the blocks are byte aligned, but has to be done
+ * on a block by block basis in case of unaligned blocks. Also this
+ * function can cope with bitmap boundaries (although it must stop on
+ * a resource group boundary)
+ *
+ * Returns: Number of free blocks in the extent
+ */
+
+static u32 gfs2_free_extlen(const struct gfs2_rbm *rrbm, u32 len)
+{
+       struct gfs2_rbm rbm = *rrbm;
+       u32 n_unaligned = rbm.offset & 3;
+       u32 size = len;
+       u32 bytes;
+       u32 chunk_size;
+       u8 *ptr, *start, *end;
+       u64 block;
+
+       if (n_unaligned &&
+           gfs2_unaligned_extlen(&rbm, 4 - n_unaligned, &len))
+               goto out;
+
+       n_unaligned = len & 3;
+       /* Start is now byte aligned */
+       while (len > 3) {
+               start = rbm.bi->bi_bh->b_data;
+               if (rbm.bi->bi_clone)
+                       start = rbm.bi->bi_clone;
+               end = start + rbm.bi->bi_bh->b_size;
+               start += rbm.bi->bi_offset;
+               BUG_ON(rbm.offset & 3);
+               start += (rbm.offset / GFS2_NBBY);
+               bytes = min_t(u32, len / GFS2_NBBY, (end - start));
+               ptr = memchr_inv(start, 0, bytes);
+               chunk_size = ((ptr == NULL) ? bytes : (ptr - start));
+               chunk_size *= GFS2_NBBY;
+               BUG_ON(len < chunk_size);
+               len -= chunk_size;
+               block = gfs2_rbm_to_block(&rbm);
+               gfs2_rbm_from_block(&rbm, block + chunk_size);
+               n_unaligned = 3;
+               if (ptr)
+                       break;
+               n_unaligned = len & 3;
+       }
+
+       /* Deal with any bits left over at the end */
+       if (n_unaligned)
+               gfs2_unaligned_extlen(&rbm, n_unaligned, &len);
+out:
+       return size - len;
+}
+
+/**
  * gfs2_bitcount - count the number of bits in a certain state
  * @rgd: the resource group descriptor
  * @buffer: the buffer that holds the bitmaps
@@ -487,6 +563,8 @@ int gfs2_rs_alloc(struct gfs2_inode *ip)
        if (!res)
                error = -ENOMEM;
 
+       RB_CLEAR_NODE(&res->rs_node);
+
        down_write(&ip->i_rw_mutex);
        if (ip->i_res)
                kmem_cache_free(gfs2_rsrv_cachep, res);
@@ -496,11 +574,12 @@ int gfs2_rs_alloc(struct gfs2_inode *ip)
        return error;
 }
 
-static void dump_rs(struct seq_file *seq, struct gfs2_blkreserv *rs)
+static void dump_rs(struct seq_file *seq, const struct gfs2_blkreserv *rs)
 {
-       gfs2_print_dbg(seq, "  r: %llu s:%llu b:%u f:%u\n",
-                      rs->rs_rgd->rd_addr, gfs2_rs_startblk(rs), rs->rs_biblk,
-                      rs->rs_free);
+       gfs2_print_dbg(seq, "  B: n:%llu s:%llu b:%u f:%u\n",
+                      (unsigned long long)rs->rs_inum,
+                      (unsigned long long)gfs2_rbm_to_block(&rs->rs_rbm),
+                      rs->rs_rbm.offset, rs->rs_free);
 }
 
 /**
@@ -508,41 +587,26 @@ static void dump_rs(struct seq_file *seq, struct gfs2_blkreserv *rs)
  * @rs: The reservation to remove
  *
  */
-static void __rs_deltree(struct gfs2_blkreserv *rs)
+static void __rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs)
 {
        struct gfs2_rgrpd *rgd;
 
        if (!gfs2_rs_active(rs))
                return;
 
-       rgd = rs->rs_rgd;
-       /* We can't do this: The reason is that when the rgrp is invalidated,
-          it's in the "middle" of acquiring the glock, but the HOLDER bit
-          isn't set yet:
-          BUG_ON(!gfs2_glock_is_locked_by_me(rs->rs_rgd->rd_gl));*/
-       trace_gfs2_rs(NULL, rs, TRACE_RS_TREEDEL);
-
-       if (!RB_EMPTY_ROOT(&rgd->rd_rstree))
-               rb_erase(&rs->rs_node, &rgd->rd_rstree);
-       BUG_ON(!rgd->rd_rs_cnt);
-       rgd->rd_rs_cnt--;
+       rgd = rs->rs_rbm.rgd;
+       trace_gfs2_rs(rs, TRACE_RS_TREEDEL);
+       rb_erase(&rs->rs_node, &rgd->rd_rstree);
+       RB_CLEAR_NODE(&rs->rs_node);
 
        if (rs->rs_free) {
                /* return reserved blocks to the rgrp and the ip */
-               BUG_ON(rs->rs_rgd->rd_reserved < rs->rs_free);
-               rs->rs_rgd->rd_reserved -= rs->rs_free;
+               BUG_ON(rs->rs_rbm.rgd->rd_reserved < rs->rs_free);
+               rs->rs_rbm.rgd->rd_reserved -= rs->rs_free;
                rs->rs_free = 0;
-               clear_bit(GBF_FULL, &rs->rs_bi->bi_flags);
+               clear_bit(GBF_FULL, &rs->rs_rbm.bi->bi_flags);
                smp_mb__after_clear_bit();
        }
-       /* We can't change any of the step 1 or step 2 components of the rs.
-          E.g. We can't set rs_rgd to NULL because the rgd glock is held and
-          dequeued through this pointer.
-          Can't: atomic_set(&rs->rs_sizehint, 0);
-          Can't: rs->rs_requested = 0;
-          Can't: rs->rs_rgd = NULL;*/
-       rs->rs_bi = NULL;
-       rs->rs_biblk = 0;
 }
 
 /**
@@ -550,17 +614,16 @@ static void __rs_deltree(struct gfs2_blkreserv *rs)
  * @rs: The reservation to remove
  *
  */
-void gfs2_rs_deltree(struct gfs2_blkreserv *rs)
+void gfs2_rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs)
 {
        struct gfs2_rgrpd *rgd;
 
-       if (!gfs2_rs_active(rs))
-               return;
-
-       rgd = rs->rs_rgd;
-       spin_lock(&rgd->rd_rsspin);
-       __rs_deltree(rs);
-       spin_unlock(&rgd->rd_rsspin);
+       rgd = rs->rs_rbm.rgd;
+       if (rgd) {
+               spin_lock(&rgd->rd_rsspin);
+               __rs_deltree(ip, rs);
+               spin_unlock(&rgd->rd_rsspin);
+       }
 }
 
 /**
@@ -572,8 +635,7 @@ void gfs2_rs_delete(struct gfs2_inode *ip)
 {
        down_write(&ip->i_rw_mutex);
        if (ip->i_res) {
-               gfs2_rs_deltree(ip->i_res);
-               trace_gfs2_rs(ip, ip->i_res, TRACE_RS_DELETE);
+               gfs2_rs_deltree(ip, ip->i_res);
                BUG_ON(ip->i_res->rs_free);
                kmem_cache_free(gfs2_rsrv_cachep, ip->i_res);
                ip->i_res = NULL;
@@ -597,7 +659,7 @@ static void return_all_reservations(struct gfs2_rgrpd *rgd)
        spin_lock(&rgd->rd_rsspin);
        while ((n = rb_first(&rgd->rd_rstree))) {
                rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
-               __rs_deltree(rs);
+               __rs_deltree(NULL, rs);
        }
        spin_unlock(&rgd->rd_rsspin);
 }
@@ -1270,211 +1332,276 @@ out:
 
 /**
  * rs_insert - insert a new multi-block reservation into the rgrp's rb_tree
- * @bi: the bitmap with the blocks
  * @ip: the inode structure
- * @biblk: the 32-bit block number relative to the start of the bitmap
- * @amount: the number of blocks to reserve
  *
- * Returns: NULL - reservation was already taken, so not inserted
- *          pointer to the inserted reservation
  */
-static struct gfs2_blkreserv *rs_insert(struct gfs2_bitmap *bi,
-                                      struct gfs2_inode *ip, u32 biblk,
-                                      int amount)
+static void rs_insert(struct gfs2_inode *ip)
 {
        struct rb_node **newn, *parent = NULL;
        int rc;
        struct gfs2_blkreserv *rs = ip->i_res;
-       struct gfs2_rgrpd *rgd = rs->rs_rgd;
-       u64 fsblock = gfs2_bi2rgd_blk(bi, biblk) + rgd->rd_data0;
+       struct gfs2_rgrpd *rgd = rs->rs_rbm.rgd;
+       u64 fsblock = gfs2_rbm_to_block(&rs->rs_rbm);
+
+       BUG_ON(gfs2_rs_active(rs));
 
        spin_lock(&rgd->rd_rsspin);
        newn = &rgd->rd_rstree.rb_node;
-       BUG_ON(!ip->i_res);
-       BUG_ON(gfs2_rs_active(rs));
-       /* Figure out where to put new node */
-       /*BUG_ON(!gfs2_glock_is_locked_by_me(rgd->rd_gl));*/
        while (*newn) {
                struct gfs2_blkreserv *cur =
                        rb_entry(*newn, struct gfs2_blkreserv, rs_node);
 
                parent = *newn;
-               rc = rs_cmp(fsblock, amount, cur);
+               rc = rs_cmp(fsblock, rs->rs_free, cur);
                if (rc > 0)
                        newn = &((*newn)->rb_right);
                else if (rc < 0)
                        newn = &((*newn)->rb_left);
                else {
                        spin_unlock(&rgd->rd_rsspin);
-                       return NULL; /* reservation already in use */
+                       WARN_ON(1);
+                       return;
                }
        }
 
-       /* Do our reservation work */
-       rs = ip->i_res;
-       rs->rs_free = amount;
-       rs->rs_biblk = biblk;
-       rs->rs_bi = bi;
        rb_link_node(&rs->rs_node, parent, newn);
        rb_insert_color(&rs->rs_node, &rgd->rd_rstree);
 
-       /* Do our inode accounting for the reservation */
-       /*BUG_ON(!gfs2_glock_is_locked_by_me(ip->i_gl));*/
-
        /* Do our rgrp accounting for the reservation */
-       rgd->rd_reserved += amount; /* blocks reserved */
-       rgd->rd_rs_cnt++; /* number of in-tree reservations */
+       rgd->rd_reserved += rs->rs_free; /* blocks reserved */
        spin_unlock(&rgd->rd_rsspin);
-       trace_gfs2_rs(ip, rs, TRACE_RS_INSERT);
-       return rs;
-}
-
-/**
- * unclaimed_blocks - return number of blocks that aren't spoken for
- */
-static u32 unclaimed_blocks(struct gfs2_rgrpd *rgd)
-{
-       return rgd->rd_free_clone - rgd->rd_reserved;
+       trace_gfs2_rs(rs, TRACE_RS_INSERT);
 }
 
 /**
- * rg_mblk_search - find a group of multiple free blocks
+ * rg_mblk_search - find a group of multiple free blocks to form a reservation
  * @rgd: the resource group descriptor
- * @rs: the block reservation
  * @ip: pointer to the inode for which we're reserving blocks
+ * @requested: number of blocks required for this allocation
  *
- * This is very similar to rgblk_search, except we're looking for whole
- * 64-bit words that represent a chunk of 32 free blocks. I'm only focusing
- * on aligned dwords for speed's sake.
- *
- * Returns: 0 if successful or BFITNOENT if there isn't enough free space
  */
 
-static int rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
+static void rg_mblk_search(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip,
+                          unsigned requested)
 {
-       struct gfs2_bitmap *bi = rgd->rd_bits;
-       const u32 length = rgd->rd_length;
-       u32 blk;
-       unsigned int buf, x, search_bytes;
-       u8 *buffer = NULL;
-       u8 *ptr, *end, *nonzero;
-       u32 goal, rsv_bytes;
-       struct gfs2_blkreserv *rs;
-       u32 best_rs_bytes, unclaimed;
-       int best_rs_blocks;
+       struct gfs2_rbm rbm = { .rgd = rgd, };
+       u64 goal;
+       struct gfs2_blkreserv *rs = ip->i_res;
+       u32 extlen;
+       u32 free_blocks = rgd->rd_free_clone - rgd->rd_reserved;
+       int ret;
+
+       extlen = max_t(u32, atomic_read(&rs->rs_sizehint), requested);
+       extlen = clamp(extlen, RGRP_RSRV_MINBLKS, free_blocks);
+       if ((rgd->rd_free_clone < rgd->rd_reserved) || (free_blocks < extlen))
+               return;
 
        /* Find bitmap block that contains bits for goal block */
        if (rgrp_contains_block(rgd, ip->i_goal))
-               goal = ip->i_goal - rgd->rd_data0;
+               goal = ip->i_goal;
        else
-               goal = rgd->rd_last_alloc;
-       for (buf = 0; buf < length; buf++) {
-               bi = rgd->rd_bits + buf;
-               /* Convert scope of "goal" from rgrp-wide to within
-                  found bit block */
-               if (goal < (bi->bi_start + bi->bi_len) * GFS2_NBBY) {
-                       goal -= bi->bi_start * GFS2_NBBY;
-                       goto do_search;
-               }
+               goal = rgd->rd_last_alloc + rgd->rd_data0;
+
+       if (WARN_ON(gfs2_rbm_from_block(&rbm, goal)))
+               return;
+
+       ret = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, extlen, ip, true);
+       if (ret == 0) {
+               rs->rs_rbm = rbm;
+               rs->rs_free = extlen;
+               rs->rs_inum = ip->i_no_addr;
+               rs_insert(ip);
        }
-       buf = 0;
-       goal = 0;
-
-do_search:
-       best_rs_blocks = max_t(int, atomic_read(&ip->i_res->rs_sizehint),
-                              (RGRP_RSRV_MINBLKS * rgd->rd_length));
-       best_rs_bytes = (best_rs_blocks *
-                        (1 + (RSRV_CONTENTION_FACTOR * rgd->rd_rs_cnt))) /
-               GFS2_NBBY; /* 1 + is for our not-yet-created reservation */
-       best_rs_bytes = ALIGN(best_rs_bytes, sizeof(u64));
-       unclaimed = unclaimed_blocks(rgd);
-       if (best_rs_bytes * GFS2_NBBY > unclaimed)
-               best_rs_bytes = unclaimed >> GFS2_BIT_SIZE;
-
-       for (x = 0; x <= length; x++) {
-               bi = rgd->rd_bits + buf;
+}
+
+/**
+ * gfs2_next_unreserved_block - Return next block that is not reserved
+ * @rgd: The resource group
+ * @block: The starting block
+ * @length: The required length
+ * @ip: Ignore any reservations for this inode
+ *
+ * If the block does not appear in any reservation, then return the
+ * block number unchanged. If it does appear in the reservation, then
+ * keep looking through the tree of reservations in order to find the
+ * first block number which is not reserved.
+ */
 
-               if (test_bit(GBF_FULL, &bi->bi_flags))
-                       goto skip;
+static u64 gfs2_next_unreserved_block(struct gfs2_rgrpd *rgd, u64 block,
+                                     u32 length,
+                                     const struct gfs2_inode *ip)
+{
+       struct gfs2_blkreserv *rs;
+       struct rb_node *n;
+       int rc;
 
-               WARN_ON(!buffer_uptodate(bi->bi_bh));
-               if (bi->bi_clone)
-                       buffer = bi->bi_clone + bi->bi_offset;
+       spin_lock(&rgd->rd_rsspin);
+       n = rgd->rd_rstree.rb_node;
+       while (n) {
+               rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
+               rc = rs_cmp(block, length, rs);
+               if (rc < 0)
+                       n = n->rb_left;
+               else if (rc > 0)
+                       n = n->rb_right;
                else
-                       buffer = bi->bi_bh->b_data + bi->bi_offset;
-
-               /* We have to keep the reservations aligned on u64 boundaries
-                  otherwise we could get situations where a byte can't be
-                  used because it's after a reservation, but a free bit still
-                  is within the reservation's area. */
-               ptr = buffer + ALIGN(goal >> GFS2_BIT_SIZE, sizeof(u64));
-               end = (buffer + bi->bi_len);
-               while (ptr < end) {
-                       rsv_bytes = 0;
-                       if ((ptr + best_rs_bytes) <= end)
-                               search_bytes = best_rs_bytes;
-                       else
-                               search_bytes = end - ptr;
-                       BUG_ON(!search_bytes);
-                       nonzero = memchr_inv(ptr, 0, search_bytes);
-                       /* If the lot is all zeroes, reserve the whole size. If
-                          there's enough zeroes to satisfy the request, use
-                          what we can. If there's not enough, keep looking. */
-                       if (nonzero == NULL)
-                               rsv_bytes = search_bytes;
-                       else if ((nonzero - ptr) * GFS2_NBBY >=
-                                ip->i_res->rs_requested)
-                               rsv_bytes = (nonzero - ptr);
-
-                       if (rsv_bytes) {
-                               blk = ((ptr - buffer) * GFS2_NBBY);
-                               BUG_ON(blk >= bi->bi_len * GFS2_NBBY);
-                               rs = rs_insert(bi, ip, blk,
-                                              rsv_bytes * GFS2_NBBY);
-                               if (IS_ERR(rs))
-                                       return PTR_ERR(rs);
-                               if (rs)
-                                       return 0;
-                       }
-                       ptr += ALIGN(search_bytes, sizeof(u64));
+                       break;
+       }
+
+       if (n) {
+               while ((rs_cmp(block, length, rs) == 0) && (ip->i_res != rs)) {
+                       block = gfs2_rbm_to_block(&rs->rs_rbm) + rs->rs_free;
+                       n = n->rb_right;
+                       if (n == NULL)
+                               break;
+                       rs = rb_entry(n, struct gfs2_blkreserv, rs_node);
                }
-skip:
-               /* Try next bitmap block (wrap back to rgrp header
-                  if at end) */
-               buf++;
-               buf %= length;
-               goal = 0;
        }
 
-       return BFITNOENT;
+       spin_unlock(&rgd->rd_rsspin);
+       return block;
 }
 
 /**
- * try_rgrp_fit - See if a given reservation will fit in a given RG
- * @rgd: the RG data
- * @ip: the inode
+ * gfs2_reservation_check_and_update - Check for reservations during block alloc
+ * @rbm: The current position in the resource group
+ * @ip: The inode for which we are searching for blocks
+ * @minext: The minimum extent length
  *
- * If there's room for the requested blocks to be allocated from the RG:
- * This will try to get a multi-block reservation first, and if that doesn't
- * fit, it will take what it can.
+ * This checks the current position in the rgrp to see whether there is
+ * a reservation covering this block. If not then this function is a
+ * no-op. If there is, then the position is moved to the end of the
+ * contiguous reservation(s) so that we are pointing at the first
+ * non-reserved block.
  *
- * Returns: 1 on success (it fits), 0 on failure (it doesn't fit)
+ * Returns: 0 if no reservation, 1 if @rbm has changed, otherwise an error
  */
 
-static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
+static int gfs2_reservation_check_and_update(struct gfs2_rbm *rbm,
+                                            const struct gfs2_inode *ip,
+                                            u32 minext)
 {
-       struct gfs2_blkreserv *rs = ip->i_res;
+       u64 block = gfs2_rbm_to_block(rbm);
+       u32 extlen = 1;
+       u64 nblock;
+       int ret;
 
-       if (rgd->rd_flags & (GFS2_RGF_NOALLOC | GFS2_RDF_ERROR))
+       /*
+        * If we have a minimum extent length, then skip over any extent
+        * which is less than the min extent length in size.
+        */
+       if (minext) {
+               extlen = gfs2_free_extlen(rbm, minext);
+               nblock = block + extlen;
+               if (extlen < minext)
+                       goto fail;
+       }
+
+       /*
+        * Check the extent which has been found against the reservations
+        * and skip if parts of it are already reserved
+        */
+       nblock = gfs2_next_unreserved_block(rbm->rgd, block, extlen, ip);
+       if (nblock == block)
                return 0;
-       /* Look for a multi-block reservation. */
-       if (unclaimed_blocks(rgd) >= RGRP_RSRV_MINBLKS &&
-           rg_mblk_search(rgd, ip) != BFITNOENT)
-               return 1;
-       if (unclaimed_blocks(rgd) >= rs->rs_requested)
-               return 1;
+fail:
+       ret = gfs2_rbm_from_block(rbm, nblock);
+       if (ret < 0)
+               return ret;
+       return 1;
+}
 
-       return 0;
+/**
+ * gfs2_rbm_find - Look for blocks of a particular state
+ * @rbm: Value/result starting position and final position
+ * @state: The state which we want to find
+ * @minext: The requested extent length (0 for a single block)
+ * @ip: If set, check for reservations
+ * @nowrap: Stop looking at the end of the rgrp, rather than wrapping
+ *          around until we've reached the starting point.
+ *
+ * Side effects:
+ * - If looking for free blocks, we set GBF_FULL on each bitmap which
+ *   has no free blocks in it.
+ *
+ * Returns: 0 on success, -ENOSPC if there is no block of the requested state
+ */
+
+static int gfs2_rbm_find(struct gfs2_rbm *rbm, u8 state, u32 minext,
+                        const struct gfs2_inode *ip, bool nowrap)
+{
+       struct buffer_head *bh;
+       struct gfs2_bitmap *initial_bi;
+       u32 initial_offset;
+       u32 offset;
+       u8 *buffer;
+       int index;
+       int n = 0;
+       int iters = rbm->rgd->rd_length;
+       int ret;
+
+       /* If we are not starting at the beginning of a bitmap, then we
+        * need to add one to the bitmap count to ensure that we search
+        * the starting bitmap twice.
+        */
+       if (rbm->offset != 0)
+               iters++;
+
+       while(1) {
+               if (test_bit(GBF_FULL, &rbm->bi->bi_flags) &&
+                   (state == GFS2_BLKST_FREE))
+                       goto next_bitmap;
+
+               bh = rbm->bi->bi_bh;
+               buffer = bh->b_data + rbm->bi->bi_offset;
+               WARN_ON(!buffer_uptodate(bh));
+               if (state != GFS2_BLKST_UNLINKED && rbm->bi->bi_clone)
+                       buffer = rbm->bi->bi_clone + rbm->bi->bi_offset;
+               initial_offset = rbm->offset;
+               offset = gfs2_bitfit(buffer, rbm->bi->bi_len, rbm->offset, state);
+               if (offset == BFITNOENT)
+                       goto bitmap_full;
+               rbm->offset = offset;
+               if (ip == NULL)
+                       return 0;
+
+               initial_bi = rbm->bi;
+               ret = gfs2_reservation_check_and_update(rbm, ip, minext);
+               if (ret == 0)
+                       return 0;
+               if (ret > 0) {
+                       n += (rbm->bi - initial_bi);
+                       goto next_iter;
+               }
+               if (ret == -E2BIG) {
+                       index = 0;
+                       rbm->offset = 0;
+                       n += (rbm->bi - initial_bi);
+                       goto res_covered_end_of_rgrp;
+               }
+               return ret;
+
+bitmap_full:   /* Mark bitmap as full and fall through */
+               if ((state == GFS2_BLKST_FREE) && initial_offset == 0)
+                       set_bit(GBF_FULL, &rbm->bi->bi_flags);
+
+next_bitmap:   /* Find next bitmap in the rgrp */
+               rbm->offset = 0;
+               index = rbm->bi - rbm->rgd->rd_bits;
+               index++;
+               if (index == rbm->rgd->rd_length)
+                       index = 0;
+res_covered_end_of_rgrp:
+               rbm->bi = &rbm->rgd->rd_bits[index];
+               if ((index == 0) && nowrap)
+                       break;
+               n++;
+next_iter:
+               if (n >= iters)
+                       break;
+       }
+
+       return -ENOSPC;
 }
 
 /**
@@ -1489,34 +1616,33 @@ static int try_rgrp_fit(struct gfs2_rgrpd *rgd, struct gfs2_inode *ip)
 
 static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip)
 {
-       u32 goal = 0, block;
-       u64 no_addr;
+       u64 block;
        struct gfs2_sbd *sdp = rgd->rd_sbd;
        struct gfs2_glock *gl;
        struct gfs2_inode *ip;
        int error;
        int found = 0;
-       struct gfs2_bitmap *bi;
+       struct gfs2_rbm rbm = { .rgd = rgd, .bi = rgd->rd_bits, .offset = 0 };
 
-       while (goal < rgd->rd_data) {
+       while (1) {
                down_write(&sdp->sd_log_flush_lock);
-               block = rgblk_search(rgd, goal, GFS2_BLKST_UNLINKED, &bi);
+               error = gfs2_rbm_find(&rbm, GFS2_BLKST_UNLINKED, 0, NULL, true);
                up_write(&sdp->sd_log_flush_lock);
-               if (block == BFITNOENT)
+               if (error == -ENOSPC)
+                       break;
+               if (WARN_ON_ONCE(error))
                        break;
 
-               block = gfs2_bi2rgd_blk(bi, block);
-               /* rgblk_search can return a block < goal, so we need to
-                  keep it marching forward. */
-               no_addr = block + rgd->rd_data0;
-               goal = max(block + 1, goal + 1);
-               if (*last_unlinked != NO_BLOCK && no_addr <= *last_unlinked)
+               block = gfs2_rbm_to_block(&rbm);
+               if (gfs2_rbm_from_block(&rbm, block + 1))
+                       break;
+               if (*last_unlinked != NO_BLOCK && block <= *last_unlinked)
                        continue;
-               if (no_addr == skip)
+               if (block == skip)
                        continue;
-               *last_unlinked = no_addr;
+               *last_unlinked = block;
 
-               error = gfs2_glock_get(sdp, no_addr, &gfs2_inode_glops, CREATE, &gl);
+               error = gfs2_glock_get(sdp, block, &gfs2_inode_glops, CREATE, &gl);
                if (error)
                        continue;
 
@@ -1543,6 +1669,19 @@ static void try_rgrp_unlink(struct gfs2_rgrpd *rgd, u64 *last_unlinked, u64 skip
        return;
 }
 
+static bool gfs2_select_rgrp(struct gfs2_rgrpd **pos, const struct gfs2_rgrpd *begin)
+{
+       struct gfs2_rgrpd *rgd = *pos;
+
+       rgd = gfs2_rgrpd_get_next(rgd);
+       if (rgd == NULL)
+               rgd = gfs2_rgrpd_get_next(NULL);
+       *pos = rgd;
+       if (rgd != begin) /* If we didn't wrap */
+               return true;
+       return false;
+}
+
 /**
  * gfs2_inplace_reserve - Reserve space in the filesystem
  * @ip: the inode to reserve space for
@@ -1562,103 +1701,96 @@ int gfs2_inplace_reserve(struct gfs2_inode *ip, u32 requested)
 
        if (sdp->sd_args.ar_rgrplvb)
                flags |= GL_SKIP;
-       rs->rs_requested = requested;
-       if (gfs2_assert_warn(sdp, requested)) {
-               error = -EINVAL;
-               goto out;
-       }
+       if (gfs2_assert_warn(sdp, requested))
+               return -EINVAL;
        if (gfs2_rs_active(rs)) {
-               begin = rs->rs_rgd;
+               begin = rs->rs_rbm.rgd;
                flags = 0; /* Yoda: Do or do not. There is no try */
        } else if (ip->i_rgd && rgrp_contains_block(ip->i_rgd, ip->i_goal)) {
-               rs->rs_rgd = begin = ip->i_rgd;
+               rs->rs_rbm.rgd = begin = ip->i_rgd;
        } else {
-               rs->rs_rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
+               rs->rs_rbm.rgd = begin = gfs2_blk2rgrpd(sdp, ip->i_goal, 1);
        }
-       if (rs->rs_rgd == NULL)
+       if (rs->rs_rbm.rgd == NULL)
                return -EBADSLT;
 
        while (loops < 3) {
-               rg_locked = 0;
-
-               if (gfs2_glock_is_locked_by_me(rs->rs_rgd->rd_gl)) {
-                       rg_locked = 1;
-                       error = 0;
-               } else if (!loops && !gfs2_rs_active(rs) &&
-                          rs->rs_rgd->rd_rs_cnt > RGRP_RSRV_MAX_CONTENDERS) {
-                       /* If the rgrp already is maxed out for contenders,
-                          we can eliminate it as a "first pass" without even
-                          requesting the rgrp glock. */
-                       error = GLR_TRYFAILED;
-               } else {
-                       error = gfs2_glock_nq_init(rs->rs_rgd->rd_gl,
+               rg_locked = 1;
+
+               if (!gfs2_glock_is_locked_by_me(rs->rs_rbm.rgd->rd_gl)) {
+                       rg_locked = 0;
+                       error = gfs2_glock_nq_init(rs->rs_rbm.rgd->rd_gl,
                                                   LM_ST_EXCLUSIVE, flags,
                                                   &rs->rs_rgd_gh);
-                       if (!error && sdp->sd_args.ar_rgrplvb) {
-                               error = update_rgrp_lvb(rs->rs_rgd);
-                               if (error) {
+                       if (error == GLR_TRYFAILED)
+                               goto next_rgrp;
+                       if (unlikely(error))
+                               return error;
+                       if (sdp->sd_args.ar_rgrplvb) {
+                               error = update_rgrp_lvb(rs->rs_rbm.rgd);
+                               if (unlikely(error)) {
                                        gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
                                        return error;
                                }
                        }
                }
-               switch (error) {
-               case 0:
-                       if (gfs2_rs_active(rs)) {
-                               if (unclaimed_blocks(rs->rs_rgd) +
-                                   rs->rs_free >= rs->rs_requested) {
-                                       ip->i_rgd = rs->rs_rgd;
-                                       return 0;
-                               }
-                               /* We have a multi-block reservation, but the
-                                  rgrp doesn't have enough free blocks to
-                                  satisfy the request. Free the reservation
-                                  and look for a suitable rgrp. */
-                               gfs2_rs_deltree(rs);
-                       }
-                       if (try_rgrp_fit(rs->rs_rgd, ip)) {
-                               if (sdp->sd_args.ar_rgrplvb)
-                                       gfs2_rgrp_bh_get(rs->rs_rgd);
-                               ip->i_rgd = rs->rs_rgd;
-                               return 0;
-                       }
-                       if (rs->rs_rgd->rd_flags & GFS2_RDF_CHECK) {
-                               if (sdp->sd_args.ar_rgrplvb)
-                                       gfs2_rgrp_bh_get(rs->rs_rgd);
-                               try_rgrp_unlink(rs->rs_rgd, &last_unlinked,
-                                               ip->i_no_addr);
-                       }
-                       if (!rg_locked)
-                               gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
-                       /* fall through */
-               case GLR_TRYFAILED:
-                       rs->rs_rgd = gfs2_rgrpd_get_next(rs->rs_rgd);
-                       rs->rs_rgd = rs->rs_rgd ? : begin; /* if NULL, wrap */
-                       if (rs->rs_rgd != begin) /* If we didn't wrap */
-                               break;
 
-                       flags &= ~LM_FLAG_TRY;
-                       loops++;
-                       /* Check that fs hasn't grown if writing to rindex */
-                       if (ip == GFS2_I(sdp->sd_rindex) &&
-                           !sdp->sd_rindex_uptodate) {
-                               error = gfs2_ri_update(ip);
-                               if (error)
-                                       goto out;
-                       } else if (loops == 2)
-                               /* Flushing the log may release space */
-                               gfs2_log_flush(sdp, NULL);
-                       break;
-               default:
-                       goto out;
+               /* Skip unuseable resource groups */
+               if (rs->rs_rbm.rgd->rd_flags & (GFS2_RGF_NOALLOC | GFS2_RDF_ERROR))
+                       goto skip_rgrp;
+
+               if (sdp->sd_args.ar_rgrplvb)
+                       gfs2_rgrp_bh_get(rs->rs_rbm.rgd);
+
+               /* Get a reservation if we don't already have one */
+               if (!gfs2_rs_active(rs))
+                       rg_mblk_search(rs->rs_rbm.rgd, ip, requested);
+
+               /* Skip rgrps when we can't get a reservation on first pass */
+               if (!gfs2_rs_active(rs) && (loops < 1))
+                       goto check_rgrp;
+
+               /* If rgrp has enough free space, use it */
+               if (rs->rs_rbm.rgd->rd_free_clone >= requested) {
+                       ip->i_rgd = rs->rs_rbm.rgd;
+                       return 0;
+               }
+
+               /* Drop reservation, if we couldn't use reserved rgrp */
+               if (gfs2_rs_active(rs))
+                       gfs2_rs_deltree(ip, rs);
+check_rgrp:
+               /* Check for unlinked inodes which can be reclaimed */
+               if (rs->rs_rbm.rgd->rd_flags & GFS2_RDF_CHECK)
+                       try_rgrp_unlink(rs->rs_rbm.rgd, &last_unlinked,
+                                       ip->i_no_addr);
+skip_rgrp:
+               /* Unlock rgrp if required */
+               if (!rg_locked)
+                       gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
+next_rgrp:
+               /* Find the next rgrp, and continue looking */
+               if (gfs2_select_rgrp(&rs->rs_rbm.rgd, begin))
+                       continue;
+
+               /* If we've scanned all the rgrps, but found no free blocks
+                * then this checks for some less likely conditions before
+                * trying again.
+                */
+               flags &= ~LM_FLAG_TRY;
+               loops++;
+               /* Check that fs hasn't grown if writing to rindex */
+               if (ip == GFS2_I(sdp->sd_rindex) && !sdp->sd_rindex_uptodate) {
+                       error = gfs2_ri_update(ip);
+                       if (error)
+                               return error;
                }
+               /* Flushing the log may release space */
+               if (loops == 2)
+                       gfs2_log_flush(sdp, NULL);
        }
-       error = -ENOSPC;
 
-out:
-       if (error)
-               rs->rs_requested = 0;
-       return error;
+       return -ENOSPC;
 }
 
 /**
@@ -1672,15 +1804,8 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
 {
        struct gfs2_blkreserv *rs = ip->i_res;
 
-       if (!rs)
-               return;
-
-       if (!rs->rs_free)
-               gfs2_rs_deltree(rs);
-
        if (rs->rs_rgd_gh.gh_gl)
                gfs2_glock_dq_uninit(&rs->rs_rgd_gh);
-       rs->rs_requested = 0;
 }
 
 /**
@@ -1693,173 +1818,47 @@ void gfs2_inplace_release(struct gfs2_inode *ip)
 
 static unsigned char gfs2_get_block_type(struct gfs2_rgrpd *rgd, u64 block)
 {
-       struct gfs2_bitmap *bi = NULL;
-       u32 length, rgrp_block, buf_block;
-       unsigned int buf;
-       unsigned char type;
-
-       length = rgd->rd_length;
-       rgrp_block = block - rgd->rd_data0;
-
-       for (buf = 0; buf < length; buf++) {
-               bi = rgd->rd_bits + buf;
-               if (rgrp_block < (bi->bi_start + bi->bi_len) * GFS2_NBBY)
-                       break;
-       }
-
-       gfs2_assert(rgd->rd_sbd, buf < length);
-       buf_block = rgrp_block - bi->bi_start * GFS2_NBBY;
+       struct gfs2_rbm rbm = { .rgd = rgd, };
+       int ret;
 
-       type = gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
-                          bi->bi_len, buf_block);
+       ret = gfs2_rbm_from_block(&rbm, block);
+       WARN_ON_ONCE(ret != 0);
 
-       return type;
+       return gfs2_testbit(&rbm);
 }
 
-/**
- * rgblk_search - find a block in @state
- * @rgd: the resource group descriptor
- * @goal: the goal block within the RG (start here to search for avail block)
- * @state: GFS2_BLKST_XXX the before-allocation state to find
- * @rbi: address of the pointer to the bitmap containing the block found
- *
- * Walk rgrp's bitmap to find bits that represent a block in @state.
- *
- * This function never fails, because we wouldn't call it unless we
- * know (from reservation results, etc.) that a block is available.
- *
- * Scope of @goal is just within rgrp, not the whole filesystem.
- * Scope of @returned block is just within bitmap, not the whole filesystem.
- *
- * Returns: the block number found relative to the bitmap rbi
- */
-
-static u32 rgblk_search(struct gfs2_rgrpd *rgd, u32 goal, unsigned char state,
-                       struct gfs2_bitmap **rbi)
-{
-       struct gfs2_bitmap *bi = NULL;
-       const u32 length = rgd->rd_length;
-       u32 biblk = BFITNOENT;
-       unsigned int buf, x;
-       const u8 *buffer = NULL;
-
-       *rbi = NULL;
-       /* Find bitmap block that contains bits for goal block */
-       for (buf = 0; buf < length; buf++) {
-               bi = rgd->rd_bits + buf;
-               /* Convert scope of "goal" from rgrp-wide to within found bit block */
-               if (goal < (bi->bi_start + bi->bi_len) * GFS2_NBBY) {
-                       goal -= bi->bi_start * GFS2_NBBY;
-                       goto do_search;
-               }
-       }
-       buf = 0;
-       goal = 0;
-
-do_search:
-       /* Search (up to entire) bitmap in this rgrp for allocatable block.
-          "x <= length", instead of "x < length", because we typically start
-          the search in the middle of a bit block, but if we can't find an
-          allocatable block anywhere else, we want to be able wrap around and
-          search in the first part of our first-searched bit block.  */
-       for (x = 0; x <= length; x++) {
-               bi = rgd->rd_bits + buf;
-
-               if (test_bit(GBF_FULL, &bi->bi_flags) &&
-                   (state == GFS2_BLKST_FREE))
-                       goto skip;
-
-               /* The GFS2_BLKST_UNLINKED state doesn't apply to the clone
-                  bitmaps, so we must search the originals for that. */
-               buffer = bi->bi_bh->b_data + bi->bi_offset;
-               WARN_ON(!buffer_uptodate(bi->bi_bh));
-               if (state != GFS2_BLKST_UNLINKED && bi->bi_clone)
-                       buffer = bi->bi_clone + bi->bi_offset;
-
-               while (1) {
-                       struct gfs2_blkreserv *rs;
-                       u32 rgblk;
-
-                       biblk = gfs2_bitfit(buffer, bi->bi_len, goal, state);
-                       if (biblk == BFITNOENT)
-                               break;
-                       /* Check if this block is reserved() */
-                       rgblk = gfs2_bi2rgd_blk(bi, biblk);
-                       rs = rs_find(rgd, rgblk);
-                       if (rs == NULL)
-                               break;
-
-                       BUG_ON(rs->rs_bi != bi);
-                       biblk = BFITNOENT;
-                       /* This should jump to the first block after the
-                          reservation. */
-                       goal = rs->rs_biblk + rs->rs_free;
-                       if (goal >= bi->bi_len * GFS2_NBBY)
-                               break;
-               }
-               if (biblk != BFITNOENT)
-                       break;
-
-               if ((goal == 0) && (state == GFS2_BLKST_FREE))
-                       set_bit(GBF_FULL, &bi->bi_flags);
-
-               /* Try next bitmap block (wrap back to rgrp header if at end) */
-skip:
-               buf++;
-               buf %= length;
-               goal = 0;
-       }
-
-       if (biblk != BFITNOENT)
-               *rbi = bi;
-
-       return biblk;
-}
 
 /**
  * gfs2_alloc_extent - allocate an extent from a given bitmap
- * @rgd: the resource group descriptor
- * @bi: the bitmap within the rgrp
- * @blk: the block within the bitmap
+ * @rbm: the resource group information
  * @dinode: TRUE if the first block we allocate is for a dinode
- * @n: The extent length
+ * @n: The extent length (value/result)
  *
- * Add the found bitmap buffer to the transaction.
+ * Add the bitmap buffer to the transaction.
  * Set the found bits to @new_state to change block's allocation state.
- * Returns: starting block number of the extent (fs scope)
  */
-static u64 gfs2_alloc_extent(struct gfs2_rgrpd *rgd, struct gfs2_bitmap *bi,
-                            u32 blk, bool dinode, unsigned int *n)
+static void gfs2_alloc_extent(const struct gfs2_rbm *rbm, bool dinode,
+                            unsigned int *n)
 {
+       struct gfs2_rbm pos = { .rgd = rbm->rgd, };
        const unsigned int elen = *n;
-       u32 goal, rgblk;
-       const u8 *buffer = NULL;
-       struct gfs2_blkreserv *rs;
-
-       *n = 0;
-       buffer = bi->bi_bh->b_data + bi->bi_offset;
-       gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-       gfs2_setbit(rgd, bi->bi_clone, bi, blk,
-                   dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
-       (*n)++;
-       goal = blk;
+       u64 block;
+       int ret;
+
+       *n = 1;
+       block = gfs2_rbm_to_block(rbm);
+       gfs2_trans_add_bh(rbm->rgd->rd_gl, rbm->bi->bi_bh, 1);
+       gfs2_setbit(rbm, true, dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
+       block++;
        while (*n < elen) {
-               goal++;
-               if (goal >= (bi->bi_len * GFS2_NBBY))
-                       break;
-               rgblk = gfs2_bi2rgd_blk(bi, goal);
-               rs = rs_find(rgd, rgblk);
-               if (rs) /* Oops, we bumped into someone's reservation */
-                       break;
-               if (gfs2_testbit(rgd, buffer, bi->bi_len, goal) !=
-                   GFS2_BLKST_FREE)
+               ret = gfs2_rbm_from_block(&pos, block);
+               if (ret || gfs2_testbit(&pos) != GFS2_BLKST_FREE)
                        break;
-               gfs2_setbit(rgd, bi->bi_clone, bi, goal, GFS2_BLKST_USED);
+               gfs2_trans_add_bh(pos.rgd->rd_gl, pos.bi->bi_bh, 1);
+               gfs2_setbit(&pos, true, GFS2_BLKST_USED);
                (*n)++;
+               block++;
        }
-       blk = gfs2_bi2rgd_blk(bi, blk);
-       rgd->rd_last_alloc = blk + *n - 1;
-       return rgd->rd_data0 + blk;
 }
 
 /**
@@ -1875,46 +1874,30 @@ static u64 gfs2_alloc_extent(struct gfs2_rgrpd *rgd, struct gfs2_bitmap *bi,
 static struct gfs2_rgrpd *rgblk_free(struct gfs2_sbd *sdp, u64 bstart,
                                     u32 blen, unsigned char new_state)
 {
-       struct gfs2_rgrpd *rgd;
-       struct gfs2_bitmap *bi = NULL;
-       u32 length, rgrp_blk, buf_blk;
-       unsigned int buf;
+       struct gfs2_rbm rbm;
 
-       rgd = gfs2_blk2rgrpd(sdp, bstart, 1);
-       if (!rgd) {
+       rbm.rgd = gfs2_blk2rgrpd(sdp, bstart, 1);
+       if (!rbm.rgd) {
                if (gfs2_consist(sdp))
                        fs_err(sdp, "block = %llu\n", (unsigned long long)bstart);
                return NULL;
        }
 
-       length = rgd->rd_length;
-
-       rgrp_blk = bstart - rgd->rd_data0;
-
        while (blen--) {
-               for (buf = 0; buf < length; buf++) {
-                       bi = rgd->rd_bits + buf;
-                       if (rgrp_blk < (bi->bi_start + bi->bi_len) * GFS2_NBBY)
-                               break;
+               gfs2_rbm_from_block(&rbm, bstart);
+               bstart++;
+               if (!rbm.bi->bi_clone) {
+                       rbm.bi->bi_clone = kmalloc(rbm.bi->bi_bh->b_size,
+                                                  GFP_NOFS | __GFP_NOFAIL);
+                       memcpy(rbm.bi->bi_clone + rbm.bi->bi_offset,
+                              rbm.bi->bi_bh->b_data + rbm.bi->bi_offset,
+                              rbm.bi->bi_len);
                }
-
-               gfs2_assert(rgd->rd_sbd, buf < length);
-
-               buf_blk = rgrp_blk - bi->bi_start * GFS2_NBBY;
-               rgrp_blk++;
-
-               if (!bi->bi_clone) {
-                       bi->bi_clone = kmalloc(bi->bi_bh->b_size,
-                                              GFP_NOFS | __GFP_NOFAIL);
-                       memcpy(bi->bi_clone + bi->bi_offset,
-                              bi->bi_bh->b_data + bi->bi_offset,
-                              bi->bi_len);
-               }
-               gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-               gfs2_setbit(rgd, NULL, bi, buf_blk, new_state);
+               gfs2_trans_add_bh(rbm.rgd->rd_gl, rbm.bi->bi_bh, 1);
+               gfs2_setbit(&rbm, false, new_state);
        }
 
-       return rgd;
+       return rbm.rgd;
 }
 
 /**
@@ -1956,46 +1939,41 @@ static void gfs2_rgrp_error(struct gfs2_rgrpd *rgd)
 }
 
 /**
- * claim_reserved_blks - Claim previously reserved blocks
- * @ip: the inode that's claiming the reservation
- * @dinode: 1 if this block is a dinode block, otherwise data block
- * @nblocks: desired extent length
+ * gfs2_adjust_reservation - Adjust (or remove) a reservation after allocation
+ * @ip: The inode we have just allocated blocks for
+ * @rbm: The start of the allocated blocks
+ * @len: The extent length
  *
- * Lay claim to previously reserved blocks.
- * Returns: Starting block number of the blocks claimed.
- * Sets *nblocks to the actual extent length allocated.
+ * Adjusts a reservation after an allocation has taken place. If the
+ * reservation does not match the allocation, or if it is now empty
+ * then it is removed.
  */
-static u64 claim_reserved_blks(struct gfs2_inode *ip, bool dinode,
-                              unsigned int *nblocks)
+
+static void gfs2_adjust_reservation(struct gfs2_inode *ip,
+                                   const struct gfs2_rbm *rbm, unsigned len)
 {
        struct gfs2_blkreserv *rs = ip->i_res;
-       struct gfs2_rgrpd *rgd = rs->rs_rgd;
-       struct gfs2_bitmap *bi;
-       u64 start_block = gfs2_rs_startblk(rs);
-       const unsigned int elen = *nblocks;
+       struct gfs2_rgrpd *rgd = rbm->rgd;
+       unsigned rlen;
+       u64 block;
+       int ret;
 
-       bi = rs->rs_bi;
-       gfs2_trans_add_bh(rgd->rd_gl, bi->bi_bh, 1);
-
-       for (*nblocks = 0; *nblocks < elen && rs->rs_free; (*nblocks)++) {
-               if (gfs2_testbit(rgd, bi->bi_bh->b_data + bi->bi_offset,
-                                bi->bi_len, rs->rs_biblk) != GFS2_BLKST_FREE)
-                       break;
-               gfs2_setbit(rgd, bi->bi_clone, bi, rs->rs_biblk,
-                           dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
-               rs->rs_biblk++;
-               rs->rs_free--;
-
-               BUG_ON(!rgd->rd_reserved);
-               rgd->rd_reserved--;
-               dinode = false;
+       spin_lock(&rgd->rd_rsspin);
+       if (gfs2_rs_active(rs)) {
+               if (gfs2_rbm_eq(&rs->rs_rbm, rbm)) {
+                       block = gfs2_rbm_to_block(rbm);
+                       ret = gfs2_rbm_from_block(&rs->rs_rbm, block + len);
+                       rlen = min(rs->rs_free, len);
+                       rs->rs_free -= rlen;
+                       rgd->rd_reserved -= rlen;
+                       trace_gfs2_rs(rs, TRACE_RS_CLAIM);
+                       if (rs->rs_free && !ret)
+                               goto out;
+               }
+               __rs_deltree(ip, rs);
        }
-
-       trace_gfs2_rs(ip, rs, TRACE_RS_CLAIM);
-       if (!rs->rs_free || *nblocks != elen)
-               gfs2_rs_deltree(rs);
-
-       return start_block;
+out:
+       spin_unlock(&rgd->rd_rsspin);
 }
 
 /**
@@ -2014,47 +1992,40 @@ int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *nblocks,
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct buffer_head *dibh;
-       struct gfs2_rgrpd *rgd;
+       struct gfs2_rbm rbm = { .rgd = ip->i_rgd, };
        unsigned int ndata;
-       u32 goal, blk; /* block, within the rgrp scope */
+       u64 goal;
        u64 block; /* block, within the file system scope */
        int error;
-       struct gfs2_bitmap *bi;
 
-       /* Only happens if there is a bug in gfs2, return something distinctive
-        * to ensure that it is noticed.
-        */
-       if (ip->i_res->rs_requested == 0)
-               return -ECANCELED;
-
-       /* If we have a reservation, claim blocks from it. */
-       if (gfs2_rs_active(ip->i_res)) {
-               BUG_ON(!ip->i_res->rs_free);
-               rgd = ip->i_res->rs_rgd;
-               block = claim_reserved_blks(ip, dinode, nblocks);
-               if (*nblocks)
-                       goto found_blocks;
-       }
-
-       rgd = ip->i_rgd;
-
-       if (!dinode && rgrp_contains_block(rgd, ip->i_goal))
-               goal = ip->i_goal - rgd->rd_data0;
+       if (gfs2_rs_active(ip->i_res))
+               goal = gfs2_rbm_to_block(&ip->i_res->rs_rbm);
+       else if (!dinode && rgrp_contains_block(rbm.rgd, ip->i_goal))
+               goal = ip->i_goal;
        else
-               goal = rgd->rd_last_alloc;
+               goal = rbm.rgd->rd_last_alloc + rbm.rgd->rd_data0;
 
-       blk = rgblk_search(rgd, goal, GFS2_BLKST_FREE, &bi);
+       gfs2_rbm_from_block(&rbm, goal);
+       error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, 0, ip, false);
+
+       if (error == -ENOSPC) {
+               gfs2_rbm_from_block(&rbm, goal);
+               error = gfs2_rbm_find(&rbm, GFS2_BLKST_FREE, 0, NULL, false);
+       }
 
        /* Since all blocks are reserved in advance, this shouldn't happen */
-       if (blk == BFITNOENT) {
-               printk(KERN_WARNING "BFITNOENT, nblocks=%u\n", *nblocks);
-               printk(KERN_WARNING "FULL=%d\n",
-                      test_bit(GBF_FULL, &rgd->rd_bits->bi_flags));
+       if (error) {
+               fs_warn(sdp, "inum=%llu error=%d, nblocks=%u, full=%d\n",
+                       (unsigned long long)ip->i_no_addr, error, *nblocks,
+                       test_bit(GBF_FULL, &rbm.rgd->rd_bits->bi_flags));
                goto rgrp_error;
        }
 
-       block = gfs2_alloc_extent(rgd, bi, blk, dinode, nblocks);
-found_blocks:
+       gfs2_alloc_extent(&rbm, dinode, nblocks);
+       block = gfs2_rbm_to_block(&rbm);
+       rbm.rgd->rd_last_alloc = block - rbm.rgd->rd_data0;
+       if (gfs2_rs_active(ip->i_res))
+               gfs2_adjust_reservation(ip, &rbm, *nblocks);
        ndata = *nblocks;
        if (dinode)
                ndata--;
@@ -2071,22 +2042,22 @@ found_blocks:
                        brelse(dibh);
                }
        }
-       if (rgd->rd_free < *nblocks) {
+       if (rbm.rgd->rd_free < *nblocks) {
                printk(KERN_WARNING "nblocks=%u\n", *nblocks);
                goto rgrp_error;
        }
 
-       rgd->rd_free -= *nblocks;
+       rbm.rgd->rd_free -= *nblocks;
        if (dinode) {
-               rgd->rd_dinodes++;
-               *generation = rgd->rd_igeneration++;
+               rbm.rgd->rd_dinodes++;
+               *generation = rbm.rgd->rd_igeneration++;
                if (*generation == 0)
-                       *generation = rgd->rd_igeneration++;
+                       *generation = rbm.rgd->rd_igeneration++;
        }
 
-       gfs2_trans_add_bh(rgd->rd_gl, rgd->rd_bits[0].bi_bh, 1);
-       gfs2_rgrp_out(rgd, rgd->rd_bits[0].bi_bh->b_data);
-       gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, rgd->rd_bits[0].bi_bh->b_data);
+       gfs2_trans_add_bh(rbm.rgd->rd_gl, rbm.rgd->rd_bits[0].bi_bh, 1);
+       gfs2_rgrp_out(rbm.rgd, rbm.rgd->rd_bits[0].bi_bh->b_data);
+       gfs2_rgrp_ondisk2lvb(rbm.rgd->rd_rgl, rbm.rgd->rd_bits[0].bi_bh->b_data);
 
        gfs2_statfs_change(sdp, 0, -(s64)*nblocks, dinode ? 1 : 0);
        if (dinode)
@@ -2100,14 +2071,14 @@ found_blocks:
                gfs2_quota_change(ip, ndata, ip->i_inode.i_uid,
                                  ip->i_inode.i_gid);
 
-       rgd->rd_free_clone -= *nblocks;
-       trace_gfs2_block_alloc(ip, rgd, block, *nblocks,
+       rbm.rgd->rd_free_clone -= *nblocks;
+       trace_gfs2_block_alloc(ip, rbm.rgd, block, *nblocks,
                               dinode ? GFS2_BLKST_DINODE : GFS2_BLKST_USED);
        *bn = block;
        return 0;
 
 rgrp_error:
-       gfs2_rgrp_error(rgd);
+       gfs2_rgrp_error(rbm.rgd);
        return -EIO;
 }
 
index ca6e267..2407795 100644 (file)
@@ -46,7 +46,7 @@ extern int gfs2_alloc_blocks(struct gfs2_inode *ip, u64 *bn, unsigned int *n,
                             bool dinode, u64 *generation);
 
 extern int gfs2_rs_alloc(struct gfs2_inode *ip);
-extern void gfs2_rs_deltree(struct gfs2_blkreserv *rs);
+extern void gfs2_rs_deltree(struct gfs2_inode *ip, struct gfs2_blkreserv *rs);
 extern void gfs2_rs_delete(struct gfs2_inode *ip);
 extern void __gfs2_free_blocks(struct gfs2_inode *ip, u64 bstart, u32 blen, int meta);
 extern void gfs2_free_meta(struct gfs2_inode *ip, u64 bstart, u32 blen);
@@ -73,30 +73,10 @@ extern int gfs2_rgrp_send_discards(struct gfs2_sbd *sdp, u64 offset,
                                   const struct gfs2_bitmap *bi, unsigned minlen, u64 *ptrimmed);
 extern int gfs2_fitrim(struct file *filp, void __user *argp);
 
-/* This is how to tell if a multi-block reservation is "inplace" reserved: */
-static inline int gfs2_mb_reserved(struct gfs2_inode *ip)
+/* This is how to tell if a reservation is in the rgrp tree: */
+static inline bool gfs2_rs_active(struct gfs2_blkreserv *rs)
 {
-       if (ip->i_res && ip->i_res->rs_requested)
-               return 1;
-       return 0;
-}
-
-/* This is how to tell if a multi-block reservation is in the rgrp tree: */
-static inline int gfs2_rs_active(struct gfs2_blkreserv *rs)
-{
-       if (rs && rs->rs_bi)
-               return 1;
-       return 0;
-}
-
-static inline u32 gfs2_bi2rgd_blk(const struct gfs2_bitmap *bi, u32 blk)
-{
-       return (bi->bi_start * GFS2_NBBY) + blk;
-}
-
-static inline u64 gfs2_rs_startblk(const struct gfs2_blkreserv *rs)
-{
-       return gfs2_bi2rgd_blk(rs->rs_bi, rs->rs_biblk) + rs->rs_rgd->rd_data0;
+       return rs && !RB_EMPTY_NODE(&rs->rs_node);
 }
 
 #endif /* __RGRP_DOT_H__ */
index fc3168f..a8d90f2 100644 (file)
@@ -1366,6 +1366,8 @@ static int gfs2_show_options(struct seq_file *s, struct dentry *root)
        val = sdp->sd_tune.gt_statfs_quantum;
        if (val != 30)
                seq_printf(s, ",statfs_quantum=%d", val);
+       else if (sdp->sd_tune.gt_statfs_slow)
+               seq_puts(s, ",statfs_quantum=0");
        val = sdp->sd_tune.gt_quota_quantum;
        if (val != 60)
                seq_printf(s, ",quota_quantum=%d", val);
@@ -1543,6 +1545,11 @@ static void gfs2_evict_inode(struct inode *inode)
 
 out_truncate:
        gfs2_log_flush(sdp, ip->i_gl);
+       if (test_bit(GLF_DIRTY, &ip->i_gl->gl_flags)) {
+               struct address_space *metamapping = gfs2_glock2aspace(ip->i_gl);
+               filemap_fdatawrite(metamapping);
+               filemap_fdatawait(metamapping);
+       }
        write_inode_now(inode, 1);
        gfs2_ail_flush(ip->i_gl, 0);
 
@@ -1557,7 +1564,7 @@ out_truncate:
 out_unlock:
        /* Error path for case 1 */
        if (gfs2_rs_active(ip->i_res))
-               gfs2_rs_deltree(ip->i_res);
+               gfs2_rs_deltree(ip, ip->i_res);
 
        if (test_bit(HIF_HOLDER, &ip->i_iopen_gh.gh_iflags))
                gfs2_glock_dq(&ip->i_iopen_gh);
index a25c252..bbdc78a 100644 (file)
@@ -509,10 +509,9 @@ TRACE_EVENT(gfs2_block_alloc,
 /* Keep track of multi-block reservations as they are allocated/freed */
 TRACE_EVENT(gfs2_rs,
 
-       TP_PROTO(const struct gfs2_inode *ip, const struct gfs2_blkreserv *rs,
-                u8 func),
+       TP_PROTO(const struct gfs2_blkreserv *rs, u8 func),
 
-       TP_ARGS(ip, rs, func),
+       TP_ARGS(rs, func),
 
        TP_STRUCT__entry(
                __field(        dev_t,  dev                     )
@@ -526,18 +525,17 @@ TRACE_EVENT(gfs2_rs,
        ),
 
        TP_fast_assign(
-               __entry->dev            = rs->rs_rgd ? rs->rs_rgd->rd_sbd->sd_vfs->s_dev : 0;
-               __entry->rd_addr        = rs->rs_rgd ? rs->rs_rgd->rd_addr : 0;
-               __entry->rd_free_clone  = rs->rs_rgd ? rs->rs_rgd->rd_free_clone : 0;
-               __entry->rd_reserved    = rs->rs_rgd ? rs->rs_rgd->rd_reserved : 0;
-               __entry->inum           = ip ? ip->i_no_addr : 0;
-               __entry->start          = gfs2_rs_startblk(rs);
+               __entry->dev            = rs->rs_rbm.rgd->rd_sbd->sd_vfs->s_dev;
+               __entry->rd_addr        = rs->rs_rbm.rgd->rd_addr;
+               __entry->rd_free_clone  = rs->rs_rbm.rgd->rd_free_clone;
+               __entry->rd_reserved    = rs->rs_rbm.rgd->rd_reserved;
+               __entry->inum           = rs->rs_inum;
+               __entry->start          = gfs2_rbm_to_block(&rs->rs_rbm);
                __entry->free           = rs->rs_free;
                __entry->func           = func;
        ),
 
-       TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%lu rr:%lu %s "
-                 "f:%lu",
+       TP_printk("%u,%u bmap %llu resrv %llu rg:%llu rf:%lu rr:%lu %s f:%lu",
                  MAJOR(__entry->dev), MINOR(__entry->dev),
                  (unsigned long long)__entry->inum,
                  (unsigned long long)__entry->start,
index 41f42cd..bf2ae9a 100644 (file)
@@ -28,11 +28,10 @@ struct gfs2_glock;
 
 /* reserve either the number of blocks to be allocated plus the rg header
  * block, or all of the blocks in the rg, whichever is smaller */
-static inline unsigned int gfs2_rg_blocks(const struct gfs2_inode *ip)
+static inline unsigned int gfs2_rg_blocks(const struct gfs2_inode *ip, unsigned requested)
 {
-       const struct gfs2_blkreserv *rs = ip->i_res;
-       if (rs && rs->rs_requested < ip->i_rgd->rd_length)
-               return rs->rs_requested + 1;
+       if (requested < ip->i_rgd->rd_length)
+               return requested + 1;
        return ip->i_rgd->rd_length;
 }
 
index 27a0b4a..db330e5 100644 (file)
@@ -448,17 +448,18 @@ ssize_t gfs2_listxattr(struct dentry *dentry, char *buffer, size_t size)
 }
 
 /**
- * ea_get_unstuffed - actually copies the unstuffed data into the
- *                    request buffer
+ * ea_iter_unstuffed - copies the unstuffed xattr data to/from the
+ *                     request buffer
  * @ip: The GFS2 inode
  * @ea: The extended attribute header structure
- * @data: The data to be copied
+ * @din: The data to be copied in
+ * @dout: The data to be copied out (one of din,dout will be NULL)
  *
  * Returns: errno
  */
 
-static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
-                           char *data)
+static int gfs2_iter_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
+                              const char *din, char *dout)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
        struct buffer_head **bh;
@@ -467,6 +468,8 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
        __be64 *dataptrs = GFS2_EA2DATAPTRS(ea);
        unsigned int x;
        int error = 0;
+       unsigned char *pos;
+       unsigned cp_size;
 
        bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_NOFS);
        if (!bh)
@@ -497,12 +500,21 @@ static int ea_get_unstuffed(struct gfs2_inode *ip, struct gfs2_ea_header *ea,
                        goto out;
                }
 
-               memcpy(data, bh[x]->b_data + sizeof(struct gfs2_meta_header),
-                      (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
+               pos = bh[x]->b_data + sizeof(struct gfs2_meta_header);
+               cp_size = (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize;
 
-               amount -= sdp->sd_jbsize;
-               data += sdp->sd_jbsize;
+               if (dout) {
+                       memcpy(dout, pos, cp_size);
+                       dout += sdp->sd_jbsize;
+               }
+
+               if (din) {
+                       gfs2_trans_add_bh(ip->i_gl, bh[x], 1);
+                       memcpy(pos, din, cp_size);
+                       din += sdp->sd_jbsize;
+               }
 
+               amount -= sdp->sd_jbsize;
                brelse(bh[x]);
        }
 
@@ -523,7 +535,7 @@ static int gfs2_ea_get_copy(struct gfs2_inode *ip, struct gfs2_ea_location *el,
                memcpy(data, GFS2_EA2DATA(el->el_ea), len);
                return len;
        }
-       ret = ea_get_unstuffed(ip, el->el_ea, data);
+       ret = gfs2_iter_unstuffed(ip, el->el_ea, NULL, data);
        if (ret < 0)
                return ret;
        return len;
@@ -727,7 +739,7 @@ static int ea_alloc_skeleton(struct gfs2_inode *ip, struct gfs2_ea_request *er,
                goto out_gunlock_q;
 
        error = gfs2_trans_begin(GFS2_SB(&ip->i_inode),
-                                blks + gfs2_rg_blocks(ip) +
+                                blks + gfs2_rg_blocks(ip, blks) +
                                 RES_DINODE + RES_STATFS + RES_QUOTA, 0);
        if (error)
                goto out_ipres;
@@ -1220,69 +1232,23 @@ static int gfs2_xattr_set(struct dentry *dentry, const char *name,
                                size, flags, type);
 }
 
+
 static int ea_acl_chmod_unstuffed(struct gfs2_inode *ip,
                                  struct gfs2_ea_header *ea, char *data)
 {
        struct gfs2_sbd *sdp = GFS2_SB(&ip->i_inode);
-       struct buffer_head **bh;
        unsigned int amount = GFS2_EA_DATA_LEN(ea);
        unsigned int nptrs = DIV_ROUND_UP(amount, sdp->sd_jbsize);
-       __be64 *dataptrs = GFS2_EA2DATAPTRS(ea);
-       unsigned int x;
-       int error;
-
-       bh = kcalloc(nptrs, sizeof(struct buffer_head *), GFP_NOFS);
-       if (!bh)
-               return -ENOMEM;
-
-       error = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0);
-       if (error)
-               goto out;
-
-       for (x = 0; x < nptrs; x++) {
-               error = gfs2_meta_read(ip->i_gl, be64_to_cpu(*dataptrs), 0,
-                                      bh + x);
-               if (error) {
-                       while (x--)
-                               brelse(bh[x]);
-                       goto fail;
-               }
-               dataptrs++;
-       }
-
-       for (x = 0; x < nptrs; x++) {
-               error = gfs2_meta_wait(sdp, bh[x]);
-               if (error) {
-                       for (; x < nptrs; x++)
-                               brelse(bh[x]);
-                       goto fail;
-               }
-               if (gfs2_metatype_check(sdp, bh[x], GFS2_METATYPE_ED)) {
-                       for (; x < nptrs; x++)
-                               brelse(bh[x]);
-                       error = -EIO;
-                       goto fail;
-               }
-
-               gfs2_trans_add_bh(ip->i_gl, bh[x], 1);
-
-               memcpy(bh[x]->b_data + sizeof(struct gfs2_meta_header), data,
-                      (sdp->sd_jbsize > amount) ? amount : sdp->sd_jbsize);
-
-               amount -= sdp->sd_jbsize;
-               data += sdp->sd_jbsize;
-
-               brelse(bh[x]);
-       }
+       int ret;
 
-out:
-       kfree(bh);
-       return error;
+       ret = gfs2_trans_begin(sdp, nptrs + RES_DINODE, 0);
+       if (ret)
+               return ret;
 
-fail:
+       ret = gfs2_iter_unstuffed(ip, ea, data, NULL);
        gfs2_trans_end(sdp);
-       kfree(bh);
-       return error;
+
+       return ret;
 }
 
 int gfs2_xattr_acl_chmod(struct gfs2_inode *ip, struct iattr *attr, char *data)
index a74cb17..7cc37ca 100644 (file)
@@ -874,7 +874,7 @@ struct dentry *generic_fh_to_dentry(struct super_block *sb, struct fid *fid,
 EXPORT_SYMBOL_GPL(generic_fh_to_dentry);
 
 /**
- * generic_fh_to_dentry - generic helper for the fh_to_parent export operation
+ * generic_fh_to_parent - generic helper for the fh_to_parent export operation
  * @sb:                filesystem to do the file handle conversion on
  * @fid:       file handle to convert
  * @fh_len:    length of the file handle in bytes
index fb1a2be..8d80c99 100644 (file)
@@ -289,7 +289,6 @@ static void nlmsvc_free_block(struct kref *kref)
        dprintk("lockd: freeing block %p...\n", block);
 
        /* Remove block from file's list of blocks */
-       mutex_lock(&file->f_mutex);
        list_del_init(&block->b_flist);
        mutex_unlock(&file->f_mutex);
 
@@ -303,7 +302,7 @@ static void nlmsvc_free_block(struct kref *kref)
 static void nlmsvc_release_block(struct nlm_block *block)
 {
        if (block != NULL)
-               kref_put(&block->b_count, nlmsvc_free_block);
+               kref_put_mutex(&block->b_count, nlmsvc_free_block, &block->b_file->f_mutex);
 }
 
 /*
index 4d31f73..7bdf790 100644 (file)
@@ -1886,8 +1886,14 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
                return err;
 
        err = -EINVAL;
-       if (!(mnt_flags & MNT_SHRINKABLE) && !check_mnt(real_mount(path->mnt)))
-               goto unlock;
+       if (unlikely(!check_mnt(real_mount(path->mnt)))) {
+               /* that's acceptable only for automounts done in private ns */
+               if (!(mnt_flags & MNT_SHRINKABLE))
+                       goto unlock;
+               /* ... and for those we'd better have mountpoint still alive */
+               if (!real_mount(path->mnt)->mnt_ns)
+                       goto unlock;
+       }
 
        /* Refuse the same filesystem on the same mount point */
        err = -EBUSY;
index b8eda70..d2c7f5d 100644 (file)
@@ -1537,7 +1537,7 @@ static int nfs_parse_mount_options(char *raw,
 
        /*
         * verify that any proto=/mountproto= options match the address
-        * familiies in the addr=/mountaddr= options.
+        * families in the addr=/mountaddr= options.
         */
        if (protofamily != AF_UNSPEC &&
            protofamily != mnt->nfs_server.address.ss_family)
index 991ef01..3748ec9 100644 (file)
@@ -691,9 +691,11 @@ __SC_COMP(__NR_process_vm_readv, sys_process_vm_readv, \
 #define __NR_process_vm_writev 271
 __SC_COMP(__NR_process_vm_writev, sys_process_vm_writev, \
           compat_sys_process_vm_writev)
+#define __NR_kcmp 272
+__SYSCALL(__NR_kcmp, sys_kcmp)
 
 #undef __NR_syscalls
-#define __NR_syscalls 272
+#define __NR_syscalls 273
 
 /*
  * All syscalls below here should go away really,
index 42970de..7e1f37d 100644 (file)
@@ -414,7 +414,7 @@ struct hid_field {
        __u16 dpad;                     /* dpad input code */
 };
 
-#define HID_MAX_FIELDS 128
+#define HID_MAX_FIELDS 256
 
 struct hid_report {
        struct list_head list;
@@ -626,6 +626,7 @@ struct hid_usage_id {
  * @report_fixup: called before report descriptor parsing (NULL means nop)
  * @input_mapping: invoked on input registering before mapping an usage
  * @input_mapped: invoked on input registering after mapping an usage
+ * @input_configured: invoked just before the device is registered
  * @feature_mapping: invoked on feature registering
  * @suspend: invoked on suspend (NULL means nop)
  * @resume: invoked on resume if device was not reset (NULL means nop)
@@ -670,6 +671,8 @@ struct hid_driver {
        int (*input_mapped)(struct hid_device *hdev,
                        struct hid_input *hidinput, struct hid_field *field,
                        struct hid_usage *usage, unsigned long **bit, int *max);
+       void (*input_configured)(struct hid_device *hdev,
+                                struct hid_input *hidinput);
        void (*feature_mapping)(struct hid_device *hdev,
                        struct hid_field *field,
                        struct hid_usage *usage);
index 725dcd0..ba48743 100644 (file)
@@ -1169,6 +1169,18 @@ struct ff_effect {
 #include <linux/mod_devicetable.h>
 
 /**
+ * struct input_value - input value representation
+ * @type: type of value (EV_KEY, EV_ABS, etc)
+ * @code: the value code
+ * @value: the value
+ */
+struct input_value {
+       __u16 type;
+       __u16 code;
+       __s32 value;
+};
+
+/**
  * struct input_dev - represents an input device
  * @name: name of the device
  * @phys: physical path to the device in the system hierarchy
@@ -1203,11 +1215,7 @@ struct ff_effect {
  *     software autorepeat
  * @timer: timer for software autorepeat
  * @rep: current values for autorepeat parameters (delay, rate)
- * @mt: pointer to array of struct input_mt_slot holding current values
- *     of tracked contacts
- * @mtsize: number of MT slots the device uses
- * @slot: MT slot currently being transmitted
- * @trkid: stores MT tracking ID for the current contact
+ * @mt: pointer to multitouch state
  * @absinfo: array of &struct input_absinfo elements holding information
  *     about absolute axes (current value, min, max, flat, fuzz,
  *     resolution)
@@ -1244,7 +1252,6 @@ struct ff_effect {
  *     last user closes the device
  * @going_away: marks devices that are in a middle of unregistering and
  *     causes input_open_device*() fail with -ENODEV.
- * @sync: set to %true when there were no new events since last EV_SYN
  * @dev: driver model's view of this device
  * @h_list: list of input handles associated with the device. When
  *     accessing the list dev->mutex must be held
@@ -1287,10 +1294,7 @@ struct input_dev {
 
        int rep[REP_CNT];
 
-       struct input_mt_slot *mt;
-       int mtsize;
-       int slot;
-       int trkid;
+       struct input_mt *mt;
 
        struct input_absinfo *absinfo;
 
@@ -1312,12 +1316,14 @@ struct input_dev {
        unsigned int users;
        bool going_away;
 
-       bool sync;
-
        struct device dev;
 
        struct list_head        h_list;
        struct list_head        node;
+
+       unsigned int num_vals;
+       unsigned int max_vals;
+       struct input_value *vals;
 };
 #define to_input_dev(d) container_of(d, struct input_dev, dev)
 
@@ -1378,6 +1384,9 @@ struct input_handle;
  * @event: event handler. This method is being called by input core with
  *     interrupts disabled and dev->event_lock spinlock held and so
  *     it may not sleep
+ * @events: event sequence handler. This method is being called by
+ *     input core with interrupts disabled and dev->event_lock
+ *     spinlock held and so it may not sleep
  * @filter: similar to @event; separates normal event handlers from
  *     "filters".
  * @match: called after comparing device's id with handler's id_table
@@ -1414,6 +1423,8 @@ struct input_handler {
        void *private;
 
        void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+       void (*events)(struct input_handle *handle,
+                      const struct input_value *vals, unsigned int count);
        bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
        bool (*match)(struct input_handler *handler, struct input_dev *dev);
        int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
index f867375..cc5cca7 100644 (file)
 
 #define TRKID_MAX      0xffff
 
+#define INPUT_MT_POINTER       0x0001  /* pointer device, e.g. trackpad */
+#define INPUT_MT_DIRECT                0x0002  /* direct device, e.g. touchscreen */
+#define INPUT_MT_DROP_UNUSED   0x0004  /* drop contacts not seen in frame */
+#define INPUT_MT_TRACK         0x0008  /* use in-kernel tracking */
+
 /**
  * struct input_mt_slot - represents the state of an input MT slot
  * @abs: holds current values of ABS_MT axes for this slot
+ * @frame: last frame at which input_mt_report_slot_state() was called
+ * @key: optional driver designation of this slot
  */
 struct input_mt_slot {
        int abs[ABS_MT_LAST - ABS_MT_FIRST + 1];
+       unsigned int frame;
+       unsigned int key;
+};
+
+/**
+ * struct input_mt - state of tracked contacts
+ * @trkid: stores MT tracking ID for the next contact
+ * @num_slots: number of MT slots the device uses
+ * @slot: MT slot currently being transmitted
+ * @flags: input_mt operation flags
+ * @frame: increases every time input_mt_sync_frame() is called
+ * @red: reduced cost matrix for in-kernel tracking
+ * @slots: array of slots holding current values of tracked contacts
+ */
+struct input_mt {
+       int trkid;
+       int num_slots;
+       int slot;
+       unsigned int flags;
+       unsigned int frame;
+       int *red;
+       struct input_mt_slot slots[];
 };
 
 static inline void input_mt_set_value(struct input_mt_slot *slot,
@@ -35,12 +64,18 @@ static inline int input_mt_get_value(const struct input_mt_slot *slot,
        return slot->abs[code - ABS_MT_FIRST];
 }
 
-int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots);
+static inline bool input_mt_is_active(const struct input_mt_slot *slot)
+{
+       return input_mt_get_value(slot, ABS_MT_TRACKING_ID) >= 0;
+}
+
+int input_mt_init_slots(struct input_dev *dev, unsigned int num_slots,
+                       unsigned int flags);
 void input_mt_destroy_slots(struct input_dev *dev);
 
-static inline int input_mt_new_trkid(struct input_dev *dev)
+static inline int input_mt_new_trkid(struct input_mt *mt)
 {
-       return dev->trkid++ & TRKID_MAX;
+       return mt->trkid++ & TRKID_MAX;
 }
 
 static inline void input_mt_slot(struct input_dev *dev, int slot)
@@ -64,4 +99,20 @@ void input_mt_report_slot_state(struct input_dev *dev,
 void input_mt_report_finger_count(struct input_dev *dev, int count);
 void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count);
 
+void input_mt_sync_frame(struct input_dev *dev);
+
+/**
+ * struct input_mt_pos - contact position
+ * @x: horizontal coordinate
+ * @y: vertical coordinate
+ */
+struct input_mt_pos {
+       s16 x, y;
+};
+
+int input_mt_assign_slots(struct input_dev *dev, int *slots,
+                         const struct input_mt_pos *pos, int num_pos);
+
+int input_mt_get_slot_by_key(struct input_dev *dev, int key);
+
 #endif
index 7e83370..f3b99e1 100644 (file)
@@ -256,72 +256,78 @@ static inline void iommu_set_fault_handler(struct iommu_domain *domain,
 {
 }
 
-int iommu_attach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline int iommu_attach_group(struct iommu_domain *domain,
+                                    struct iommu_group *group)
 {
        return -ENODEV;
 }
 
-void iommu_detach_group(struct iommu_domain *domain, struct iommu_group *group)
+static inline void iommu_detach_group(struct iommu_domain *domain,
+                                     struct iommu_group *group)
 {
 }
 
-struct iommu_group *iommu_group_alloc(void)
+static inline struct iommu_group *iommu_group_alloc(void)
 {
        return ERR_PTR(-ENODEV);
 }
 
-void *iommu_group_get_iommudata(struct iommu_group *group)
+static inline void *iommu_group_get_iommudata(struct iommu_group *group)
 {
        return NULL;
 }
 
-void iommu_group_set_iommudata(struct iommu_group *group, void *iommu_data,
-                              void (*release)(void *iommu_data))
+static inline void iommu_group_set_iommudata(struct iommu_group *group,
+                                            void *iommu_data,
+                                            void (*release)(void *iommu_data))
 {
 }
 
-int iommu_group_set_name(struct iommu_group *group, const char *name)
+static inline int iommu_group_set_name(struct iommu_group *group,
+                                      const char *name)
 {
        return -ENODEV;
 }
 
-int iommu_group_add_device(struct iommu_group *group, struct device *dev)
+static inline int iommu_group_add_device(struct iommu_group *group,
+                                        struct device *dev)
 {
        return -ENODEV;
 }
 
-void iommu_group_remove_device(struct device *dev)
+static inline void iommu_group_remove_device(struct device *dev)
 {
 }
 
-int iommu_group_for_each_dev(struct iommu_group *group, void *data,
-                            int (*fn)(struct device *, void *))
+static inline int iommu_group_for_each_dev(struct iommu_group *group,
+                                          void *data,
+                                          int (*fn)(struct device *, void *))
 {
        return -ENODEV;
 }
 
-struct iommu_group *iommu_group_get(struct device *dev)
+static inline struct iommu_group *iommu_group_get(struct device *dev)
 {
        return NULL;
 }
 
-void iommu_group_put(struct iommu_group *group)
+static inline void iommu_group_put(struct iommu_group *group)
 {
 }
 
-int iommu_group_register_notifier(struct iommu_group *group,
-                                 struct notifier_block *nb)
+static inline int iommu_group_register_notifier(struct iommu_group *group,
+                                               struct notifier_block *nb)
 {
        return -ENODEV;
 }
 
-int iommu_group_unregister_notifier(struct iommu_group *group,
-                                   struct notifier_block *nb)
+static inline int iommu_group_unregister_notifier(struct iommu_group *group,
+                                                 struct notifier_block *nb)
 {
        return 0;
 }
 
-int iommu_group_id(struct iommu_group *group)
+static inline int iommu_group_id(struct iommu_group *group)
 {
        return -ENODEV;
 }
index 9a323d1..0ba014c 100644 (file)
 
 struct irq_affinity_notify;
 struct proc_dir_entry;
-struct timer_rand_state;
 struct module;
 /**
  * struct irq_desc - interrupt descriptor
  * @irq_data:          per irq and chip data passed down to chip functions
- * @timer_rand_state:  pointer to timer rand state struct
  * @kstat_irqs:                irq stats per cpu
  * @handle_irq:                highlevel irq-events handler
  * @preflow_handler:   handler called before the flow handler (currently used by sparc)
index 3d7ae4d..46c0f32 100644 (file)
@@ -74,6 +74,7 @@ enum max77686_regulators {
 struct max77686_regulator_data {
        int id;
        struct regulator_init_data *initdata;
+       struct device_node *of_node;
 };
 
 enum max77686_opmode {
index f4f0dfa..6823548 100644 (file)
@@ -67,7 +67,7 @@ struct max8998_regulator_data {
 /**
  * struct max8998_board - packages regulator init data
  * @regulators: array of defined regulators
- * @num_regulators: number of regultors used
+ * @num_regulators: number of regulators used
  * @irq_base: base IRQ number for max8998, required for IRQs
  * @ono: power onoff IRQ number for max8998
  * @buck_voltage_lock: Do NOT change the values of the following six
index f350fd0..9451471 100644 (file)
@@ -14,6 +14,7 @@
 #define TPS6586X_SLEW_RATE_MASK         0x07
 
 enum {
+       TPS6586X_ID_SYS,
        TPS6586X_ID_SM_0,
        TPS6586X_ID_SM_1,
        TPS6586X_ID_SM_2,
index 61f0905..de20120 100644 (file)
@@ -1,3 +1,15 @@
+/*
+ * include/linux/micrel_phy.h
+ *
+ * Micrel PHY IDs
+ *
+ * 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 _MICREL_PHY_H
 #define _MICREL_PHY_H
 
 
 #define PHY_ID_KSZ9021         0x00221610
 #define PHY_ID_KS8737          0x00221720
-#define PHY_ID_KS8041          0x00221510
-#define PHY_ID_KS8051          0x00221550
+#define PHY_ID_KSZ8021         0x00221555
+#define PHY_ID_KSZ8041         0x00221510
+#define PHY_ID_KSZ8051         0x00221550
 /* both for ks8001 Rev. A/B, and for ks8721 Rev 3. */
-#define PHY_ID_KS8001          0x0022161A
+#define PHY_ID_KSZ8001         0x0022161A
 
 /* struct phy_device dev_flags definitions */
 #define MICREL_PHY_50MHZ_CLK   0x00000001
index 9490a00..c25ccca 100644 (file)
@@ -35,8 +35,10 @@ struct nvme_bar {
        __u64                   acq;    /* Admin CQ Base Address */
 };
 
+#define NVME_CAP_MQES(cap)     ((cap) & 0xffff)
 #define NVME_CAP_TIMEOUT(cap)  (((cap) >> 24) & 0xff)
 #define NVME_CAP_STRIDE(cap)   (((cap) >> 32) & 0xf)
+#define NVME_CAP_MPSMIN(cap)   (((cap) >> 48) & 0xf)
 
 enum {
        NVME_CC_ENABLE          = 1 << 0,
index 7f7e00d..e3bcc3f 100644 (file)
@@ -285,6 +285,7 @@ struct regmap_irq {
  * @ack_base:    Base ack address.  If zero then the chip is clear on read.
  * @wake_base:   Base address for wake enables.  If zero unsupported.
  * @irq_reg_stride:  Stride to use for chips where registers are not contiguous.
+ * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.
  *
  * @num_regs:    Number of registers in each control bank.
  * @irqs:        Descriptors for individual IRQs.  Interrupt numbers are
@@ -299,6 +300,8 @@ struct regmap_irq_chip {
        unsigned int ack_base;
        unsigned int wake_base;
        unsigned int irq_reg_stride;
+       unsigned int mask_invert;
+       bool runtime_pm;
 
        int num_regs;
 
index da339fd..c43cd35 100644 (file)
@@ -177,6 +177,8 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode);
 unsigned int regulator_get_mode(struct regulator *regulator);
 int regulator_set_optimum_mode(struct regulator *regulator, int load_uA);
 
+int regulator_allow_bypass(struct regulator *regulator, bool allow);
+
 /* regulator notifier block */
 int regulator_register_notifier(struct regulator *regulator,
                              struct notifier_block *nb);
@@ -328,6 +330,12 @@ static inline int regulator_set_optimum_mode(struct regulator *regulator,
        return REGULATOR_MODE_NORMAL;
 }
 
+static inline int regulator_allow_bypass(struct regulator *regulator,
+                                        bool allow)
+{
+       return 0;
+}
+
 static inline int regulator_register_notifier(struct regulator *regulator,
                              struct notifier_block *nb)
 {
@@ -352,4 +360,11 @@ static inline void regulator_set_drvdata(struct regulator *regulator,
 
 #endif
 
+static inline int regulator_set_voltage_tol(struct regulator *regulator,
+                                           int new_uV, int tol_uV)
+{
+       return regulator_set_voltage(regulator,
+                                    new_uV - tol_uV, new_uV + tol_uV);
+}
+
 #endif
index bac4c87..7932a3b 100644 (file)
@@ -32,6 +32,8 @@ enum regulator_status {
        REGULATOR_STATUS_NORMAL,
        REGULATOR_STATUS_IDLE,
        REGULATOR_STATUS_STANDBY,
+       /* The regulator is enabled but not regulating */
+       REGULATOR_STATUS_BYPASS,
        /* in case that any other status doesn't apply */
        REGULATOR_STATUS_UNDEFINED,
 };
@@ -58,6 +60,7 @@ enum regulator_status {
  *     regulator_desc.n_voltages.  Voltages may be reported in any order.
  *
  * @set_current_limit: Configure a limit for a current-limited regulator.
+ *                     The driver should select the current closest to max_uA.
  * @get_current_limit: Get the configured limit for a current-limited regulator.
  *
  * @set_mode: Set the configured operating mode for the regulator.
@@ -67,6 +70,9 @@ enum regulator_status {
  * @get_optimum_mode: Get the most efficient operating mode for the regulator
  *                    when running with the specified parameters.
  *
+ * @set_bypass: Set the regulator in bypass mode.
+ * @get_bypass: Get the regulator bypass mode state.
+ *
  * @enable_time: Time taken for the regulator voltage output voltage to
  *               stabilise after being enabled, in microseconds.
  * @set_ramp_delay: Set the ramp delay for the regulator. The driver should
@@ -133,6 +139,10 @@ struct regulator_ops {
        unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV,
                                          int output_uV, int load_uA);
 
+       /* control and report on bypass mode */
+       int (*set_bypass)(struct regulator_dev *dev, bool enable);
+       int (*get_bypass)(struct regulator_dev *dev, bool *enable);
+
        /* the operations below are for configuration of regulator state when
         * its parent PMIC enters a global STANDBY/HIBERNATE state */
 
@@ -205,6 +215,8 @@ struct regulator_desc {
        unsigned int vsel_mask;
        unsigned int enable_reg;
        unsigned int enable_mask;
+       unsigned int bypass_reg;
+       unsigned int bypass_mask;
 
        unsigned int enable_time;
 };
@@ -221,7 +233,8 @@ struct regulator_desc {
  * @driver_data: private regulator data
  * @of_node: OpenFirmware node to parse for device tree bindings (may be
  *           NULL).
- * @regmap: regmap to use for core regmap helpers
+ * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is
+ *          insufficient.
  * @ena_gpio: GPIO controlling regulator enable.
  * @ena_gpio_invert: Sense for GPIO enable control.
  * @ena_gpio_flags: Flags to use when calling gpio_request_one()
@@ -253,6 +266,7 @@ struct regulator_dev {
        int exclusive;
        u32 use_count;
        u32 open_count;
+       u32 bypass_count;
 
        /* lists we belong to */
        struct list_head list; /* list of all regulators */
@@ -310,6 +324,8 @@ int regulator_disable_regmap(struct regulator_dev *rdev);
 int regulator_set_voltage_time_sel(struct regulator_dev *rdev,
                                   unsigned int old_selector,
                                   unsigned int new_selector);
+int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable);
+int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable);
 
 void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
 
diff --git a/include/linux/regulator/fan53555.h b/include/linux/regulator/fan53555.h
new file mode 100644 (file)
index 0000000..5c45c85
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * fan53555.h - Fairchild Regulator FAN53555 Driver
+ *
+ * Copyright (C) 2012 Marvell Technology Ltd.
+ * Yunfan Zhang <yfzhang@marvell.com>
+ *
+ * This package 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.
+ *
+ */
+
+#ifndef __FAN53555_H__
+
+/* VSEL ID */
+enum {
+       FAN53555_VSEL_ID_0 = 0,
+       FAN53555_VSEL_ID_1,
+};
+
+/* Transition slew rate limiting from a low to high voltage.
+ * -----------------------
+ *   Bin |Slew Rate(mV/uS)
+ * ------|----------------
+ *   000 |    64.00
+ * ------|----------------
+ *   001 |    32.00
+ * ------|----------------
+ *   010 |    16.00
+ * ------|----------------
+ *   011 |     8.00
+ * ------|----------------
+ *   100 |     4.00
+ * ------|----------------
+ *   101 |     2.00
+ * ------|----------------
+ *   110 |     1.00
+ * ------|----------------
+ *   111 |     0.50
+ * -----------------------
+ */
+enum {
+       FAN53555_SLEW_RATE_64MV = 0,
+       FAN53555_SLEW_RATE_32MV,
+       FAN53555_SLEW_RATE_16MV,
+       FAN53555_SLEW_RATE_8MV,
+       FAN53555_SLEW_RATE_4MV,
+       FAN53555_SLEW_RATE_2MV,
+       FAN53555_SLEW_RATE_1MV,
+       FAN53555_SLEW_RATE_0_5MV,
+};
+
+struct fan53555_platform_data {
+       struct regulator_init_data *regulator;
+       unsigned int slew_rate;
+       /* Sleep VSEL ID */
+       unsigned int sleep_vsel_id;
+};
+
+#endif /* __FAN53555_H__ */
index 40dd0a3..36adbc8 100644 (file)
@@ -32,6 +32,7 @@ struct regulator;
  *           board/machine.
  * STATUS:   Regulator can be enabled and disabled.
  * DRMS:     Dynamic Regulator Mode Switching is enabled for this regulator.
+ * BYPASS:   Regulator can be put into bypass mode
  */
 
 #define REGULATOR_CHANGE_VOLTAGE       0x1
@@ -39,6 +40,7 @@ struct regulator;
 #define REGULATOR_CHANGE_MODE          0x4
 #define REGULATOR_CHANGE_STATUS                0x8
 #define REGULATOR_CHANGE_DRMS          0x10
+#define REGULATOR_CHANGE_BYPASS                0x20
 
 /**
  * struct regulator_state - regulator state during low power system states
index 3dea6a9..d143b8e 100644 (file)
@@ -118,6 +118,7 @@ void reset_security_ops(void);
 extern unsigned long mmap_min_addr;
 extern unsigned long dac_mmap_min_addr;
 #else
+#define mmap_min_addr          0UL
 #define dac_mmap_min_addr      0UL
 #endif
 
index b0b4eb2..1905ca8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Trace files that want to automate creationg of all tracepoints defined
+ * Trace files that want to automate creation of all tracepoints defined
  * in their file should include this file. The following are macros that the
  * trace file may define:
  *
index c785554..ebf3bac 100644 (file)
@@ -62,7 +62,7 @@ void fprop_global_destroy(struct fprop_global *p)
  */
 bool fprop_new_period(struct fprop_global *p, int periods)
 {
-       u64 events;
+       s64 events;
        unsigned long flags;
 
        local_irq_save(flags);
index bcb63ac..f468185 100644 (file)
@@ -419,7 +419,7 @@ int __init reserve_bootmem_node(pg_data_t *pgdat, unsigned long physaddr,
 }
 
 /**
- * reserve_bootmem - mark a page range as usable
+ * reserve_bootmem - mark a page range as reserved
  * @addr: starting address of the range
  * @size: size of the range in bytes
  * @flags: reservation flags (see linux/bootmem.h)
index 57c4b93..141dbb6 100644 (file)
@@ -1811,7 +1811,6 @@ static void __collapse_huge_page_copy(pte_t *pte, struct page *page,
                        src_page = pte_page(pteval);
                        copy_user_highpage(page, src_page, address, vma);
                        VM_BUG_ON(page_mapcount(src_page) != 1);
-                       VM_BUG_ON(page_count(src_page) != 2);
                        release_pte_page(src_page);
                        /*
                         * ptl mostly unnecessary, but preempt has to
index c718fd3..4de77ea 100644 (file)
@@ -105,7 +105,7 @@ static const struct file_operations vlandev_fops = {
 };
 
 /*
- * Proc filesystem derectory entries.
+ * Proc filesystem directory entries.
  */
 
 /* Strings */
index e877af8..469daab 100644 (file)
@@ -642,7 +642,8 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
        struct batadv_neigh_node *router = NULL;
        struct batadv_orig_node *orig_node_tmp;
        struct hlist_node *node;
-       uint8_t bcast_own_sum_orig, bcast_own_sum_neigh;
+       int if_num;
+       uint8_t sum_orig, sum_neigh;
        uint8_t *neigh_addr;
 
        batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
@@ -727,17 +728,17 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
        if (router && (neigh_node->tq_avg == router->tq_avg)) {
                orig_node_tmp = router->orig_node;
                spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
-               bcast_own_sum_orig =
-                       orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+               if_num = router->if_incoming->if_num;
+               sum_orig = orig_node_tmp->bcast_own_sum[if_num];
                spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
 
                orig_node_tmp = neigh_node->orig_node;
                spin_lock_bh(&orig_node_tmp->ogm_cnt_lock);
-               bcast_own_sum_neigh =
-                       orig_node_tmp->bcast_own_sum[if_incoming->if_num];
+               if_num = neigh_node->if_incoming->if_num;
+               sum_neigh = orig_node_tmp->bcast_own_sum[if_num];
                spin_unlock_bh(&orig_node_tmp->ogm_cnt_lock);
 
-               if (bcast_own_sum_orig >= bcast_own_sum_neigh)
+               if (sum_orig >= sum_neigh)
                        goto update_tt;
        }
 
index 109ea2a..21c5357 100644 (file)
@@ -100,18 +100,21 @@ static int batadv_interface_set_mac_addr(struct net_device *dev, void *p)
 {
        struct batadv_priv *bat_priv = netdev_priv(dev);
        struct sockaddr *addr = p;
+       uint8_t old_addr[ETH_ALEN];
 
        if (!is_valid_ether_addr(addr->sa_data))
                return -EADDRNOTAVAIL;
 
+       memcpy(old_addr, dev->dev_addr, ETH_ALEN);
+       memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
+
        /* only modify transtable if it has been initialized before */
        if (atomic_read(&bat_priv->mesh_state) == BATADV_MESH_ACTIVE) {
-               batadv_tt_local_remove(bat_priv, dev->dev_addr,
+               batadv_tt_local_remove(bat_priv, old_addr,
                                       "mac address changed", false);
                batadv_tt_local_add(dev, addr->sa_data, BATADV_NULL_IFINDEX);
        }
 
-       memcpy(dev->dev_addr, addr->sa_data, ETH_ALEN);
        dev->addr_assign_type &= ~NET_ADDR_RANDOM;
        return 0;
 }
index d4de5db..0b997c8 100644 (file)
@@ -734,6 +734,8 @@ static int hci_dev_do_close(struct hci_dev *hdev)
 
        cancel_work_sync(&hdev->le_scan);
 
+       cancel_delayed_work(&hdev->power_off);
+
        hci_req_cancel(hdev, ENODEV);
        hci_req_lock(hdev);
 
index 4ea1710..38c00f1 100644 (file)
@@ -1008,7 +1008,7 @@ static void l2cap_send_disconn_req(struct l2cap_conn *conn, struct l2cap_chan *c
        if (!conn)
                return;
 
-       if (chan->mode == L2CAP_MODE_ERTM) {
+       if (chan->mode == L2CAP_MODE_ERTM && chan->state == BT_CONNECTED) {
                __clear_retrans_timer(chan);
                __clear_monitor_timer(chan);
                __clear_ack_timer(chan);
index ad6613d..eba022d 100644 (file)
@@ -2875,6 +2875,22 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered)
                if (scan)
                        hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
 
+               if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) {
+                       u8 ssp = 1;
+
+                       hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp);
+               }
+
+               if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) {
+                       struct hci_cp_write_le_host_supported cp;
+
+                       cp.le = 1;
+                       cp.simul = !!(hdev->features[6] & LMP_SIMUL_LE_BR);
+
+                       hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED,
+                                    sizeof(cp), &cp);
+               }
+
                update_class(hdev);
                update_name(hdev, hdev->dev_name);
                update_eir(hdev);
index 24c5eea..159aa8b 100644 (file)
@@ -1073,16 +1073,13 @@ static int write_partial_msg_pages(struct ceph_connection *con)
                        BUG_ON(kaddr == NULL);
                        base = kaddr + con->out_msg_pos.page_pos + bio_offset;
                        crc = crc32c(crc, base, len);
+                       kunmap(page);
                        msg->footer.data_crc = cpu_to_le32(crc);
                        con->out_msg_pos.did_page_crc = true;
                }
                ret = ceph_tcp_sendpage(con->sock, page,
                                      con->out_msg_pos.page_pos + bio_offset,
                                      len, 1);
-
-               if (do_datacrc)
-                       kunmap(page);
-
                if (ret <= 0)
                        goto out;
 
index 3057920..a6000fb 100644 (file)
@@ -691,7 +691,8 @@ set_rcvbuf:
 
        case SO_KEEPALIVE:
 #ifdef CONFIG_INET
-               if (sk->sk_protocol == IPPROTO_TCP)
+               if (sk->sk_protocol == IPPROTO_TCP &&
+                   sk->sk_type == SOCK_STREAM)
                        tcp_set_keepalive(sk, valbool);
 #endif
                sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
index e1e0a4e..c7527f6 100644 (file)
@@ -510,7 +510,10 @@ relookup:
                                        secure_ipv6_id(daddr->addr.a6));
                p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
                p->rate_tokens = 0;
-               p->rate_last = 0;
+               /* 60*HZ is arbitrary, but chosen enough high so that the first
+                * calculation of tokens is at its maximum.
+                */
+               p->rate_last = jiffies - 60*HZ;
                INIT_LIST_HEAD(&p->gc_list);
 
                /* Link the node. */
index ff0f071..d23c657 100644 (file)
@@ -131,18 +131,20 @@ found:
  *     0 - deliver
  *     1 - block
  */
-static __inline__ int icmp_filter(struct sock *sk, struct sk_buff *skb)
+static int icmp_filter(const struct sock *sk, const struct sk_buff *skb)
 {
-       int type;
+       struct icmphdr _hdr;
+       const struct icmphdr *hdr;
 
-       if (!pskb_may_pull(skb, sizeof(struct icmphdr)))
+       hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+                                sizeof(_hdr), &_hdr);
+       if (!hdr)
                return 1;
 
-       type = icmp_hdr(skb)->type;
-       if (type < 32) {
+       if (hdr->type < 32) {
                __u32 data = raw_sk(sk)->filter.data;
 
-               return ((1 << type) & data) != 0;
+               return ((1U << hdr->type) & data) != 0;
        }
 
        /* Do not block unknown ICMP types */
index 5b087c3..0f9bdc5 100644 (file)
@@ -86,28 +86,30 @@ static int mip6_mh_len(int type)
 
 static int mip6_mh_filter(struct sock *sk, struct sk_buff *skb)
 {
-       struct ip6_mh *mh;
+       struct ip6_mh _hdr;
+       const struct ip6_mh *mh;
 
-       if (!pskb_may_pull(skb, (skb_transport_offset(skb)) + 8) ||
-           !pskb_may_pull(skb, (skb_transport_offset(skb) +
-                                ((skb_transport_header(skb)[1] + 1) << 3))))
+       mh = skb_header_pointer(skb, skb_transport_offset(skb),
+                               sizeof(_hdr), &_hdr);
+       if (!mh)
                return -1;
 
-       mh = (struct ip6_mh *)skb_transport_header(skb);
+       if (((mh->ip6mh_hdrlen + 1) << 3) > skb->len)
+               return -1;
 
        if (mh->ip6mh_hdrlen < mip6_mh_len(mh->ip6mh_type)) {
                LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH message too short: %d vs >=%d\n",
                               mh->ip6mh_hdrlen, mip6_mh_len(mh->ip6mh_type));
-               mip6_param_prob(skb, 0, ((&mh->ip6mh_hdrlen) -
-                                        skb_network_header(skb)));
+               mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_hdrlen) +
+                               skb_network_header_len(skb));
                return -1;
        }
 
        if (mh->ip6mh_proto != IPPROTO_NONE) {
                LIMIT_NETDEBUG(KERN_DEBUG "mip6: MH invalid payload proto = %d\n",
                               mh->ip6mh_proto);
-               mip6_param_prob(skb, 0, ((&mh->ip6mh_proto) -
-                                        skb_network_header(skb)));
+               mip6_param_prob(skb, 0, offsetof(struct ip6_mh, ip6mh_proto) +
+                               skb_network_header_len(skb));
                return -1;
        }
 
index ef0579d..4a5f78b 100644 (file)
@@ -107,21 +107,20 @@ found:
  *     0 - deliver
  *     1 - block
  */
-static __inline__ int icmpv6_filter(struct sock *sk, struct sk_buff *skb)
+static int icmpv6_filter(const struct sock *sk, const struct sk_buff *skb)
 {
-       struct icmp6hdr *icmph;
-       struct raw6_sock *rp = raw6_sk(sk);
-
-       if (pskb_may_pull(skb, sizeof(struct icmp6hdr))) {
-               __u32 *data = &rp->filter.data[0];
-               int bit_nr;
+       struct icmp6hdr *_hdr;
+       const struct icmp6hdr *hdr;
 
-               icmph = (struct icmp6hdr *) skb->data;
-               bit_nr = icmph->icmp6_type;
+       hdr = skb_header_pointer(skb, skb_transport_offset(skb),
+                                sizeof(_hdr), &_hdr);
+       if (hdr) {
+               const __u32 *data = &raw6_sk(sk)->filter.data[0];
+               unsigned int type = hdr->icmp6_type;
 
-               return (data[bit_nr >> 5] & (1 << (bit_nr & 31))) != 0;
+               return (data[type >> 5] & (1U << (type & 31))) != 0;
        }
-       return 0;
+       return 1;
 }
 
 #if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)
index d71cd92..6f93635 100644 (file)
@@ -80,8 +80,8 @@ static int l2tp_nl_cmd_noop(struct sk_buff *skb, struct genl_info *info)
 
        hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq,
                          &l2tp_nl_family, 0, L2TP_CMD_NOOP);
-       if (IS_ERR(hdr)) {
-               ret = PTR_ERR(hdr);
+       if (!hdr) {
+               ret = -EMSGSIZE;
                goto err_out;
        }
 
@@ -250,8 +250,8 @@ static int l2tp_nl_tunnel_send(struct sk_buff *skb, u32 pid, u32 seq, int flags,
 
        hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags,
                          L2TP_CMD_TUNNEL_GET);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       if (!hdr)
+               return -EMSGSIZE;
 
        if (nla_put_u8(skb, L2TP_ATTR_PROTO_VERSION, tunnel->version) ||
            nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
@@ -617,8 +617,8 @@ static int l2tp_nl_session_send(struct sk_buff *skb, u32 pid, u32 seq, int flags
        sk = tunnel->sock;
 
        hdr = genlmsg_put(skb, pid, seq, &l2tp_nl_family, flags, L2TP_CMD_SESSION_GET);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       if (!hdr)
+               return -EMSGSIZE;
 
        if (nla_put_u32(skb, L2TP_ATTR_CONN_ID, tunnel->tunnel_id) ||
            nla_put_u32(skb, L2TP_ATTR_SESSION_ID, session->session_id) ||
index 5c22ce8..a4c1e45 100644 (file)
@@ -117,11 +117,11 @@ static int limit_mt_check(const struct xt_mtchk_param *par)
 
        /* For SMP, we only want to use one set of state. */
        r->master = priv;
+       /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
+          128. */
+       priv->prev = jiffies;
+       priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
        if (r->cost == 0) {
-               /* User avg in seconds * XT_LIMIT_SCALE: convert to jiffies *
-                  128. */
-               priv->prev = jiffies;
-               priv->credit = user2credits(r->avg * r->burst); /* Credits full. */
                r->credit_cap = priv->credit; /* Credits full. */
                r->cost = user2credits(r->avg);
        }
index 2ded3c7..72d170c 100644 (file)
@@ -350,6 +350,9 @@ static void reg_regdb_search(struct work_struct *work)
        struct reg_regdb_search_request *request;
        const struct ieee80211_regdomain *curdom, *regdom;
        int i, r;
+       bool set_reg = false;
+
+       mutex_lock(&cfg80211_mutex);
 
        mutex_lock(&reg_regdb_search_mutex);
        while (!list_empty(&reg_regdb_search_list)) {
@@ -365,9 +368,7 @@ static void reg_regdb_search(struct work_struct *work)
                                r = reg_copy_regd(&regdom, curdom);
                                if (r)
                                        break;
-                               mutex_lock(&cfg80211_mutex);
-                               set_regdom(regdom);
-                               mutex_unlock(&cfg80211_mutex);
+                               set_reg = true;
                                break;
                        }
                }
@@ -375,6 +376,11 @@ static void reg_regdb_search(struct work_struct *work)
                kfree(request);
        }
        mutex_unlock(&reg_regdb_search_mutex);
+
+       if (set_reg)
+               set_regdom(regdom);
+
+       mutex_unlock(&cfg80211_mutex);
 }
 
 static DECLARE_WORK(reg_regdb_work, reg_regdb_search);
index d24810f..fd8fa9a 100755 (executable)
@@ -200,7 +200,7 @@ EOF
 syscall_list() {
     grep '^[0-9]' "$1" | sort -n | (
        while read nr abi name entry ; do
-           echo <<EOF
+           cat <<EOF
 #if !defined(__NR_${name}) && !defined(__IGNORE_${name})
 #warning syscall ${name} not implemented
 #endif
index 2efac28..2b131a8 100644 (file)
@@ -51,10 +51,10 @@ statement S1,S2;
 p << r.p;
 @@
 
-coccilib.org.print_todo(p[0], "WARNING opportunity for memdep_user")
+coccilib.org.print_todo(p[0], "WARNING opportunity for memdup_user")
 
 @script:python depends on report@
 p << r.p;
 @@
 
-coccilib.report.print_report(p[0], "WARNING opportunity for memdep_user")
+coccilib.report.print_report(p[0], "WARNING opportunity for memdup_user")
index 2fbbbc1..3368939 100644 (file)
@@ -100,7 +100,7 @@ my @searchconfigs = (
        },
 );
 
-sub find_config {
+sub read_config {
     foreach my $conf (@searchconfigs) {
        my $file = $conf->{"file"};
 
@@ -115,17 +115,15 @@ sub find_config {
 
        print STDERR "using config: '$file'\n";
 
-       open(CIN, "$exec $file |") || die "Failed to run $exec $file";
-       return;
+       open(my $infile, '-|', "$exec $file") || die "Failed to run $exec $file";
+       my @x = <$infile>;
+       close $infile;
+       return @x;
     }
     die "No config file found";
 }
 
-find_config;
-
-# Read in the entire config file into config_file
-my @config_file = <CIN>;
-close CIN;
+my @config_file = read_config;
 
 # Parse options
 my $localmodconfig = 0;
@@ -135,7 +133,7 @@ GetOptions("localmodconfig" => \$localmodconfig,
           "localyesconfig" => \$localyesconfig);
 
 # Get the build source and top level Kconfig file (passed in)
-my $ksource = $ARGV[0];
+my $ksource = ($ARGV[0] ? $ARGV[0] : '.');
 my $kconfig = $ARGV[1];
 my $lsmod_file = $ENV{'LSMOD'};
 
@@ -173,8 +171,8 @@ sub read_kconfig {
        $source =~ s/\$$env/$ENV{$env}/;
     }
 
-    open(KIN, "$source") || die "Can't open $kconfig";
-    while (<KIN>) {
+    open(my $kinfile, '<', $source) || die "Can't open $kconfig";
+    while (<$kinfile>) {
        chomp;
 
        # Make sure that lines ending with \ continue
@@ -251,10 +249,10 @@ sub read_kconfig {
            $state = "NONE";
        }
     }
-    close(KIN);
+    close($kinfile);
 
     # read in any configs that were found.
-    foreach $kconfig (@kconfigs) {
+    foreach my $kconfig (@kconfigs) {
        if (!defined($read_kconfigs{$kconfig})) {
            $read_kconfigs{$kconfig} = 1;
            read_kconfig($kconfig);
@@ -295,8 +293,8 @@ foreach my $makefile (@makefiles) {
     my $line = "";
     my %make_vars;
 
-    open(MIN,$makefile) || die "Can't open $makefile";
-    while (<MIN>) {
+    open(my $infile, '<', $makefile) || die "Can't open $makefile";
+    while (<$infile>) {
        # if this line ends with a backslash, continue
        chomp;
        if (/^(.*)\\$/) {
@@ -343,10 +341,11 @@ foreach my $makefile (@makefiles) {
            }
        }
     }
-    close(MIN);
+    close($infile);
 }
 
 my %modules;
+my $linfile;
 
 if (defined($lsmod_file)) {
     if ( ! -f $lsmod_file) {
@@ -356,13 +355,10 @@ if (defined($lsmod_file)) {
                die "$lsmod_file not found";
        }
     }
-    if ( -x $lsmod_file) {
-       # the file is executable, run it
-       open(LIN, "$lsmod_file|");
-    } else {
-       # Just read the contents
-       open(LIN, "$lsmod_file");
-    }
+
+    my $otype = ( -x $lsmod_file) ? '-|' : '<';
+    open($linfile, $otype, $lsmod_file);
+
 } else {
 
     # see what modules are loaded on this system
@@ -379,16 +375,16 @@ if (defined($lsmod_file)) {
        $lsmod = "lsmod";
     }
 
-    open(LIN,"$lsmod|") || die "Can not call lsmod with $lsmod";
+    open($linfile, '-|', $lsmod) || die "Can not call lsmod with $lsmod";
 }
 
-while (<LIN>) {
+while (<$linfile>) {
        next if (/^Module/);  # Skip the first line.
        if (/^(\S+)/) {
                $modules{$1} = 1;
        }
 }
-close (LIN);
+close ($linfile);
 
 # add to the configs hash all configs that are needed to enable
 # a loaded module. This is a direct obj-${CONFIG_FOO} += bar.o
@@ -605,6 +601,8 @@ foreach my $line (@config_file) {
        if (defined($configs{$1})) {
            if ($localyesconfig) {
                $setconfigs{$1} = 'y';
+               print "$1=y\n";
+               next;
            } else {
                $setconfigs{$1} = $2;
            }
index 4d995ae..9cdec70 100644 (file)
@@ -1,6 +1,5 @@
 #
 # Generated include files
 #
-af_names.h
 capability_names.h
 rlim_names.h
index 7efb12b..12a3920 100644 (file)
@@ -1,4 +1,3 @@
 #Ignore generated files
-maui_boot.h
 pss_boot.h
 trix_boot.h
index 3fd5b29..a3acb7a 100644 (file)
@@ -702,7 +702,7 @@ static bool wm2000_readable_reg(struct device *dev, unsigned int reg)
 }
 
 static const struct regmap_config wm2000_regmap = {
-       .reg_bits = 8,
+       .reg_bits = 16,
        .val_bits = 8,
 
        .max_register = WM2000_REG_IF_CTL,
index d6e2bb4..060dccb 100644 (file)
@@ -197,7 +197,13 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                        /* no data provider, so send silence */
                        unsigned int offs = 0;
                        for (i = 0; i < ctx->packets; ++i) {
-                               int counts = ctx->packet_size[i];
+                               int counts;
+
+                               if (ctx->packet_size[i])
+                                       counts = ctx->packet_size[i];
+                               else
+                                       counts = snd_usb_endpoint_next_packet_size(ep);
+
                                urb->iso_frame_desc[i].offset = offs * ep->stride;
                                urb->iso_frame_desc[i].length = counts * ep->stride;
                                offs += counts;
index 3bdb407..eb34057 100644 (file)
@@ -58,7 +58,7 @@ struct callchain_list {
 /*
  * A callchain cursor is a single linked list that
  * let one feed a callchain progressively.
- * It keeps persitent allocated entries to minimize
+ * It keeps persistent allocated entries to minimize
  * allocations.
  */
 struct callchain_cursor_node {
index 127d648..607dd29 100644 (file)
@@ -752,7 +752,7 @@ static int test_pmu(void)
 
        ret = stat(path, &st);
        if (ret)
-               pr_debug("ommiting PMU cpu tests\n");
+               pr_debug("omitting PMU cpu tests\n");
        return !ret;
 }
 
index 2133628..c40c2d3 100644 (file)
@@ -1,5 +1,5 @@
 #
-# List of files needed by perf python extention
+# List of files needed by perf python extension
 #
 # Each source file must be placed on its own line so that it can be
 # processed by Makefile and util/setup.py accordingly.
index 323a552..63a1a83 100644 (file)
@@ -33,7 +33,7 @@ DEFAULTS
 THIS_DIR := ${PWD}
 
 
-# to orginize your configs, having each machine save their configs
+# to organize your configs, having each machine save their configs
 # into a separate directly is useful.
 CONFIG_DIR := ${THIS_DIR}/configs/${MACHINE}
 
index 4fdb811..60cedb1 100644 (file)
@@ -47,7 +47,7 @@ BUILD_NOCLEAN = 1
 # Build, install, boot and test with a randconfg 10 times.
 # It is important that you have set MIN_CONFIG in the config
 # that includes this file otherwise it is likely that the
-# randconfig will not have the neccessary configs needed to
+# randconfig will not have the necessary configs needed to
 # boot your box. This version of the test requires a min
 # config that has enough to make sure the target has network
 # working.
index 52b7959..c05bcd2 100755 (executable)
@@ -840,7 +840,9 @@ sub __read_config {
 
                if ($rest =~ /\sIF\s+(.*)/) {
                    # May be a ELSE IF section.
-                   if (!process_if($name, $1)) {
+                   if (process_if($name, $1)) {
+                       $if_set = 1;
+                   } else {
                        $skip = 1;
                    }
                    $rest = "";
index 8b40bd5..4c53cae 100644 (file)
@@ -36,7 +36,7 @@ mkdir $mnt
 mount -t hugetlbfs none $mnt
 
 echo "--------------------"
-echo "runing hugepage-mmap"
+echo "running hugepage-mmap"
 echo "--------------------"
 ./hugepage-mmap
 if [ $? -ne 0 ]; then
@@ -50,7 +50,7 @@ shmall=`cat /proc/sys/kernel/shmall`
 echo 268435456 > /proc/sys/kernel/shmmax
 echo 4194304 > /proc/sys/kernel/shmall
 echo "--------------------"
-echo "runing hugepage-shm"
+echo "running hugepage-shm"
 echo "--------------------"
 ./hugepage-shm
 if [ $? -ne 0 ]; then
@@ -62,7 +62,7 @@ echo $shmmax > /proc/sys/kernel/shmmax
 echo $shmall > /proc/sys/kernel/shmall
 
 echo "--------------------"
-echo "runing map_hugetlb"
+echo "running map_hugetlb"
 echo "--------------------"
 ./map_hugetlb
 if [ $? -ne 0 ]; then